2019-01-10 11:56:48 qq_43667944 阅读数 383
               

本文只是讲述一下过程,采用很简单的打包加密方法,至于需要什么样的加密结果,请大家按照需求去修改,字节偏移、前后颠倒加算法都可以,不过一般无需这么复杂,而且太复杂的加密对于极其追求运行效率的游戏来说,也是一重负担。


对于Unity,虽然Unity自身会进行压缩加密,但是其解密算法在网上随处可见,如果自己觉得游戏里面的资料具有保密性质,请对其进行自行加密。


打包加密的原理:

1、大家都知道文件都是由字节组成的。

2、一张图片之所以看起来很漂亮,是因为其数据按照一定顺序排列。

漂亮的剑灵妹子


我们可以用一个文本编辑器将其打开。

是乱码,不然你还想看到什么呢?


3、如果我们把图片数据打乱,或者在前面加一些很乱的数据,会怎么样呢?


嗯,图片不显示了。

这很容易理解,就像你看片的时候被打了马赛克嘛。。

仔细想想,为什么打马赛克?

不就是为了保密嘛。。。


好的,上面我们就起到了保密-加密功能。


4、我又找来一张图片……萌萌哒的。。



同样用一个文本编辑器打开这个图片。

复制所有的内容到第一个图片文件后面。

结果会怎么样?

两个图片会拼在一起吗?



真不幸……

不过这是符合我的主题的。加密嘛,就是要让人看不出来。


原理就讲到这里,下面就是代码了。

代码不是针对单个文件,而是对多个文件夹进行打包加密。

加密方法仅仅是多个文件打包。


-----------------------------------------------------------------------------------

我是可爱的分割线

-----------------------------------------------------------------------------------


用到的知识点:

1、读写文件

2、对文件夹、文件的操作,获取所有的文件


-----------------------------------------------------------------------------------

我是可爱的分割线

-----------------------------------------------------------------------------------

下面是主要的代码:


