开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:1866

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 2826|回复: 13

[碧俐千仞] Unity3D热更新LuaFramework入门实战(4)——Lua组件

[复制链接]  [移动端链接]
排名
16961
昨日变化
21

23

主题

176

帖子

686

积分

Rank: 9Rank: 9Rank: 9

UID
53741
好友
26
蛮牛币
1892
威望
0
注册时间
2014-11-6
在线时间
107 小时
最后登录
2017-8-13

专栏作家

发表于 2016-8-22 21:39:43 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

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

x
本帖最后由 tyxxxx 于 2016-8-22 21:42 编辑

基于组件的编程模式是的核心思想之一,然而使用纯lua编程,基本就破坏了这一模式。那么有没有办法做一些封装,让Lua脚本也能挂载到游戏物体上,作为组件呢?


知乎 @罗培羽


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组件的运行结果:

Unity3D热更新LuaFramework入门实战43486.png

图:程序运行结果

Unity3D热更新LuaFramework入门实战43497.png

图:程序运行结果

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热更新LuaFramework入门实战44479.png

图:坦克组件运行结果

最后是广告时间:

《Unity3D网络游戏实战》是笔者即将出版的一本Unity3D实战类书籍。该书通过一个完整的多人坦克对战实例,详细介绍网络游戏开发过程中涉及到的知识和技巧。书中还介绍了服务端框架、客户端网络模块、UI系统的架构等内容。相信透过本书,读者能够掌握Unity3D网络游戏开发的大部分知识,也能够从框架设计中了解商业游戏的设计思路。

《手把手教你用C#制作RPG游戏》(十二五全国高校数字游戏设计精品教材)是笔者出版的第一本书,书中将一款完整单机RPG游戏分解为角色、队伍、地图、NPC、界面系统、物品系统、技能系统、战斗系统、任务系统等模块,一步步介绍每个模块的实现方法,最后完成一款完整的游戏。

Unity3D热更新LuaFramework入门实战44962.png

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



评分

参与人数 3鲜花 +27 收起 理由
浮云zzy + 5 很给力!
笑梦天 + 2 很给力!
仅为年时 + 20 很给力!

查看全部评分


回复

使用道具 举报

6蛮牛粉丝
1130/1500
排名
1113
昨日变化
3

8

主题

30

帖子

1130

积分

Rank: 6Rank: 6Rank: 6

UID
10877
好友
0
蛮牛币
6520
威望
0
注册时间
2013-12-22
在线时间
282 小时
最后登录
2017-9-21
发表于 2016-8-22 23:21:46 | 显示全部楼层
在哪能买到书

回复

使用道具 举报

排名
299
昨日变化

0

主题

156

帖子

2342

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
10192
好友
27
蛮牛币
3478
威望
0
注册时间
2013-12-12
在线时间
728 小时
最后登录
2017-9-18
发表于 2016-8-23 08:54:02 | 显示全部楼层
大神好厉害!!!

回复

使用道具 举报

4四处流浪
335/500
排名
6000
昨日变化
2

0

主题

38

帖子

335

积分

Rank: 4

UID
93748
好友
0
蛮牛币
579
威望
0
注册时间
2015-4-20
在线时间
131 小时
最后登录
2017-9-20
发表于 2016-8-23 09:54:12 | 显示全部楼层
大神啊,能把demo发出来吗

回复 支持 反对

使用道具 举报

排名
50570
昨日变化
751

0

主题

1

帖子

3

积分

Rank: 1

UID
163756
好友
0
蛮牛币
7
威望
0
注册时间
2016-8-23
在线时间
0 小时
最后登录
2016-8-23
发表于 2016-8-23 14:55:25 | 显示全部楼层
看上去不错,收藏慢慢看看

回复 支持 反对

使用道具 举报

2初来乍到
128/150
排名
15383
昨日变化
14

4

主题

33

帖子

128

积分

Rank: 2Rank: 2

UID
164237
好友
0
蛮牛币
167
威望
0
注册时间
2016-8-25
在线时间
61 小时
最后登录
2017-3-1
发表于 2016-10-17 17:14:27 | 显示全部楼层
本帖最后由 lieb 于 2016-10-17 17:17 编辑

照着调试, 没成功, 不懂, 我用的最新LuaFramework ugui   , 不应该吧LuaComponent  注册在哪里么? 至少需要写一个LuaComponenWrap吧。 lua怎么能直接就识别呢, 还事放在某个指定文件夹下的 脚本都自动 添加到lua 全局表里?

回复 支持 反对

使用道具 举报

排名
16961
昨日变化
21

23

主题

176

帖子

686

积分

Rank: 9Rank: 9Rank: 9

UID
53741
好友
26
蛮牛币
1892
威望
0
注册时间
2014-11-6
在线时间
107 小时
最后登录
2017-8-13

