==== 什么是userdata ====
在饥荒中,有一些东西是可以找到代码定义的:
- ```EntityScript:DoTaskInTime()``` 定义在 ```scripts/entityscript.lua```
- ```KnownModIndex:GetModInfo()``` 定义在 ```scripts/modindex.lua```
- ```TheFrontEnd:PushScreen()``` 定义在 ```scripts/frontend.lua```
但是也有一些东西只能找到使用它的地方,而找不到定义位置:
- ```TheNet:IsDedicated()```
- ```TheSim:FindEntities()```
- ```WorldSim:SetWorldSize()```
- ```inst.AnimState:PlayAnimation()```
如果更仔细一点研究,可以发现```TheNet```, ```TheSim```, ```WorldSim``` 这些东西的类型不是table,而是userdata。
print(type(TheNet))
```
userdata
```
饥荒用的代码是c++和Lua,userdata其实就是写在了c++部分,所以看不见定义。
userdata不是表,不能直接用pairs和ipairs遍历。
for k,v in pairs(TheNet)do
print(k,v)
end
```
bad argument #1 to 'pairs' (table expected, got userdata)
```
==== 查看userdata方法 ====
你可以查看userdata的所有函数名字,注意这里使用了元表相关操作:
(不懂元表也没关系,知道代码怎么写就行)
for k,v in pairs(getmetatable(TheNet).__index)do
print(k,v)
end
GetDefaultMaxPlayers function: 0x6000005d88c0
SetIsMatchStarting function: 0x6000005da840
GetServerIsDedicated function: 0x6000005d9c00
PrintNetwork function: 0x6000005df680
SetDefaultMaxPlayers function: 0x6000005d8900
ViewNetFriends function: 0x6000005d9d40
SetDefaultGameMode function: 0x6000005d8540
GetClientMetricsForUser function: 0x6000005d9940
GetServerIsClientHosted function: 0x6000005d9c40
TruncateSnapshotsInClusterSlot function: 0x6000005d9640
SystemMessage function: 0x6000005df740
GetWorldSessionFile function: 0x6000005d9180
SendRPCToClient function: 0x6000005deec0
...(还有一大堆)
虽然能看到函数名字,但是看不了源代码定义,具体用法需要结合函数名和官方使用案例去理解。
==== 修改userdata方法 ====
你可以修改现有的userdata里面的函数,也可以定义一个自己的函数。
函数的第一个参数是userdata自身。
=== 创建新函数 =====
写一个新的函数,叫```TheNet:PrintStatus()```。
getmetatable(TheNet).__index.PrintStatus = function(net)
print("Is Server:", net:GetIsServer())
print("Is Client:", net:GetIsClient())
print("Is Dedicated", net:IsDedicated())
end
TheNet:PrintStatus()
因为我是在游戏主界面运行的,所以结果都是false。
Is Server: false
Is Client: false
Is Dedicated false
=== 修改已有函数 =====
让```TheSim:FindEntities()```函数每次被调用的时候,都打印搜索到的实体数量。
local mt = getmetatable(TheSim).__index
local old_FindEntities = mt.FindEntites
mt.FindEntities = function(sim, ...)
local ents = old_FindEntities(sim, ...)
print("查找到"..#ents.."个实体!")
return ents
end
==== 小技巧 ====
如果要修改AnimState, Physics, Transform 之类的函数,常规操作是获取元表修改:
local inst = CreateEntity()
inst.entity:AddTransform()
inst.entity:AddAnimState()
local mt = getmetatable(inst.AnimState).__index
function mt.PlayAndPush(anim, name1, name2)
anim:PlayAnimation(name1)
anim:PushAnimation(name2)
end
但其实klei已经把这个表放到全局变量里了,你可以直接修改:
function AnimState.PlayAndPush(anim, name1, name2)
anim:PlayAnimation(name1)
anim:PushAnimation(name2)
end
然后就能在其他地方用这个新增的```PlayAndPush```函数了:
local inst = CreateEntity()
inst.entity:AddTransform()
inst.entity:AddAnimState()
inst.AnimState:SetBank("wilson")
inst.AnimState:SetBuild("wilson")
inst.AnimState:PlayAndPush("run_start", "run_loop") ---<
==== 完整事例 ====
上述操作都可以在klei源代码里看到。
- ```scripts/debugsounds.lua```
- ```scripts/postprocesseffects.lua``` (联机)