当前位置:网站首页>Roblox剑九之剑二
Roblox剑九之剑二
2022-06-29 06:39:00 【爱coding的小杨】
Roblox剑九之剑二【御码之剑】
前言
博主于2020年入坑UGC游戏开发,至今已产出多款UGC作品,如异能危机、英雄远征之天劫、王者之剑、百变大富翁……可以说UGC游戏开发是一种创业的新风口。就如当今的低代码一样,易上手,多产出,快成效。
本博文主要带大家攻破LUA这一脚本语言-以Roblox为例。
一、学习路线
- 图片形式

二、Lua基础
目前所知的UGC游戏创作平台【重启世界-Reworld】、【Roblox】均使用Lua这一脚本语言进行UGC游戏的程序编程。博主的第一门面向对象的语言是C#,主要为GIS开发服务。后来遇到UGC游戏开发,学习了辅助语言-Lua,Lua这一脚本语言或许许多人没有听说过,但这一强大的胶水语言确确实实在我们身边,就以游戏为例
- 完全可以和C/C++、C#、Java完美地进行交互
- 定义、存储和管理基础游戏数据
- 创建和维护开发者友好的游戏存储和载入系统
- 创建功能原型,可以之后用高性能语言移植
- 进行游戏的
热更新操作【即补丁,常用】
1、Lua介绍
Lua是一种
轻量小巧的脚本语言,它由标准的C语言编写并且是开源的,可以很方便的和其他程序进行集成和扩展(C#,Java…),其设计目的是为了嵌入应用程序中,为应用程序提供灵活的扩展和定制功能。
1.1 解释型语言
Lua是脚本语言,脚本语言是解释型语言的其中一种。针对编程语言的分类,不同角度有不同类型的分类。其中编译型语言和解释型语言主要以程序如何执行而进行的分类。关于两者的具体的区别可以参考戳我
简单来说
- 解释型语言是
解释器边解释边执行程序,不生成可执行文件。 - 编译型语言是
编译器先编译后执行程序,生成可执行文件,可重复运行可执行文件。
2、Lua特性
- 支持面向过程编程和
函数式编程 自动内存管理,只提供一种通用类型的表(table),但可以实现数组,哈希表,集合,对象。- 语言内置匹配模式。
- 闭包(closure)。
- 函数也可以看作一个值。
- 提供
多线程(协程,不是操作系统支持的线程)。 - 通过
闭包和table可以方便的支持面向对象编辑所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。
3、Lua的数据类型
3.1 数据类型
Lua中的数据类型主要有八种,分别是nil、boolean、number、string、function(函数式编程)、table、thread、userdata。
| 数据类型 | 描述 |
|---|---|
| nil | 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。 |
| boolean | 包含两个值:false和true。 |
| number | 表示双精度类型的实浮点数 |
| string | 字符串由一对双引号或单引号来表示 |
| function | 由 C 或 Lua 编写的函数 |
| userdata | 表示任意存储在变量中的C数据结构 |
| thread | 表示执行的独立线路,用于执行协同程序 |
| table | Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。 |
示例
print(type("Hello world")) --> string
print(type(10.4*3)) --> number
print(type(print)) --> function
print(type(type)) --> function
print(type(true)) --> boolean
print(type(nil)) --> nil
print(type(type(X))) --> string
3.2 Lua变量
3.2.1 变量类型
Lua 变量有3种类型
- 全局变量
- 局部变量(local)
- 表中的域
3.2.2 变量约定
- Lua中的所有
变量全部是全局变量,哪怕是在函数体中也是全局变量,除非用local关键字显式声明为局部变量,局部变量的作用域是从声明处到语句块结束。简而言之,如果不是local声明的,都是全局变量。【重点】 - 变量在使用前,
必须进行声明 - 变量的
默认值都为nil - Lua可以对多个变量
同时赋值,变量列表和值列表需要用逗号隔开 - Lua语言支持
直接赋值而不需声明数据类型的操作。
示例
val = 10 --全局变量
local locVal = 10 --局部变量
function value1()
c = 20
local d = 10
end
value1()
print(val, locVal, c, d) --执行函数后 局部变量的值无法打印,全局变量可以
do
local val = 4 --定义局部变量val
locVal = 5 --对局部变量重新赋值
print(val, locVal) --打印do内的val = 4以及locVal = 5
end
print(val, locVal) --这里的val是全局变量val,locVal为被重新赋值的局部变量locVal
--多个变量同时赋值
a, b = 10, 2*x -- a=10; b=2*x
- 补充 注释写法
--单行注释
或者
--[[ 多行注释 --]]
4、控制语句
4.1 分支语句
if(布尔表达式) then
--[ 布尔表达式为 true 时执行该语句块 --]
else
--[ 布尔表达式为 false 时执行该语句块 --]
end
4.2 循环语句
| 循环类型 | 描述 |
|---|---|
| while | 循环在条件为 true 时,让程序重复地执行某些语句。执行语句前会先检查条件是否为 true。 |
| for | 循环重复执行指定语句,重复次数可在 for 语句中控制。 |
| repeat…until | 重复执行循环,直到 指定的条件为真时为止 |
4.2.1 while循环
while(condition)do
<执行体>
end
4.2.2 for循环
1)数值for循环
for var=exp1,exp2,exp3 do
<执行体>
end
var从exp1变化到exp2,每次变化以exp3为步长递增var,并执行一次”执行体”。- exp3是可选的,如果不指定,
默认为1。
2)泛型for循环
注游戏开发中常用
泛型for循环通过一个迭代器函数来遍历所有值,类似java中的foreach语句。
--打印数组a的所有值
for k,v in ipairs(a) do
print(v)
end
简单而言,k,v为键值对,k是索引值,v是对应索引的元素值。
4.2.3 repeat…until 循环
先执行一次,再进行条件判断
repeat
<执行体>
while(condition)
循环控制语句
- break 退出当前循环或语句,脚本开始执行
紧接着的语句。
lua中没有continue关键词 - 变相实现
continue
for i = 1, 100 do
while true do
if i % 2 == 1 then break end
-- 这里有一大堆代码
--
--
break
end
end
4.3 函数
optional_全局/局部 function 函数名(argument1, argument2, argument3..., argumentn)
函数体
return 函数返回值
end
- Lua函数可以
返回多个结果值,在return后列出要返回的值得列表即可返回多值,如return m, mi - Lua函数可以接受
可变数目的参数,和C语言类似在函数参数列表中使用三点(…)表示函数有可变的参数,Lua将函数的参数放在一个叫arg的表中,#arg表示传入参数的个数
二、Lua的表(table)
1、table的介绍
表(Table)是Lua语言中最主要(事实上也是唯一的)和强大的数据结构。
使用表,Lua语言可以以一种简单、统一且高效的方式表示数组、集合、记录和其他很多数据结构。
Lua语言也使用表来表示包( package )和其他对象。
Lua语言中的表本质上是一种辅助数组( associative array ),这种数组不仅可以使用数值作为索引,也可以使用字符串或其他任意类型的值作为索引(nil除外)。
同一个表存储的值可以具有不同的类型索引,并且可以按需求增长以容纳新的元素
示例
当调用函数math.sin时,我们可能认为是“调用了math库中函数sin”;
而对于Lua语言来说,其实际含义是“以字符串"sin"为键检索表math”。
2、table的使用
2.1 初始化
1、
local a = {
}
2、
local a = {
["x"] = 12,["mutou"] = 99,[3] = "hello"}
print(a["x"])
3、
local a = {
x=12,mutou=99,[3]="hello"}
print(a["x"])
4、
local a = {
x=12,mutou=99,[3]="hello"}
print(a.x)
注:
1为空表初始化,2为初始化并赋初值的通用操作(使用[]代表索引,使用=将索引和值联系起来)
3和4为索引是字符串的简洁写法
默认数字索引(列表式构造器)
local a ={
[1]=12,[2]=43,[3]=45,[4]=90}
简洁版 local a = {
12,43,45,90}
print(a[1])
- 使用
列表式构造器时,默认的索引从1开始(而不是0),并且依次类推。这与其它语言不一样。 没有给出索引的,由表自动给出数字索引
2.2 操作
2.2.1 访问元素
重点
使用.这个语法糖后接字符串索引值或[索引值]即可访问table的索引对应的元素值
注:table的长度用#表名进行表示
local a = {
{
x = 1,y=2},
{
x = 3,y = 10}
}
print(a[1].x)
print(#a)
补充 in pairs 和 in ipairs的区别
in pairs方法是比较通用的,不会过分强调table中的key值
in iparis方法则就强调table的key值为顺序排列,当顺序中断的时候,for循环终止
2.2.2 插入
使用table.insert()方法
tab2 = {
"a",2,"b",5} --定义一个table
table.insert(tab2,2,"king") --指定在某一位置插入某值
for i, v in ipairs(tab2) do
-- print(v) --输出a king 2 b 5
end
table.insert(tab2,3) --没有指定位置的话,默认将值插入到末尾位置
for i, v in ipairs(tab2) do
-- print(v) --输出a king 2 b 5 3
end
tab3 = {
"d",7,"e"}
table.insert(tab2,tab3) -- 将table插入table
for i, v in ipairs(tab2[7]) do
--print(v) --输出d 7 e
end
tab2["mm"]="mmm" --添加一个新的键值对 下面的for迭代器选择pairs才能将新的键值对遍历出来,而非ipairs
for i, v in pairs(tab2) do
print(i,v) --输出1 a ; 2 king ; 3 2 ; 4 b ; 5 5 ; 6 3 ; 7 table ;mm mmm
end
2.2.3 删除
使用table.remove()方法
tab4 = {
1,4,"tt","jj"}
table.remove(tab4,1) --移除指定位置的table值,若没有指定位置,则默认移除最后一位的元素
for i, v in ipairs(tab4) do
print(v) --输出 4 tt jj
end
2.2.4 更新
访问索引对应的元素并赋值即可
2.2.5 排序
使用table.sort()方法
language = {
"lua","java","c#","c++"}
table.sort(language) --只有table一个参数,使用lua默认的排序方式排序
for i, v in ipairs(language) do
-- print(v) --输出c# c++ java lua
end
local function my_comp1(element1,element2) --自定义比较函数 作为table.sort()参数
return element1<element2
end
table.sort(language,my_comp1)
for i, v in ipairs(language) do
print(v) --输出 c# c++ java lua
end
local function my_comp2(element1,element2) --自定义比较函数 作为table.sort()参数
return element1>element2
end
table.sort(language,my_comp2)
for i, v in ipairs(language) do
-- print(v) --输出lua java c++ c#
end
local function my_comp3(element1,element2) --自定义比较函数 作为table.sort()参数
if element1==nil then
return false
end
if element2==nil then
return true
end
return element1>element2
end
language[2]=nil --table中有nil存在的情况
table.sort(language,my_comp3)
for i, v in ipairs(language) do
-- print(v) --输出lua c++ c#
end
2.2.6 移除整个表的内容
- 可以有多个表名引用于同一个表,当最后一个引用释放时,垃圾收集器会最终删除这个表。
- 将表设为
nil,然后等垃圾收集或强制执行一次垃圾收集(collectgarbage)。 - 也就是说,在程序开发中,需要将
表置空来释放表资源。 - 如果
不想置空,可以尝试weak table的使用,需要用到元组的概念
local table = {
1,1,1,1,1,1,11,1,1}
--1.这个方法比较暴力,直接将table置空
table = {
} 或者 table=nil --释放table的引用
print(#table) --- nil
--2.通过遍历循环的办法移除整个table里的元素
for i = #table ,table do
table.remove(table,i)
i = i - 1
end
--注意:在移除table里的元素时,table里的元素会自动补位,所以移除全部从最后一位开始移除
三、元表介绍
1、元表介绍
Lua的
表本质其实是个类似HashMap的东西,其元素是很多的Key-Value对,如果尝试访问了一个表中并不存在的元素时,就会触发Lua的一套查找机制,也是凭借这个机制来模拟了类似“继承”的行为
元表像是一个“操作指南”,里面包含了一系列操作的解决方案,例如__index方法就是定义了这个表在索引失败的情况下该怎么办。
设置元表的方法为setmetatable(son, father) --把son的metatable(元表)设置为father
关键词是setmetatable
1.1 Lua查找表中元素时的规则
- 在表中查找,如果找到,返回该元素,找不到则继续
- .判断该表是否有元表,如果没有元表,返回nil,有元表则继续
- 判断元表有没有
__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值
2、面向对象
面向对象三大特性
- 封装
- 继承
- 多态
详细介绍可以移步Java剑开天门(二)
Lua是不支持面向对象的,但是可以利用元表实现面向对象
2.1 官方做法
- 类定义
- 方法一般用 : 这个语法糖表示
- self代指自己
local Class= {
value=0 } --定义属性
function Class:new(o) --定义new方法,模板可以不用修改
o=o or {
};
setmetatable(o,self); --将self设置为o的元表
self.__index=self; --指向自己
return o;
end
function Class:showmsg() --定义方法
print(self.value);
end
local test = Class:new(); --类的实例化
test:showmsg(); --方法调用
- 类继承
local A = Class:new(); --类A定义
function A:new(o) -- new方法,模板可不修改
local o = o or {
};
setmetatable(o,self);
self.__index=self;
return o;
end
function A:showmsg( ) --普通方法
print (self.value); --父类的属性
print("zhishi 测试啊啊 ")
end
local s = A:new({
value=100}); --类A初始化
s:showmsg(); --方法调用
2.2 优雅方法
class.lua文件
--[[ 模拟继承结构的基类 ]]
local _class = {
}
function Class(super)
local class_type = {
}
class_type.ctor = false --构造函数
class_type.super = super --继承
class_type.New = function(...) --实例
local obj = {
}
do
local create --创建闭包函数
create = function(c, ...)
if c.super then --存在父类,递归创建
create(c.super, ...)
end
if c.ctor then
c.ctor(obj, ...) --构造函数
end
end
create(class_type, ...) --闭包函数入口
end
setmetatable(obj, {
__index = _class[class_type]}) --查找元表
return obj
end
local vtbl = {
} --临时表
_class[class_type] = vtbl --成员变量赋值
setmetatable( --赋值、查找元表(vtbl查找与赋值)
class_type,
{
__newindex = function(t, k, v)
vtbl[k] = v
end,
--For call parent method
__index = vtbl
}
)
if super then --继承查找元表(vtbl赋值,返回父类值)
setmetatable(
vtbl,
{
__index = function(t, k)
local ret = _class[super][k]
vtbl[k] = ret
return ret
end
}
)
end
return class_type
end
--------使用
--当然要调用class.lua文件
base=Class() -- 定义一个基类 base_type
function base:ctor(x) -- 定义 base_type 的构造函数
print("base ctor")
self.x=x
end
function base:print_x() -- 定义一个成员函数 base:print_x
print(self.x)
end
function base:hello() -- 定义另一个成员函数 base:hello
print("hello base")
end
--子类实现
child=Class(base);
function child:ctor()
print("child ctor");
end
function child:hello()
print("hello child")
end
--调用
local a=child.New(21);
a:print_x();
a:hello();
两种方式各有优缺点
博主使用的是
第二种方式,第二种方式和面向对象编程出入不大。local变量则是创建一个新的变量,遵守子作用域覆盖父作用域的规则。
特别是对于require "modname"中的模块,在编写的时候,不能直接使用全局变量,因为是同一个变量会保存其变量状态影响其他使用。最好的处理方式就是尽可能的依据入参,函数内部定义local 变量等来编写代码。
local访问速度更快(原因是local变量是存放在lua的堆栈里面的是array操作,而全局变量是存放在_G中的table中,效率不及堆栈)
封装是可以实现,但
不方便嵌入此代码中,所以我们忽略封装的深层含义,在Lua中,封装就简单理解为将属性和方法封装在一起。
四、UGC平台与Lua开发的结合
1、客户端脚本
客户端程序编写在客户端脚本中,程序功能主要是
提交数据,玩家会通过设备进行输入,比如点击按钮、点击鼠标等,客户端程序获取玩家的输入数据并简单处理后提交给服务器。客户端脚本创建的零件只有本地玩家才可以看见的。
- 以Roblox为例,
LocalScript是用于在连接到 Roblox 服务器的客户端上运行 Lua 代码的 Lua 源容器。
2、服务器脚本
服务器代码编写在服务器脚本中,程序功能主要是
分析处理这些数据,然后把处理结果返回给客户端进行显示。服务器脚本创建的零件是所有玩家都可以看见的。
像UGC游戏开发平台中开发的每个游戏,平台都会分配对应的
服务器容量。
- 以Roblox为例,
Script(脚本)是一种 Lua 代码容器,它的内容可以在服务器上运行。
- 服务器脚本和客户端脚本可以通过
RemoteEvent、事件对象等不同平台上的不同通信机制进行两端的通信
3、模块化开发
在大型项目中的开发中,我们采取分治策略,将大项目分成一个个小模块,小模块间留有接口,整合后组成这个大项目。
在UGC平台开发游戏,模块化主要体现在以下两个方面
- 使用
面向对象编程思想进行工程化开发 - 使用
通用模块进行一个个模块的编写,供客户端或服务器脚本调用【关键词是require,通用模块脚本均可以被客户端和服务器脚本调用】
以Roblox为例,可以ModuleScript 这一Lua源容器,它只会运行一次,并且必定返回相同的一个值。
然后在 ModuleScript 作为唯一参数的情况下,通过调用 require 返回此值。
以博主看法,模块可以分为客户端和服务器两大模块
客户端模块
- 常规UI输入【发送请求】
- 本地场景配置
- 本地存储
- 本地逻辑
服务器脚本
- 常规工具类
- 服务器存储
- Controller转发【接收请求】
- Service业务逻辑
- Mapper数据持久化
五、结语
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////
边栏推荐
- 精选西门子PLC工程实例源码【共300套】
- What is a Test Architect
- tf.count_nonzero
- 阿里云访问资源:NoSuchKey
- Markdown skill tree (8): code blocks
- National Security Agency and CISA kubernetes reinforcement guidelines - new content in version 1.1
- Appium automation test foundation ADB common commands (II)
- uva10891
- KingbaseES 中select distinct on 语句
- Problem solving -- > online OJ (13)
猜你喜欢

Unexpected exception ... code: Badrequest when downloading Xilinx 2018.2

【工控老马】洗衣机PLC程序控制系统设计详解

SQL 注入绕过(六)

Kingbasees v8r6 cluster maintenance case -- single instance data migration to cluster case

1032 Sharing

解题-->在线OJ(十三)

IMX6DL4.1.15支持EIM总线(上)——实际操作,修改内容。

Vibration signal generation and processing based on MATLAB Doppler effect
![[qnx hypervisor 2.2 user manual]6.2.1 communication between guests](/img/3d/6a0cde206a09a7ef03038fb99cf532.png)
[qnx hypervisor 2.2 user manual]6.2.1 communication between guests

【FreeRTOS】中断机制
随机推荐
[translation] E-Cloud. Large scale CDN using kubeedge
cv2.cvtColor
【工控老马】西门子PLC s7-300SCL编程详解
反射修改final
IMX6DL4.1.15支持EIM总线(上)——实际操作,修改内容。
systemd 管理node-exporter
Do you really understand "binder copy once"?
Kingbasees v8r6 cluster maintenance case -- single instance data migration to cluster case
Vibration signal generation and processing based on MATLAB Doppler effect
Markdown skill tree (2): paragraph and emphasis
SYSTEMd management node exporter
How to talk about salary correctly in software test interview?
国家安全局和CISA Kubernetes加固指南--1.1版的新内容
Problem solving -- > online OJ (13)
部署Prometheus-server服务 system管理
软件测试面试如何正确谈论薪资?
【科普资料】从科学精神到科学知识的材料
tf. compat. v1.assign
Final summary spark
KingbaseES V8R6集群维护案例之--单实例数据迁移到集群案例