using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace MyPackRes{    class Helper    {        public static void Log(string str)        {            Console.Write(str+"\n\n");        }        public static void Pause()        {            Console.Write("Press any key to continue . . . ");            Console.ReadKey(true);        }    }}

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.IO;using System.Diagnostics;namespace MyPackRes{    class Program    {        private static int m_id=0;        private static int m_totalSize = 0;//         private static List<int> m_idList = new List<int>();//         private static List<int> m_startPosList = new List<int>();//         private static List<int> m_sizeList = new List<int>();//         private static List<string> m_pathList = new List<string>();        private static Dictionary<int,OneFileInfor> m_allFileInfoDic = new Dictionary<int,OneFileInfor>();        private static string m_currentDir = "";        static void Main(string[] args)        {            m_currentDir = Environment.CurrentDirectory;            Helper.Log("MyPackRes Start " + m_currentDir);            List<string> folderToPackList = new List<string>();            /**  读取配置文件  **/            Helper.Log("需要打包的文件夹:");            StreamReader streamReader = new StreamReader("MyPackRes.ini", Encoding.Default);            string line;            while ((line=streamReader.ReadLine())!=null)            {                Helper.Log(line);                if (!folderToPackList.Contains(line))                {                    folderToPackList.Add(line);                }            }            streamReader.Close();            /**  遍历打包文件夹 **/            for (int index = 0; index < folderToPackList.Count;index++ )            {                PackFolder(folderToPackList[index]);            }            Helper.Log("打包完毕!");            Helper.Pause();        }        /** 遍历文件夹获取所有文件信息 **/        private static void TraverseFolder(string foldername)        {            Helper.Log("遍历文件夹 " + foldername);            /** 读取文件夹下面所有文件的信息 **/            DirectoryInfo dirInfo = new DirectoryInfo(foldername);            foreach (FileInfo fileinfo in dirInfo.GetFiles("*.*",SearchOption.AllDirectories))            {                string filename = (fileinfo.FullName.Replace(m_currentDir+"\\","")).Replace("\\","/");                int filesize = (int)fileinfo.Length;                Helper.Log(m_id + " : " + filename + " 文件大小: " + filesize);                OneFileInfor info = new OneFileInfor();                info.m_id = m_id;                info.m_Size = filesize;                info.m_Path = filename;                                /**  读取这个文件  **/                FileStream fileStreamRead = new FileStream(fileinfo.FullName, FileMode.Open, FileAccess.Read);                if (fileStreamRead == null)                {                    Helper.Log("读取文件失败 : "+fileinfo.FullName);                    Helper.Pause();                    return;                }                else                {                    byte[] filedata = new byte[filesize];                    fileStreamRead.Read(filedata, 0, filesize);                    info.m_data = filedata;                }                fileStreamRead.Close();                m_allFileInfoDic.Add(m_id,info);                m_id++;                m_totalSize += filesize;            }        }        /**  打包一个文件夹  **/        private static void PackFolder(string foldername)        {            TraverseFolder(foldername);            Helper.Log("文件数量 : " + m_id);            Helper.Log("文件总大小 : " + m_totalSize);            /**  更新文件在UPK中的起始点  **/            int firstfilestartpos = 4 + (4 + 4 + 4 + 256) * m_allFileInfoDic.Count;            int startpos = 0;            for (int index = 0; index < m_allFileInfoDic.Count; index++)            {                if (index == 0)                {                    startpos = firstfilestartpos;                }                else                {                    startpos = m_allFileInfoDic[index - 1].m_StartPos + m_allFileInfoDic[index - 1].m_Size;//上一个文件的开始+文件大小;                }                m_allFileInfoDic[index].m_StartPos = startpos;            }            /**  写文件  **/            FileStream fileStream = new FileStream(foldername + ".UPK", FileMode.Create);            /**  文件总数量  **/            byte[] totaliddata=System.BitConverter.GetBytes(m_id);            fileStream.Write(totaliddata, 0, totaliddata.Length);            for (int index = 0; index < m_allFileInfoDic.Count;index++ )            {                /** 写入ID **/                byte[] iddata = System.BitConverter.GetBytes(m_allFileInfoDic[index].m_id);                fileStream.Write(iddata, 0, iddata.Length);                /**  写入StartPos  **/                byte[] startposdata = System.BitConverter.GetBytes(m_allFileInfoDic[index].m_StartPos);                fileStream.Write(startposdata, 0, startposdata.Length);                /**  写入size  **/                byte[] sizedata = System.BitConverter.GetBytes(m_allFileInfoDic[index].m_Size);                fileStream.Write(sizedata, 0, sizedata.Length);                /**  写入path  **/                byte[] pathdata = new byte[256];                byte[] mypathdata = new UTF8Encoding().GetBytes(m_allFileInfoDic[index].m_Path);                for (int i = 0; i < mypathdata.Length; i++)                {                    pathdata[i] = mypathdata[i];                }                pathdata[mypathdata.Length] = 0;                fileStream.Write(pathdata, 0, pathdata.Length);                            }            /**  写入文件数据  **/            for (int index = 0; index < m_allFileInfoDic.Count; index++)            {                fileStream.Write(m_allFileInfoDic[index].m_data, 0, m_allFileInfoDic[index].m_Size);            }            fileStream.Flush();            fileStream.Close();            /** 重置数据 **/            m_id = 0;            m_totalSize = 0;            m_allFileInfoDic.Clear();        }    }    public class OneFileInfor    {        public int m_id=0;        public int m_StartPos=0;        public int m_Size=0;        public string m_Path="";        public byte[] m_data = null;    };}

-----------------------------------------------------------------------------------

我是可爱的分割线

-----------------------------------------------------------------------------------

代码浅显易懂 ( # ▽ # )


首先新建一个配置文件

MyPackRes.ini


里面添加需要打包的文件夹 每行一个:


看看我们运行结果。

首先看看我准备的图。从碧之轨迹中TP抽取的哦。


打包结束后,会生成UPK文件。


UPK,这个后缀是虚幻引擎的默认资源文件格式哦。


最后附上工程:

http://download.csdn.net/detail/cp790621656/8393457


           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

2019-03-28 20:03:24 qq_39824797 阅读数 379

记录最近在学习Lua热更新的过程中遇到的一些坑

笔者这里使用的是Slua,关于Lua文件的打包下载和读取,不论是什么Lua框架,应该都是通用的

Unity3D加载.lua/.txt文件

在Slua官方给出的Demo中,加载的lua文件是.txt后缀,而大多数编辑器只能打开.lua文件,所以需要让Unity3D能够加载.lua文件是必要的一步,否则开发过程中就需要频繁的修改后缀名,非常麻烦,Slua中其实也提供了接口(如果直接用记事本写代码这一步可以省略。。。):

LuaSvr svr;
LuaTable self;
LuaFunction update;
[CustomLuaClass]
public delegate void UpdateDelegate(object self);

//调用这个方法,读取Entry.lua文件并执行
private void StartLua()
    {
        string dataPath="lua文件所在的根文件夹"
        svr = new LuaSvr();
        svr.init(null, () =>
        {
            string initLua = @"return require(""Entry"")";
            IntPtr l = LuaSvr.mainState.L;
            LuaDLL.lua_getglobal(l, "package");
            LuaDLL.lua_pushstring(l,dataPath+ "?.lua");//这里也可以改成.txt

            LuaDLL.lua_setfield(l, -2, "path");
            LuaDLL.lua_pop(l, 1);
            self = LuaSvr.mainState.doString(initLua) as LuaTable;

            update = (LuaFunction)self["update"];//绑定Entry.lua中的update方法
            UD = update.cast<UpdateDelegate>();

        });
    }

在Unity3D所加载的Entry.lua文件中,一定要返回一个class,否则绑定Update方法会错,这是Entry.lua文件:

import "UnityEngine"
if not UnityEngine.GameObject or not  UnityEngine.UI then
	error("Click Make/All to generate lua wrap file")
end

print("Unity3D你好,我是Lua")
local class = { }
function class:update() -- gc alloc is zero
    print("这里是Lua脚本的Update方法")
end
return class

如果需要在Lua脚本中调用C#中的类,只需要在类的上面加上一个 [CustomLuaClass] 特性,然后点击Slua/All/Make会自动导出

Lua文件打包成AssetBundle

这里有个坑,Unity3D在打AB包的时候是无法识别.lua文件的,所以打包的时候需要将.lua文件拷贝一份,改成.txt或者其他Unity3D支持的后缀名,再进行打包,打包完毕后再删除,笔者是改成.txt文件,写了一个自用的打包工具,注释不多但是应该是可以看懂,直接用就好了,在菜单栏Tool/Resouce Manager下打开,注意:要选中Lua脚本的代码文件夹,且不要选中场景中的物体,否则会报错!!!

以下是工具代码,要放记得在Unity3D的Editor文件夹下:

using System.IO;
using UnityEditor;
using UnityEngine;

public class Tools  {

    [MenuItem("Tools/Resources Manager #q", false, 1)]
    static void ResourcesManager()
    {
        ScriptableWizard.DisplayWizard<ResourcesWindow>("资源打包工具","确定");
    }
}

public enum BuildPath
{
    Android,
    Windows,
    //IOS
}

public class ResourcesWindow : ScriptableWizard
{
    //检测create按钮的点击
    public BuildPath type = BuildPath.Android;
    public string LuaPath = "D:/Unity3D/Slua/Assets/Slua/Resources/LuaCode";//lua脚本所在文件夹
    public string BuildRootPath= "C:/Users/Administrator.SC-201902281819/Desktop/坦克/AssetBoundles";//AB存放路径
    public bool Code = true;
    public bool Resouce = false;

    void OnWizardCreate()
    {
        string dir = BuildRootPath;
        if (type == BuildPath.Android)
        {
            dir += "/Android";
        }
        else
        {
            dir += "/Windows";
        }
        if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

        DirectoryInfo TheFolder = new DirectoryInfo(LuaPath);
        string bundlePath = dir + "/LuaCode.unity3d";//Lua的AB包名称
        ChangeLua2Txt(TheFolder, LuaPath);//拷贝并修改后缀为.txt
        AssetDatabase.Refresh();//刷新一下

        if (type == BuildPath.Android)
        {
            if(Resouce)
                BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.Android);
            if(Code)
            {
                Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
                //打包
                BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, bundlePath,
                    BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.Android);
            }

        }
        else
        {
            if (Resouce)//打包资源
                BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
            if (Code)//打包lua脚本
            {
                Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
                //打包
                BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, bundlePath,
                    BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.StandaloneWindows64);
            }
        }

        DeleteTxt(TheFolder);//删除.txt文件
        AssetDatabase.Refresh();
    }

    static private void ChangeLua2Txt(DirectoryInfo TheFolder, string path)
    {
        foreach (var NextFile in TheFolder.GetFiles("*.lua"))
        {
            var utf8WithoutBom = new System.Text.UTF8Encoding(false);
            StreamReader sr = new StreamReader(NextFile.FullName, utf8WithoutBom);
            string text = sr.ReadToEnd();
            FileStream fs = new FileStream(path + "/" + Path.ChangeExtension(NextFile.Name, "txt"), FileMode.Create);
            StreamWriter sw = new StreamWriter(fs, utf8WithoutBom);
            sw.Write(text);

            sr.Close();

            sw.Flush();
            sw.Close();
            fs.Close();
        }
        foreach (DirectoryInfo NextFolder in TheFolder.GetDirectories())
        {
            ChangeLua2Txt(NextFolder, path);
        }
    }

    static private void DeleteTxt(DirectoryInfo TheFolder)
    {
        foreach (var NextFile in TheFolder.GetFiles("*.txt"))
        {
            NextFile.Delete();
        }
        foreach (DirectoryInfo NextFolder in TheFolder.GetDirectories())
        {
            DeleteTxt(NextFolder);
        }
    }

    //当前字段值修改的时候会被调用,对话框弹出时也会调用一次
    void OnEnable()
    {
        Debug.Log("本地IP地址:" + Network.player.ipAddress);
    }
}

