首先谈下自己对脚本模块的看法,脚本的好处是即时修改,不用重新编译并运行exe程序,基于这个好处,可以把可能会经常修改调整的逻辑用脚本来实现,而引擎只负责基本不变的逻辑。但变与不变具有相对性,对专用引擎来说,因为需要定制很多功能,所以经常 变的主要是这些功能的配置等逻辑,而对通用引擎来说,理论上用户可以自定义开发各种各样的游戏,所以整个游戏逻辑都属于会变的,应该可以由脚本来实现,当然完全独立出来是不现实的,因为有些逻辑跟引擎结合很紧或脚本实现效率太低。
现在回到ce3,ce3确实尽量把很多游戏逻辑独立出来,比较深入用过ce3的编辑器sandbox的人应该有所体会,sandbox里面有很多entity对象,这些对象都有脚本文件,实际上这些对象都是由自己的脚本文件创建出来的,用户可以通过创建脚本来创建自己的entity和entity的游戏逻辑,下面分析下ce3里面entity的lua脚本集成做法,只记录下实现思想,不涉及具体代码。
1、用lua表现一个entity对象
在c++中要表现一个对象最常用的就是类(类包含数据和函数),lua中没有类,但使用的技术可以变相实现相同的效果,lua通过table、 meta table、__index、__newindex来实现c++类中的基类和派生类,ce3中entity对象都是以table来定义的,对象的属性和函数都是table中的数据。
2、引擎和脚本的交互
一个lua对象定义好后,那么需要和引擎就需要进行交互,如:创建一个实例、修改实例属性、调用实例的函数等。
ce3在引擎脚本模块初始化时会扫描entity的资源,把每个entity对象(table)加载到lua中,然后c++内保存的lua中的引用,这个entity对象或者说table对象是被实例所共享。
当加载关卡或sandbox编辑时一个entity对象可能会产生多个实例,每个实例的属性可以不同,游戏过程中可以调用实例的函数或事件进行交互。这一过程中引擎内部的实际操作是这样的:实例创建的实际操作是创建了两个lua内的table,一个是实例,一个是属性,然后将上面提到的共享的对象中的属性表(以字符串"properties"命名)复制到实例的属性,这些值对于编辑时新加的实例是默认值,关卡加载时这些值则会被编辑时编辑后的值覆盖,最后属性(表)会放到实例(表)中,然后共享的对象表作为实例的__index,这样实例就"继承了"entity的函数。
当引擎调用entity的函数时,将实例(一个lua table)作为第一个参数传给实例的函数,这样函数内访问的值就是实例的值而不是共享对象的值。
由于引擎对脚本的定义是一无所知的,所以所有属性和函数都是用字符串作为关键字来访问的,也预设了一些关键字,如上面提到的属性表名为"properties"还有一些函数名,函数调用参数个数在引擎内部也是预先定义好多个参数的模板。除了一些预设的函数,比如实例的初始化,引擎一般不会调用实例的函数(也不知道entity有那些函数),这些函数通常由用户定义的逻辑来调用,如flowgraph、ai、trackview等。
笔记比较凌乱,主要用于记录自己理解一些思想,要实现完整的脚本模块还有非常多的细节需要考虑和完善。
本文详细解析了CryEngine3如何通过lua脚本实现灵活的扩展性和自定义开发能力,特别关注实体对象的脚本集成方式,包括如何使用table、metatable等技术模拟类的概念,以及引擎与脚本之间的交互机制。
838

被折叠的 条评论
为什么被折叠?



