找回密码
 立即注册
查看: 874|回复: 20

Unity3D热更新LuaFramework入门实战(4)——Lua组件

[复制链接]
发表于 2021-12-15 09:08 | 显示全部楼层 |阅读模式
基于组件的编程模式是Unity3D的核心思想之一,然而使用纯lua编程,基本就破坏了这一模式。那么有没有办法做一些封装,让Lua脚本也能挂载到游戏物体上,作为组件呢?
By 罗培羽  
1、设计思想

在需要添加Lua组件的游戏物体上添加一个LuaComponent组件,LuaComponent引用一个lua表,这个lua表包含lua组件的各种属性以及Awake、Start等函数,由LuaComponent适时调用Lua表所包含的函数。
下面列举lua组件的文件格式,它包含一个表(如Component),这个表包含property1 、property2 等属性,包含Awake、Start等方法。表中必须包含用于派生对象的New方法,它会创建一个继承自Component的表o,供LuaComponent调用。
Component=            --组件表
{
        property1 = 100,
        property2 = “helloWorld”
}

function Component:Awake()
        print("TankCmp Awake name = "..self.name );
end

function Component:Start()
        print("TankCmp Start name = "..self.name );
End

--更多方法略

function Component:New(obj)
        local o = {}
    setmetatable(o, self)  
    self.__index = self  
        return o
end   
2、LuaComponent组件

LuaComponent主要有Get和Add两个静态方法,其中Get相当于UnityEngine中的GetComponent方法,Add相当于AddComponent方法,只不过这里添加的是lua组件不是c#组件。每个LuaComponent拥有一个LuaTable(lua表)类型的变量table,它既引用上述的Component表。
Add方法使用AddComponent添加LuaComponent,调用参数中lua表的New方法,将其返回的表赋予table。
Get方法使用GetComponents获取游戏对象上的所有LuaComponent(一个游戏对象可能包含多个lua组件,由参数table决定需要获取哪一个),通过元表地址找到对应的LuaComponent,返回lua表。代码如下:
using UnityEngine;
using System.Collections;
using LuaInterface;
using LuaFramework;

public class LuaComponent : MonoBehaviour
{
        //Lua表
        public LuaTable table;

        //添加LUA组件  
        public static LuaTable Add(GameObject go, LuaTable tableClass)  
        {  
                LuaFunction fun = tableClass.GetLuaFunction("New");
                if (fun == null)
                        return null;

                object[] rets = fun.Call (tableClass);
                if (rets.Length != 1)
                        return null;

                LuaComponent cmp = go.AddComponent<LuaComponent>();  
                cmp.table = (LuaTable)rets[0];
                cmp.CallAwake ();
                return cmp.table;
        }  

        //获取lua组件
        public static LuaTable Get(GameObject go,LuaTable table)  
        {  
                LuaComponent[] cmps = go.GetComponents<LuaComponent>();  
                foreach (LuaComponent cmp in cmps)
                {
                        string mat1 = table.ToString();
                        string mat2 = cmp.table.GetMetaTable().ToString();
                        if(mat1 == mat2)
                        {
                                return cmp.table;
                        }
                }
                return null;  
        }  
        //删除LUA组件的方法略,调用Destory()即可  

        void CallAwake ()
        {
                LuaFunction fun = table.GetLuaFunction("Awake");
                if (fun != null)
                        fun.Call (table, gameObject);
        }

        void Start ()
        {
                LuaFunction fun = table.GetLuaFunction("Start");
                if (fun != null)
                        fun.Call (table, gameObject);
        }

        void Update ()
        {
                //效率问题有待测试和优化
                //可在lua中调用UpdateBeat替代
                LuaFunction fun = table.GetLuaFunction("Update");
                if (fun != null)
                        fun.Call (table, gameObject);
        }

        void OnCollisionEnter(Collision collisionInfo)
        {
                //略
        }

    //更多函数略
}

3、调试LuaCompomemt

现在编写名为TankCmp的lua组件,测试LuaCompomemt的功能,TankCmp会在Awake、Start和Update打印出属性name。TankCmp.lua的代码如下:
TankCmp =
{
        --里面可以放一些属性
        Hp = 100,
        att = 50,
        name = "good tank",
}

function TankCmp:Awake()
        print("TankCmp Awake name = "..self.name );
end

function TankCmp:Start()
        print("TankCmp Start name = "..self.name );
end

function TankCmp:Update()
        print("TankCmp Update name = "..self.name );
end