这样就可以把lua代码打包成AB文件了

从AB包中读取Lua文件并运行

设置LuaSvr.mainState.loaderDelegate,返回AB包中的lua文件就可以了,每次在require lua文件都会调用这个委托,如果打包的时候是.txt文件,就要改成.txt后缀

// 加载lua文件Delagate
    private byte[] LuaFileLoader(string strFile, ref string absoluteFn)
    {       
        if (strFile == null)
        {
            return null;
        }

        string filename = "assets/resources/hotfix/lua/" + strFile.Replace('.', '/') + ".bytes";              
        ab = AssetBundle.LoadFromFile(Application.persistentDataPath + "/lua.unity3d");
        TextAsset textAsset = ab.LoadAsset<TextAsset>(filename);
        return textAsset.bytes;
    }

因为笔者之前打包进AB包的lua文件本身就是.txt格式,有一个比较笨的办法(不建议使用。。。)——将AB包中的lua文件写入txt中保存到StreamingAssets目录下,然后再从StreamingAssets下去加载运行lua代码,到这里lua的热更新就实现了

using SLua;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;

[CustomLuaClass]
public class Entry : MonoBehaviour {

    LuaSvr svr;
    LuaTable self;
    LuaFunction update;

    [CustomLuaClass]
    public delegate void UpdateDelegate(object self);

