==== 什么是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``` (联机)