unity如何重新生成解决方案_【十一】重新起步

哈,没想到吧,本专栏还有再次更新的一天。

网易游戏雷火事业群:浅谈倩女手游中的资源更新​zhuanlan.zhihu.com
776961748caaf296b4282feb40dd8475.png

其实是看到这个之后,决定把热更新也整合进去。既然要整合热更新,那现在的框架不得不重新写一次,因为代码最终要编译成Dll的。

这次重写的话,我做出了如下选择:

脚本语言:xLua

Unity版本:Unity 2019.3.5 (VS2019)

文件夹设置:

a5a6f9b190c7849cb12b640d2d21a38f.png

游戏工程的建立

没啥好说的,中规中矩建一个3D工程。随意新建一个脚本,用VS打开,在“解决方案管理器”视图找到Unity相关的引用,其属性为:

984d198afa45d17e2f49774cd267e343.png

把路径里用到的东西打包复制到Dll工程的3rd目录下,供Dll工程引用。然后再找到pdb2mdb.exe和mono.exe:

e01a402194c0e3de9d9bea03fa6968fa.png

911f8c6032802799d8a543252c439547.png

如果电脑里有多个Unity版本,则可能会有多个该程序,选择对应版本的即可,等下要用的是路径,pdb2mdb.exe是DLL的调试符号转换器,不生成mdb就没法调试DLL中的代码。

类库工程建立

依旧是中规中矩创建一个类库的工程。先添加刚才准备好的Unity相关程序集引用,然后打开项目属性,设置生成后事件:

"C:Program FilesUnityHubEditor2019.3.5f1EditorDataMonoBleedingEdgebinmono.exe" "C:Program FilesUnityHubEditor2019.3.5f1EditorDataMonoBleedingEdgelibmono4.5pdb2mdb.exe" $(TargetPath)
copy $(TargetPath) $(ProjectDir)....GameGameFrameAssetsDll$(TargetFileName)
copy $(TargetPath).mdb $(ProjectDir)....GameGameFrameAssetsDll$(TargetFileName).mdb

此处就使用了mono和pdb2mdb的路径,生成之后复制到游戏工程中。这里随便写了一个测试类,在游戏工程中可见,Dll中已经有这个类了。

1a16d206d6038c125e91b7165bda96a8.png

打上断点,挂上调试。然后运行游戏工程,好的,成功断住:

4579be639b33b21d23c0d4b2b196fce0.png

接入xLua

将Plugin放进游戏工程中,将Src下的源码分别放进Editor目录和DLL工程中,测试一下:

LuaEnv n = new LuaEnv();
n.DoString("local p = {}");
Debug.Log(n.ToString());

没毛病,成功启动Lua虚拟机。接下来就是要配置xLua,选择一些可能要在Lua端使用的C#内容,按照xLua文档所示,直接整个静态类静态List,把要用的东西码入。

这里我根据个人的判断添加了如下内容:

using System;
using System.Collections.Generic;
using UnityEngine;
using XLua;

public static class CustomGeneratorConfig 
{
    // 此处是Unity和系统的原生类型配置
    [LuaCallCSharp]
    public static List<Type> _LCS_UnityTypeList = new List<Type>()
    {
        // 测试
        typeof(Debug),
        // 基础
        typeof(RectTransform),
        typeof(Component),
        typeof(Behaviour),
        typeof(Transform),
        typeof(MonoBehaviour),
        typeof(System.Object),
        typeof(UnityEngine.Object),
        typeof(Time),
        typeof(GameObject),
        typeof(Application),
        typeof(Screen),
        typeof(PlayerPrefs),
        // 结构
        typeof(Vector2),
        typeof(Vector3),
        typeof(Vector4),
        typeof(Rect),
        typeof(Quaternion),
        typeof(Color),
        typeof(Ray),
        typeof(Bounds),
        typeof(Ray2D),
        typeof(Resources),
        typeof(TextAsset),
        // 物理
        typeof(BoxCollider),
        typeof(BoxCollider2D),
        typeof(CircleCollider2D),
        typeof(SphereCollider),
        typeof(CapsuleCollider),
        typeof(CapsuleCollider),
        typeof(AudioSource),
        typeof(AudioClip),
        // 动画
        typeof(Keyframe),
        typeof(AnimationCurve),
        typeof(AnimationClip),
        // 渲染
        typeof(Material),
        typeof(Mesh),
        typeof(Texture2D),
        typeof(ParticleSystem),
        typeof(SkinnedMeshRenderer),
        typeof(Renderer),
        typeof(Camera),
    };

    // 此处是自定义类型的配置
    [LuaCallCSharp]
    public static List<Type> _LCS_FrameTypeList = new List<Type>()
    {

    };


    // 黑名单
    [BlackList]
    public static List<List<string>> _LCS_UnityBlackList = new List<List<string>>()
    {

    };

    [BlackList]
    public static List<List<string>> _LCS_FrameBlackList = new List<List<string>>()
    {

    };
}

然后调整一下配置生成器的路径:

public static string common_path = Application.dataPath + "../../../../Core/GameCore/xLua/Gen/";

然后执行一下生成命令,更新Dll工程,然后编译一下。是时候测试了。

LuaEnv n = new LuaEnv();
n.DoString("CS.UnityEngine.Debug.Log('hello')");

也确实打印出来了“hello”字样。

API定义生成

写这种脚本语言,没有个编辑和调试插件其实是挺蛋疼的。这里推荐luaide,直接在vscode插件里找就可以,收费也比较便宜。