    UpdateDelegate UD;
    AssetBundle AB;
    string dataPath
    {
        get { return Application.streamingAssetsPath + "/"; }
    }

    void Start()
    {
        StartCoroutine(LoadAssetsBoundle());
    }

    void Update()
    {
        if (UD != null) UD(self);
    }

    IEnumerator LoadAssetsBoundle()
    {
        //这里可以先下载版本文件,对比服务器版本,如果版本有更新再下载资源
        string path= @"http://127.0.0.1/C:/Users/Administrator.SC-201902281819/Desktop/坦克/AssetBoundles/Windows/LuaCode.unity3d";
        UnityWebRequest webRequest = UnityWebRequest.GetAssetBundle(path);
        yield return webRequest.SendWebRequest();
        if (webRequest.error != null) Debug.Log(webRequest.error);
        AssetBundle AB = DownloadHandlerAssetBundle.GetContent(webRequest);

        object[] luaList = AB.LoadAllAssets();
        foreach (var item in luaList)
        {
            var lua = item as TextAsset;
            var savePath = dataPath + lua.name + ".txt";
            FileStream FS = new FileStream(savePath, FileMode.Create, FileAccess.Write);//创建写入文件 
            StreamWriter SW = new StreamWriter(FS);
            SW.Write(lua.text);
            SW.Close();
            FS.Close();
        }
        StartLua();
    }