--创建对象
function TankCmp:New(obj)
        local o = {}
    setmetatable(o, self)  
    self.__index = self  
        return o
end  编写Main.lua,给游戏对象添加lua组件。
require "TankCmp"

--主入口函数。从这里开始lua逻辑
function Main()       
    --组件1                               
        local go = UnityEngine.GameObject ('go')
        local tankCmp1 = LuaComponent.Add(go,TankCmp)
        tankCmp1.name = "Tank1"
        --组件2
        local go2 = UnityEngine.GameObject ('go2')
        LuaComponent.Add(go2,TankCmp)
        local tankCmp2 = LuaComponent.Get(go2,TankCmp)
        tankCmp2.name = "Tank2"
end运行游戏,即可看到lua组件的运行结果:


图:程序运行结果


图:程序运行结果

4、坦克组件

下面代码演示用lua组件实现“用键盘控制坦克移动”的功能,TankCmp.lua的代码如下:
TankCmp =
{
        name = "good tank",
}

function TankCmp:Update(gameObject)
        print("TankCmp Update name = "..self.name );
       
        local Input = UnityEngine.Input;
        local horizontal = Input.GetAxis("Horizontal");
        local verticla = Input.GetAxis("Vertical");
       
        local x = gameObject.transform.position.x + horizontal
        local z = gameObject.transform.position.z + verticla
        gameObject.transform.position = Vector3.New(x,0,z)
end

--创建对象
function TankCmp:New(obj)
        local o = {}
    setmetatable(o, self)  
    self.__index = self  
        return o
end  

Main.lua先加载坦克模型,然后给他添加lua组件,代码如下:
require "TankCmp"

--主入口函数。从这里开始lua逻辑
function Main()                                       
        LuaHelper = LuaFramework.LuaHelper;
        resMgr = LuaHelper.GetResManager();
        resMgr:LoadPrefab('tank', { 'TankPrefab' }, OnLoadFinish);
end

--加载完成后的回调--
function OnLoadFinish(objs)
        go = UnityEngine.GameObject.Instantiate(objs[0]);
        LuaComponent.Add(go,TankCmp)
end运行游戏,即可用键盘的控制坦克移动。


图:坦克组件运行结果


最后是广告时间:
《Unity3D网络游戏实战》是笔者即将出版的一本Unity3D实战类书籍。该书通过一个完整的多人坦克对战实例,详细介绍网络游戏开发过程中涉及到的知识和技巧。书中还介绍了服务端框架、客户端网络模块、UI系统的架构等内容。相信透过本书,读者能够掌握Unity3D网络游戏开发的大部分知识,也能够从框架设计中了解商业游戏的设计思路。
《手把手教你用C#制作RPG游戏》(十二五全国高校数字游戏设计精品教材)是笔者出版的第一本书,书中将一款完整单机RPG游戏分解为角色、队伍、地图、NPC、界面系统、物品系统、技能系统、战斗系统、任务系统等模块,一步步介绍每个模块的实现方法,最后完成一款完整的游戏。


图:《手把手教你用C#制作RPG游戏》项目截图

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-12-15 09:10 | 显示全部楼层
沙发沙发。。lua调用c#比c#调用lua效率要高,优化时,尽量减少c#调用lua的次数。。
发表于 2021-12-15 09:13 | 显示全部楼层
《Unity3D网络游戏实战》什么时候出啊?
发表于 2021-12-15 09:15 | 显示全部楼层
谢谢提醒:)
发表于 2021-12-15 09:24 | 显示全部楼层
快了快了,估计1个多月吧,如果你愿意我可以在出版的时候通知你哦
发表于 2021-12-15 09:29 | 显示全部楼层
您能不能开个专栏呀!
发表于 2021-12-15 09:37 | 显示全部楼层
应该会开个专栏吧,等多积累一些文章
发表于 2021-12-15 09:43 | 显示全部楼层
好啊,出版了通知我
发表于 2021-12-15 09:48 | 显示全部楼层
第三步:local tankCmp2 = LuaComponent.Get(go2,TankCmp) 报空指针。LuaComponent组件加上去了。但是除了New方法会执行,其他方法都不执行啊。调试很久代码都没找出原因。。。
发表于 2021-12-15 09:54 | 显示全部楼层
void CallAwake ()         {                LuaFunction fun = table.GetLuaFunction("Awake");                if (fun != null)                        fun.Call (table, gameObject);        }  这里面的fun是空的,lua里面的Awake 方法没有被调到 ,请问该怎么解决
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2025-5-14 22:31 , Processed in 0.144914 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表