不过暂时还没有用它的打算,而是先接入它的api定义生成,这样编写Lua的时候可以相对直观的看到C#中一些api和数据的写法。

ShowDoc​www.showdoc.cc

放进工程目录之后,修改导出路径,然后注释掉LuaIdeApi.cs里的菜单标签和自动生成;在xlua的生成函数末尾添加LuaIdeApi的生成即可。

这样,每次生成xlua内容的时候就会自动把api定义也更新,可以说是非常完美。

VS Code准备

用VS Code打开了Lua文件夹之后,会发现Unity生成的.meta文件也被计入了其中,因此我们要设定过滤,保证VS Code开发环境的清爽:

0ae4e6d20b9719ea42e063062047ae25.png

另外我比较习惯折叠代码,这点VSCode的Lua样式还没有,手动打开配置一下:

72ad60fedc6beabeabf582ba313bd2f1.png
    "folding": {
        "offSide": true,
        "markers": {
            "start": "^s*--s*regionb",
            "end": "^s*--s*endregionb"
        }
    }

加载Lua

在编辑器下的开发应当越快越好,越高效越好,所以加载的时候,就有必要设计一种编辑器下的加载模式,跟使用AssetBundle或者其他自定义数据存储形式的生产环境不同,该模式应当做到修改PrefabLua等资产之后,无需打包即可立刻启动。

因为是为了测试xLua的加载,所以一切从简:

using UnityEngine;
using System;

using Object = UnityEngine.Object;

namespace GameCore.Loader
{
    interface ILoadEx
    {
        Object LoadAsset(LoadContext context);
        void LoadAsset(LoadContext context, Action<Object, LoadContext> onLoaded);
        string LoadLua(string luaPath);
        XLua.LuaEnv.CustomLoader GetCustomLoader();
    }
}

其中LoadContext是一个发起加载的上下文,该结构我暂时还没想好填充什么,不过也无关紧要,对于现在的测试来说,只要保证LoadLua方法可用就行。

    class EditorLoadEx : BaseLoadEx
    {
        public override LuaEnv.CustomLoader GetCustomLoader()
        {
            return (ref string path) => {
                Debug.Log(path);
                var lua = LoadLua(path);
                return string.IsNullOrEmpty(lua) ? null : System.Text.Encoding.UTF8.GetBytes(lua);
            };
        }

        public override string LoadLua(string luaPath)
        {
            string path = Application.dataPath + "/Editor/Lua/" + luaPath + ".lua";
            if (File.Exists(path))
            {
                return File.ReadAllText(path);
            }
            return null;
        }
    }

这里使用System.IO.File而不是用AssetDatabase的原因是,“*.lua”在Unity中会被认为是DefaultAsset,无法被当做TextAsset处理。

-- region Print
local function toString(...)
    local params = {...}
    local s = ""
    for i = 1, #params do
        if i == 1 then
            s = params[i]
        else
            s = s..", "..params[i]
        end
    end
    return s
end

function print(...)
    CS.UnityEngine.Debug.Log(toString(...).."n"..debug.traceback())
end
-- endregion

print("1", "2")

在测试用的MonoBehavior类中用require('Game/Game'),打印成功。

Lua的class实现

才疏学浅,自己写的果然又长又臭,这里使用了quick-cocos2d-x的实现,但是因为我并没用到什么native C++的东西,所以大笔一挥,只保留了基本的Lua Object的内容:

chukong/quick-cocos2d-x​github.com
29729cd28d0c392682a01f465cbb899d.png
-- Lua OOP Class
-- From quick-cocos2d-x
function class(classname, super)
    local superType = type(super)
    local cls

    if superType ~= "table" then
        superType = nil
        super = nil
    end

    if super then
        cls = {}
        setmetatable(cls, {__index = super})
        cls.super = super
    else
        cls = {ctor = function() end}
    end

    cls.__cname = classname
    cls.__index = cls

    function cls.new(...)
        local instance = setmetatable({}, cls)
        instance.class = cls
        instance:ctor(...)
        return instance
    end

    return cls
end

好,我们来稍加测试一下:

SRequire "Game.Class"

local A = class("base")
function A:test()
    print(self.value)
end
function A:ctor(value)
    self.value = value
end

local B = class("sub1", A)
function B:test2()
    print("d")
end

local C = class("sub2", A)
function C:ctor(value, cvalue)
    self.super:ctor(value)
    self.cvalue = cvalue
end
function C:test()
    self.super:test()
    print(self.value, self.cvalue)
end

local a = A.new("a")
local b = B.new("b")
local c = C.new("c", "cc")

a:test()
b:test()
b:test2()
c:test()

3b0bd1c45dff9e6cfa86d2e50a872026.png

表格的使用

将Excel表格生成成如下形式,代码可参照之前的内容:

local K = {
	[1] = "id",
	[2] = "name",
	}

_M = {
	[1] = {[K[1]]=1,[K[2]]="???"},
	[2] = {[K[1]]=2,[K[2]]="wdnmd"},
}
return _M

使用一个_Data.lua来封装所有对数据表的查询操作:

-- 表数据查询封装层
local _P = {}
local _M = setmetatable({},
    {__index = function(tab, key)
        if _P[key] == nil then
            local t = SRequire ("DataTable."..key)
            if t == nil then
                _P[key] = {}
            else
                _P[key] = t
            end
            return _P[key]
        else    
            return _P[key]
        end
    end}
)

return _M

测试,然后通过:

//...
local DT = SRequire "DataTable._Data"

local t = DT.Test
print(DT.Test[1].name)

3aaedd257c04f668da35cd87f4d5c24f.png
表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页