    private void StartLua()
    {
        //将AB中的lua文件保存到本地,读取 
        svr = new LuaSvr();
        svr.init(null, () =>
        {
            string initLua = @"return require(""Entry"")";
            IntPtr l = LuaSvr.mainState.L;
            LuaDLL.lua_getglobal(l, "package");
            LuaDLL.lua_pushstring(l,dataPath+ "?.txt");

            LuaDLL.lua_setfield(l, -2, "path");
            LuaDLL.lua_pop(l, 1);
            self = LuaSvr.mainState.doString(initLua) as LuaTable;

            update = (LuaFunction)self["update"];
            UD = update.cast<UpdateDelegate>();

        });
    }

    // lua调用 C# 脚本函数
    public void Log(string str)
    {
        Debug.Log("Lua调用C#Log方法:" + str);
    }
}

 

2018-11-01 13:04:01 qq_39097425 阅读数 232

1. Unity 程序窗口初始化脚本

using System;
using System.Collections;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;

public class RemoveTheGameBorder : MonoBehaviour
{

    public Text text;
    public Text text2;
    [DllImport("user32.dll")]
    static extern IntPtr SetWindowLong(IntPtr hwnd, int _nIndex, int dwNewLong);
    [DllImport("user32.dll")]
    static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
    [DllImport("user32.dll")]
    static extern IntPtr GetForegroundWindow();

    const uint SWP_SHOWWINDOW = 0x0040;
    const int GWL_STYLE = -16;  //边框用的
    const int WS_BORDER = 1;
    const int WS_POPUP = 0x800000;

    // 窗口显示的位置
    int _posX = 0;
    int _posY = 0;

    // 在这里设置你想要的窗口宽
    int _Txtwith = 200;
    // 在这里设置你想要的窗口高
    int _Txtheight = 200;

    // 是否全屏显示
    bool fullScreenBol = true;
    private void Awake()
    {
        // 是否全屏显示
        SetFullScreen();
      
        // 根据配置表读的数据计算出窗口位置	
        SetWindowPos();
      
        // 设置无边框和位置
        StartCoroutine("Setposition");
    }

    /// <summary>
    ///  全屏显示
    /// </summary>
    private void SetFullScreen()
    {
        //这个是Unity里的设置屏幕大小,
        _Txtwith = int.Parse(TxtHandle.TxtReadStr("_Txtwith"));
        _Txtheight = int.Parse(TxtHandle.TxtReadStr("_Txtheight"));
        // 读取 txt 文本配置表
        if (TxtHandle.TxtReadStr("fullScreenBol").Trim().Equals("true", StringComparison.CurrentCultureIgnoreCase))
        {
            fullScreenBol = true;
        }
        else
        {
            fullScreenBol = false;
        }
        Screen.SetResolution(_Txtwith, _Txtheight, fullScreenBol);
    }