专栏作家

 楼主| 发表于 2016-10-17 19:01:06 | 显示全部楼层
lieb 发表于 2016-10-17 17:14
照着调试, 没成功, 不懂, 我用的最新LuaFramework ugui   , 不应该吧LuaComponent  注册在哪里么? 至少 ...

可以参考下  http://www.manew.com/thread-92557-1-1.html  这篇文章的《自定义API》一节

回复 支持 反对

使用道具 举报

5熟悉之中
815/1000
排名
3142
昨日变化
12

3

主题

95

帖子

815

积分

Rank: 5Rank: 5

UID
45560
好友
1
蛮牛币
118
威望
0
注册时间
2014-9-18
在线时间
363 小时
最后登录
2017-9-20
发表于 2016-11-23 14:11:14 | 显示全部楼层
按照教程调试不成,local tankCmp1 = LuaComponent.Add(go,TankCmp),这一步报错LuaException: [string "Main.lua"]:9: attempt to index global 'LuaComponent' (a nil value),脚本中也require "Common.TankCmp"还是不行,求解

回复 支持 反对

使用道具 举报

5熟悉之中
656/1000
排名
6784
昨日变化
8

2

主题

101

帖子

656

积分

Rank: 5Rank: 5

UID
107980
好友
0
蛮牛币
60
威望
0
注册时间
2015-6-13
在线时间
413 小时
最后登录
2017-9-18
发表于 2016-11-24 17:54:04 | 显示全部楼层
LuaFunction fun = table.GetLuaFunction("Awake");
这句无法获取到方法,fun为空,遇到这个问题了。

回复 支持 反对

使用道具 举报

排名
16961
昨日变化
21

23

主题

176

帖子

686

积分

Rank: 9Rank: 9Rank: 9

UID
53741
好友
26
蛮牛币
1892
威望
0
注册时间
2014-11-6
在线时间
107 小时
最后登录
2017-8-13

专栏作家

 楼主| 发表于 2016-12-6 22:30:20 | 显示全部楼层

《Unity3D网络游戏实战》已经出版了,感谢关注^_^

回复 支持 反对

使用道具 举报

排名
16961
昨日变化
21

23

主题

176

帖子

686

积分

Rank: 9Rank: 9Rank: 9

UID
53741
好友
26
蛮牛币
1892
威望
0
注册时间
2014-11-6
在线时间
107 小时
最后登录
2017-8-13

专栏作家

 楼主| 发表于 2016-12-6 22:31:34 | 显示全部楼层
xiaopan 发表于 2016-11-23 14:11
按照教程调试不成,local tankCmp1 = LuaComponent.Add(go,TankCmp),这一步报错LuaException: [string "Ma ...

是不是没有加上_GT(LuaComponet)呢

回复 支持 反对

使用道具 举报

排名
16961
昨日变化
21

23

主题

176

帖子

686

积分

Rank: 9Rank: 9Rank: 9

UID
53741
好友
26
蛮牛币
1892
威望
0
注册时间
2014-11-6
在线时间
107 小时
最后登录
2017-8-13

专栏作家

 楼主| 发表于 2016-12-6 22:33:39 | 显示全部楼层
BOOM沙卡拉卡 发表于 2016-11-24 17:54
LuaFunction fun = table.GetLuaFunction("Awake");
这句无法获取到方法,fun为空,遇到这个问题了。 ...

说明没有找到lua脚本里面的Awake方法,有两个地方可能会导致这个问题,可以检查看看cmp.table = (LuaTable)rets[0];这里的table是不是符合预期的(即new出来的返回值),而且这个返回值是继承自TankCmp,且确保表TankCmp 有Awake方法

回复 支持 反对

使用道具 举报

5熟悉之中
536/1000
排名
7711
昨日变化
69

2

主题

96

帖子

536

积分

Rank: 5Rank: 5

UID
138025
好友
1
蛮牛币
129
威望
0
注册时间
2016-3-1
在线时间
320 小时
最后登录
2017-9-19
发表于 2017-1-3 15:19:48 | 显示全部楼层
运行后,main.lua里面的第一句,require TankCmp就错了,要怎么require文件?

回复 支持 反对

使用道具 举报

6蛮牛粉丝
1446/1500
排名
1217
昨日变化
1

5

主题

201

帖子

1446

积分

Rank: 6Rank: 6Rank: 6

UID
10760
好友
1
蛮牛币
3030
威望
0
注册时间
2013-12-20
在线时间
412 小时
最后登录
2017-9-19
发表于 2017-3-9 18:28:51 | 显示全部楼层
tyxxxx 发表于 2016-12-6 22:33
说明没有找到lua脚本里面的Awake方法,有两个地方可能会导致这个问题,可以检查看看cmp.table = (LuaTabl ...

我也遇到同样的问题,TankCmp中有Awake方法,您说的table是不符合预期的,这个如何判断呢?谢谢

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

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