    /// <summary>
    /// 根据配置表读的数据计算出窗口位置 屏幕中心为 0,0 左上角为(-1,1),以此类推
    /// </summary>
    private void SetWindowPos()
    {
        if (TxtHandle.TxtReadStr("_posXID") == null || TxtHandle.TxtReadStr("_posYID") == null)
        {
            Debug.Log("Txt 文本未读取成功");
            return;
        }
        int _posXID = int.Parse(TxtHandle.TxtReadStr("_posXID"));
        int _posYID  = int.Parse(TxtHandle.TxtReadStr("_posYID"));
        
        // 用户不需要去计算屏幕的宽高 (0,-1)(0,0)(0,-1)(1,0)(0,0)...
        switch (_posXID)
        {
            case -1:
                _posX = 0;
                break;
            case 0:
                _posX = Screen.currentResolution.width / 2 - Screen.width/2;
                break;
            case 1:
                _posX = Screen.currentResolution.width - Screen.width;
                break;
            default:
                break;
        }
        switch (_posYID)
        {
            case -1:
                _posY = Screen.currentResolution.height - Screen.height;
                break;
            case 0:
                _posY = Screen.currentResolution.height / 2 - Screen.height/2;
                break;
            case 1:
                _posY = 0;
                break;
            default:
                break;
        }

    }

    /// <summary>
    /// 设置窗口位置和无边框等
    /// </summary>
    /// <returns></returns>
    IEnumerator Setposition()
    {
        text.text = _posX.ToString();
        text2.text = _posY.ToString();
        yield return new WaitForSeconds(0.1f);		//不知道为什么发布于行后,设置位置的不会生效,我延迟0.1秒就可以
        SetWindowLong(GetForegroundWindow(), GWL_STYLE, WS_POPUP);      //无边框
        bool result = SetWindowPos(GetForegroundWindow(), -1, _posX, _posY, _Txtwith, _Txtheight, SWP_SHOWWINDOW);       //设置屏幕大小和位置
    }
}

2.Txt 读取类

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.SceneManagement;

public class TxtHandle : MonoBehaviour
{
    public static string[] configAry;
    static  List<string> list = new List<string>();
    string txtName = "configuration.txt";
    // Use this for initialization
    void Awake()
    {
        //StartCoroutine(WWWLoadTxtFile("/" + txtName));
        IOLoadTxtFile(txtName);

    }

    /// <summary>
    /// 文件流读取Txt文件
    /// </summary>
    /// <param name="_filePath"></param>
    void IOLoadTxtFile(string _filePath)
    {
        //本地路径
        var fileAddress = System.IO.Path.Combine(Application.streamingAssetsPath, _filePath);
        FileInfo fInfo0 = new FileInfo(fileAddress);
        string s = "";
        if (fInfo0.Exists)
        {
            StreamReader r = new StreamReader(fileAddress);
            s = r.ReadToEnd();
            configAry = s.Trim().Split(',');
            foreach (var item in configAry)
            {
                list.Add(item);
            }
        }
    }

    /// <summary>
    ///  www类 加载txt文件
    /// </summary>
    /// <param name="_filePath"></param>
    /// <returns></returns>
    public IEnumerator WWWLoadTxtFile(string _filePath)
    {
      
        print(Application.streamingAssetsPath);
        WWW www = new WWW(Application.streamingAssetsPath + _filePath);
        yield return www;
        if (www.error == null)
        {
            configAry = www.text.ToString().Trim().Split(',');
            foreach (var item in configAry)
            {
                print(item);
            }
            SceneManager.LoadScene(1);
        }
        else
        {
            Debug.Log(www.error);
        }
    }

    /// <summary>
    /// 根据传过来的 string 返回 该字符串在txt文件中的下一个字符串
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static string TxtReadStr(string str)
    {
        string tempStr = null;
        if (list.Contains(str))
        {
            for (int i = 0; i < TxtHandle.configAry.Length; i++)
            {
                if (TxtHandle.configAry[i].Trim().Equals(str, StringComparison.CurrentCultureIgnoreCase))
                {
                    tempStr = TxtHandle.configAry[i + 1];
                    break;
                }
            }
        }
        else
        {
            Debug.Log("list not contain" + str);
        }
        return tempStr;
    }

}

3. StreamingAssets 文件夹下 txt 文本内容如下:

4.效果图如下:

2016-11-29 09:47:07 nicepainkiller 阅读数 1776

前几天 要搞软件著作权;需要源代码, 一共60页,每页50行。怎么搞 复制粘贴 60页?

想想作为程序员的我们,不应该干这么 土鳖的事情吧! 后来写了个脚本 导出所有源代码 到文件文件中,

使用之前需要在 桌面创建一个 data.txt 文件

上源码:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;

public class ExportCode : MonoBehaviour {

    public List<string> filePath;
    public List<DirectoryInfo> AllDirector = new List<DirectoryInfo>();

 
    void Start ()
    {
        GetAllDirect(  new DirectoryInfo( Application.dataPath ) );

        GetCSFiles();
        WriteAllDate();
    }


    void WriteAllDate()
    {
        FileStream   aFile = new FileStream(@"C:\Users\join\Desktop\date.txt", FileMode.Create);
        StreamWriter sw = new StreamWriter(aFile);
        string content;
        foreach (string codePath in filePath)
        {
            content =  System.IO.File.ReadAllText(codePath);
            sw.WriteLine(content);
        }
        sw.Close();
    }


    void GetCSFiles()
    {
        foreach (DirectoryInfo info in AllDirector)
        {
            filePath.AddRange(Directory.GetFiles(info.FullName, "*.cs").ToList<string>());
        }
    }


    void GetAllDirect( DirectoryInfo dir)
    {
        DirectoryInfo[] allDirectoryInfo = dir.GetDirectories();

        foreach (DirectoryInfo info in allDirectoryInfo)
        {
            AllDirector.Add(info);
            GetAllDirect(info);
        }

    }

}


2017-05-02 18:04:12 yongh701 阅读数 2248

Unity3D可以利用C#的文件流读入文件,但不建议这样做,因为自身就提供了一个TextAsset用于读入数据,尤其是可以将一个关键的csv和txt文件放到Resource文件中,让发布的时候能让其自动打包,不会让用户轻易修改看上去没这么业余。同时比起《【C#】txt的读写》(点击打开链接)的读入方法,利用TextAsset读入数据,比文件流更加简单。下面举一个例子来说明这个问题。

有一个这样的map.csv地图文件:

1,1,1,1,1,0,1
1,0,0,0,1,0,1
1,0,2,0,2,0,1
1,0,1,0,1,0,1
1,0,1,0,0,0,1
1,0,2,0,2,0,1
1,0,1,1,1,1,1
转成UTF-8编码的,避免乱码,天地可鉴。


要按照上面的地图配置csv(可以用Excel编辑;保存成txt都没问题),生成如下的7x7地图,其中1代表墙高1个单位,2代表墙高2个单位,0代表没有墙。用逗号分割出一个个单位。


地图换一个视觉则如下所示:


当然地图简陋无比,这灯光和摄像机整成的渣画面,绝对无法上架,但原理就是这个原理,你让你的策划MM(哪有这么多MM?有也是有另一半的-_-!)布置一张1个亿x1个亿的地图也是这样生成的。制作过程如下:

我们根本就不需要在场景布置任何东西,你只要将摄像机和灯光放到合适位置就行,设置你可以脚本生成灯光和控制摄像机。我这里是用了一个Point light在这迷宫上方,有更好的灯光布置方式真的请多加指教!你只要在Asset新建一个Rescource文件夹,将上述的Map.csv如同《【Unity3D】BGM》(点击打开链接)中的BGM或者《【Unity3D】对话》(点击打开链接)中的对白导入到这个Rescoure文件夹则行。


整一个空物体布置如下的Map.cs就行。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;//这里用到了C#容器

public class Map : MonoBehaviour
{

    void Start()
    {
        TextAsset textAsset = (TextAsset)Resources.Load("Map");//载入Map.csv,注意不要有其它格式叫Map的,或许有未知错误
        string[] map_row_string = textAsset.text.Trim().Split('\n');//清除这个Map.csv前前后后的换行,空格之类的,并按换行符分割每一行
        int map_row_max_cells = 0;//计算这个二维表中,最大列数,也就是在一行中最多有个单元格
        List<List<string>> map_Collections = new List<List<string>>();//设置一个C#容器map_Collections
        for (int i = 0; i < map_row_string.Length; i++)//读取每一行的数据
        {
            List<string> map_row = new List<string>(map_row_string[i].Split(','));//按逗号分割每个一个单元格
            if (map_row_max_cells < map_row.Count)
            {//求一行中最多有个单元格,未来要据此生成一个Plane来放Cube的
                map_row_max_cells = map_row.Count;
            }
            map_Collections.Add(map_row);//整理好,放到容器map_Collections中
        }

        /*生成一个刚好放好Cube的Plane*/
        GameObject map_plane = GameObject.CreatePrimitive(PrimitiveType.Plane);//生成一个Plane
        map_plane.transform.position = new Vector3(0, 0, 0);//放到(0,0,0)这个位置
        //求其原始大小
        float map_plane_original_x_size = map_plane.GetComponent<MeshFilter>().mesh.bounds.size.x;
        float map_plane_original_z_size = map_plane.GetComponent<MeshFilter>().mesh.bounds.size.z;
        //缩放这个Map到所需大小,刚好和二维表匹配
        float map_plane_x = map_row_max_cells / map_plane_original_x_size;
        float map_plane_z = map_Collections.Count / map_plane_original_z_size;
        map_plane.transform.localScale = new Vector3(map_plane_x, 1, map_plane_z);

        /*在Plane上放Cube*/
        for (int i = 0; i < map_Collections.Count; i++)//Z方向是长度就是容器的大小,也就是map.csv有多少有效的行
        {
            for (int j = 0; j < map_Collections[i].Count; j++)
            {//X方向的宽度就是容器一行中的最大的长度,也就是map.csv中每行最大长度
                int cube_num = int.Parse(map_Collections[i][j]);//将每个单元格的数字转换成整形                
                for (int k = 0; k < cube_num; k++)
                {//根据数字,在一个单元格内生成cube
                    GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                    cube.transform.position = new Vector3(-(map_row_max_cells / 2) + i, (float)0.5 + k, -(map_Collections.Count / 2) + j);
                    /*
                     cube所处的坐标就是(-(map_row_max_cells / 2) + i, (float)0.5 + k, -(map_Collections.Count / 2) + j)
                     */
                }
            }
        }
    }

}
整个算法的思想如下图所示:


首先要注意的是,笛卡尔三维坐标系Y是垂直的轴。

然后算法内,在Unity3D你要缩放一个物体,只能用到~.transform.localScale去单位缩放,也就是说只能设置缩放多少倍,但我们就是要将原本默认的10x10的Plane缩放的7x7该怎么做呢?就是要利用~.GetComponent<MeshFilter>().mesh.bounds.size.X/Y/Z;求出该物体在X/Y/Z轴的原始尺寸,然后再用7/这个原始尺寸,就是我们要这个物体缩放的比例。

最后,至于这些二维表文件配合容器处理的思想,可以参考《【C#】读取txt、csv等二维表》(点击打开链接)和《【Java】一行代码读完记事本中的二维表》(点击打开链接)我已经一些再写了,不赘述了。


Unity 文件读取

阅读数 1

没有更多推荐了,返回首页