-- 浅拷贝示例 local shallowCopy = {} for k, v inpairs(original) do shallowCopy[k] = v end
-- 深拷贝实现(考虑循环引用) functiondeepCopy(orig, visited) visited = visited or {} iftype(orig) ~= "table"then return orig end if visited[orig] then return visited[orig] -- 处理循环引用 end local copy = {} visited[orig] = copy setmetatable(copy, deepCopy(getmetatable(orig), visited)) for k, v inpairs(orig) do copy[deepCopy(k, visited)] = deepCopy(v, visited) end return copy end
functionClass(super) local class_type = {} class_type.ctor = false class_type.super = super -- 设置元表,查找链指向父类 class_type.__index = class_type local mt = {} mt.__index = super mt.__call = function(c, ...) local obj = {} setmetatable(obj, c) -- 递归调用父类构造函数 localcreate = function(c, ...) if c.super then create(c.super, ...) end if c.ctor then c.ctor(obj, ...) end end create(c, ...) return obj end setmetatable(class_type, mt) return class_type end
-- 使用示例 BaseEntity = Class() functionBaseEntity:ctor(id) self.id = id self.hp = 100 end
Player = Class(BaseEntity) functionPlayer:ctor(id, name) self.name = name self.mp = 50 end
local p = Player(1, "Hero") print(p.hp, p.mp) -- 输出: 100 50
local ObjectPool = {} local pool = {} -- 分类存储 local activeObjects = {} -- 追踪使用中对象
-- 创建池 functionObjectPool.CreatePool(prefabName, prefab, capacity) pool[prefabName] = { available = {}, prefab = prefab, capacity = capacity or50 } end
-- 获取对象 functionObjectPool.Get(prefabName, parent) local poolData = pool[prefabName] ifnot poolData thenreturnnilend local obj if #poolData.available > 0then obj = table.remove(poolData.available) obj:SetActive(true) else -- 实例化新对象(通过C#) obj = CS.UnityEngine.Object.Instantiate(poolData.prefab) end if parent then obj.transform:SetParent(parent) end activeObjects[obj] = true return obj end
-- 回收对象 functionObjectPool.Recycle(prefabName, obj) local poolData = pool[prefabName] ifnot poolData then CS.UnityEngine.Object.Destroy(obj) return end obj:SetActive(false) obj.transform:SetParent(nil) -- 脱离场景树 if #poolData.available < poolData.capacity then table.insert(poolData.available, obj) activeObjects[obj] = nil else CS.UnityEngine.Object.Destroy(obj) -- 超容销毁 end end
-- 典型闭包:计数器工厂 functionCreateCounter(initial) local count = initial or0-- upvalue returnfunction() count = count + 1 return count end end
local counterA = CreateCounter(0) local counterB = CreateCounter(10) print(counterA()) -- 1 print(counterA()) -- 2 (保持独立状态) print(counterB()) -- 11
-- Unity游戏应用:延时回调带上下文 functionDelayedCallback(delay, callback, context) local startTime = CS.UnityEngine.Time.time-- upvalue捕获 returnfunction() if CS.UnityEngine.Time.time - startTime >= delay then callback(context) returntrue-- 完成标记 end returnfalse end end
-- ipairs只输出索引1,在索引2遇到nil停止 for i, v inipairs(t) doprint(i, v) end-- 输出: 1 a
-- pairs输出全部,但顺序不定 for k, v inpairs(t) doprint(k, v) end-- 可能输出: 1 a, 3 c, name test
-- 自定义迭代器:按值排序遍历 functionSortedPairsByValue(t) local sorted = {} for k inpairs(t) dotable.insert(sorted, k) end table.sort(sorted, function(a, b)return t[a] < t[b] end) local i = 0 returnfunction() i = i + 1 local key = sorted[i] if key ~= nilthen return key, t[key] end end end
-- 使用 for k, v in SortedPairsByValue({hp = 100, mp = 50, atk = 200}) do print(k, v) -- 按值从小到大输出 end
口头表达建议:
“ipairs是专为数组设计的,从1开始步进,碰到nil就停,所以中间有空洞的数组不能用。pairs会遍历所有key,包括字符串key,但Unity配置表里经常需要确定性的遍历顺序,比如按优先级排序的buff列表。这时我会用SortedPairs迭代器,先把keys抽出来排序,再返回闭包。性能上pairs比ipairs略慢,因为要处理哈希部分。实际项目里,网络协议编号我用ipairs保证顺序,配置表ID索引用pairs配合id做key。如果需要反向遍历数组,直接写for i = #t, 1, -1 do,不要用通用的迭代器,避免函数调用开销。”
-- 弱值表:缓存纹理,不阻止资源回收 local textureCache = {} setmetatable(textureCache, {__mode = "v"})
functionLoadTexture(path) if textureCache[path] then return textureCache[path] -- 命中缓存 end local tex = CS.UnityEngine.Resources.Load(path) textureCache[path] = tex return tex end
-- 弱键表:关联对象元数据,不阻止键对象回收 local objectMetadata = {} setmetatable(objectMetadata, {__mode = "k"})
functionSetMeta(obj, data) objectMetadata[obj] = data -- obj为Unity对象 end
functionGetMeta(obj) return objectMetadata[obj] end
-- 实现类1:战士 local Warrior = {} Warrior.__index = Warrior
functionWarrior:New() returnsetmetatable({range = 2}, self) end
functionWarrior:Attack(target) print(" melee attack " .. target.name) end
functionWarrior:GetAttackRange() returnself.range end
-- 实现类2:弓箭手 local Archer = {} Archer.__index = Archer
functionArcher:New() returnsetmetatable({range = 10}, self) end
functionArcher:Attack(target) print(" ranged attack " .. target.name) end
functionArcher:GetAttackRange() returnself.range * 1.5-- 不同实现 end
-- 多态使用 functionPerformAttack(attacker, target) -- 鸭子类型:不检查类型,只检查行为 iftype(attacker.Attack) == "function"and type(attacker.GetAttackRange) == "function"then if Distance(attacker, target) <= attacker:GetAttackRange() then attacker:Attack(target) else MoveToRange(attacker, target) end end end
严格接口检查(可选):
1 2 3 4 5 6 7 8 9
-- 接口验证装饰器 functionImplementInterface(class, interfaceMethods) for _, method inipairs(interfaceMethods) do iftype(class[method]) ~= "function"then error("Class does not implement method: " .. method) end end return class end
“生成代码是XLua能上正式项目的基石。如果不生成,XLua会用反射找方法信息,iOS IL2CPP下会崩溃,而且GC大到不能用。我配置导出规则时遵循最小化原则,只导交互层接口,业务逻辑全放Lua。比如战斗系统只导BattleManager给Lua,具体技能、Buff逻辑Lua实现。生成代码过大影响打包时间,所以用分部编译,把生成代码单独放Assembly。Editor下可以反射调试,真机必须生成。记住改完C#接口要重新生成,否则Lua调用报’attempt to call a nil value’。”
题目14: 什么是LuaJIT?它与标准Lua(PUC-Rio Lua)的区别及适用场景
详细解答:
LuaJIT是Mike Pall开发的Lua高性能实现,核心特性:
JIT编译:将热点字节码即时编译为机器码,效率接近C
FFI(Foreign Function Interface):直接调用C函数,无需绑定层
内存限制:使用32位指针(GC64模式除外),单进程内存受限
1 2 3 4 5 6
-- LuaJIT FFI示例(应用层较少直接使用,但了解其威力) local ffi = require("ffi") ffi.cdef[[ int printf(const char *, ...); ]] ffi.C.printf("Hello from %s\n", "FFI")
-- 方案1:按需分表加载(省内存) local ConfigMgr = {} local loadedTables = {}
functionConfigMgr.Load(tableName) ifnot loadedTables[tableName] then localpath = string.format("Config/%s.lua", tableName) local chunk = assert(loadfile(path)) loadedTables[tableName] = chunk() end return loadedTables[tableName] end
-- 方案3:共享元表(大量重复默认配置) local DefaultSkill = { cd = 1.0, range = 5, cost = 10 } local SkillConfig = {}
functionLoadSkillConfig(id) local raw = RawLoad(id) -- 从文件读取原始差异数据 ifnot raw thenreturnnilend -- 使用变更表+默认原型的方式 local skill = {} setmetatable(skill, {__index = DefaultSkill}) for k, v inpairs(raw) do skill[k] = v -- 只覆盖差异字段 end return skill end
local EventBus = {} local listeners = {} -- [eventType] = { [listener] = callback, ... } local toRemove = {} -- 延迟删除队列 local isDispatching = false
functionEventBus.AddListener(eventType, listener, callback) local eventListeners = listeners[eventType] ifnot eventListeners then eventListeners = {} listeners[eventType] = eventListeners end -- 检查重复注册 if eventListeners[listener] then Debug.LogWarning("重复注册事件: " .. eventType) return end eventListeners[listener] = callback end
functionEventBus.RemoveListener(eventType, listener) local eventListeners = listeners[eventType] ifnot eventListeners thenreturnend if isDispatching then -- 派发中先标记,避免遍历中修改表 toRemove[eventType] = toRemove[eventType] or {} toRemove[eventType][listener] = true else eventListeners[listener] = nil ifnext(eventListeners) == nilthen listeners[eventType] = nil-- 清理空表 end end end
functionEventBus.Dispatch(eventType, ...) local eventListeners = listeners[eventType] ifnot eventListeners thenreturnend isDispatching = true for listener, callback inpairs(eventListeners) do ifnot (toRemove[eventType] and toRemove[eventType][listener]) then local ok, err = pcall(callback, ...) ifnot ok then Debug.LogError(string.format("事件%s处理错误: %s", eventType, err)) end end end isDispatching = false -- 执行延迟删除 if toRemove[eventType] then for listener, _ inpairs(toRemove[eventType]) do eventListeners[listener] = nil end toRemove[eventType] = nil ifnext(eventListeners) == nilthen listeners[eventType] = nil end end end
-- 带过期时间的事件(定时任务) functionEventBus.Schedule(eventType, delay, ...) local args = {...} local co = coroutine.create(function() coroutine.yield(CS.UnityEngine.WaitForSeconds(delay)) EventBus.Dispatch(eventType, table.unpack(args)) end) -- 启动协程... end
-- 自定义搜索器,支持按命名空间加载 local pathCache = {} package.searchers[2] = function(modName) localpath = modName:gsub("%.", "/") .. ".lua" pathCache[modName] = path returnloadfile(path) end
口头表达建议:
“Lua模块本质是返回table的脚本,关键是要避免全局污染。我定下的规范是:每个文件local M = {}开头,return M结尾,绝不用全局变量。项目结构按ECS思路分Core、Gameplay、UI三层。Core提供最基础的事件、定时器、对象池,这层稳定后不轻易改。Gameplay里战斗、技能、AI各自独立模块,互相通信只用EventBus。UI层用MVP模式,Panel脚本只处理显示,逻辑在Presenter里。模块加载用自定义searcher,支持’Gameplay.Combat.Skill’这样的点分路径,自动映射文件目录。循环引用用接口回调或事件解耦,不用require循环依赖。代码审查必查有没有遗漏的local,全局变量是Lua项目崩溃的隐形炸弹。”
functionReload(moduleName) -- 清空package.loaded缓存 package.loaded[moduleName] = nil local newModule = require(moduleName) -- 迁移旧模块的实例状态到新模式(如果需要) local oldModule = Persistent.modules[moduleName] if oldModule and oldModule._instances then newModule._instances = oldModule._instances end Persistent.modules[moduleName] = newModule return newModule end
-- 方案2:使用函数转发(保持接口稳定) local RealImpl = require("CurrentImpl")
-- Unity项目实战:C#回调封装 functionSafeCallback(callback, ...) iftype(callback) ~= "function"thenreturnend local args = {...} local ok, err = pcall(function() callback(table.unpack(args)) end) ifnot ok then CS.UnityEngine.Debug.LogError("Lua callback error: " .. tostring(err)) -- 上报到异常监控系统 ReportErrorToServer(err) end end
-- 模块级错误隔离 functionLoadModuleSafe(moduleName) local ok, module = pcall(require, moduleName) ifnot ok then CS.UnityEngine.Debug.LogError("Failed to load "..moduleName..": "..module) returnnil end returnmodule end
functionTimerMgr.Update(deltaTime) if paused thenreturnend for i = #timers, 1, -1do-- 逆序遍历安全删除 local timer = timers[i] ifnot timer.isPaused andnot timer.isDestroyed then timer.lifeTime = timer.lifeTime - deltaTime if timer.lifeTime <= 0then -- 执行回调 local ok, err = pcall(timer.callback, timer.context, timer) ifnot ok then Debug.LogError("Timer error: " .. err) timer.isDestroyed = true end if timer.repeatCount > 0then timer.repeatCount = timer.repeatCount - 1 end if timer.repeatCount == 0then timer.isDestroyed = true else timer.lifeTime = timer.lifeTime + timer.interval -- 重置 end end end if timer.isDestroyed then table.remove(timers, i) end end end
functionOuter() local x = 10-- 这是upvalue local y = 20-- 也是upvalue functionInner1() print(x) -- 引用upvalue x x = x + 1-- 修改会影响Inner2看到的值 end functionInner2() print(x) -- 与Inner1共享同一个x upvalue end return Inner1, Inner2 end
-- 危险:闭包持有Unity对象引用 functionCreateListener(go, callback) returnfunction() -- go是upvalue,即使此闭包很少调用,go也不能GC if go then callback(go) end end end
-- 安全:使用弱引用或ID索引 functionCreateListenerSafe(goId, callback) returnfunction() local go = GetGameObject(goId) -- 运行时查询 if go then callback(go) end end end
local ConfigVersion = { current = 3, migrations = { [1] = function(data) -- v1 -> v2: 重命名字段 if data.oldName then data.newName = data.oldName data.oldName = nil end return data end, [2] = function(data) -- v2 -> v3: 添加默认值 data.newField = data.newField or0 if data.items then for _, item inipairs(data.items) do item.quality = item.quality or1-- 新增品质字段 end end return data end } }
functionLoadSavedData(rawData) local version = rawData._version or1 local data = rawData -- 逐级迁移 for v = version, ConfigVersion.current - 1do local migrate = ConfigVersion.migrations[v] if migrate then data = migrate(data) print(string.format("Migrated data from v%d to v%d", v, v+1)) end end data._version = ConfigVersion.current return data end
functionValidateAndFix(data, schema) -- 补充缺失字段 for k, v inpairs(schema.defaults) do if data[k] == nilthen data[k] = v end end -- 类型修正 for k, expectedType inpairs(schema.types) do if data[k] ~= nilandtype(data[k]) ~= expectedType then -- 尝试转换或设为默认值 data[k] = schema.defaults[k] end end return data end
-- 二叉堆实现的优先队列(比table.insert排序高效) local BinaryHeap = {} functionBinaryHeap:new() return { items = {}, scores = {} } end
functionBinaryHeap:push(item, score) table.insert(self.items, item) table.insert(self.scores, score) self:siftUp(#self.items) end
functionBinaryHeap:pop() if #self.items == 0thenreturnnilend local result = self.items[1] self.items[1] = self.items[#self.items] self.scores[1] = self.scores[#self.scores] table.remove(self.items) table.remove(self.scores) self:siftDown(1) return result end
functionAStar.FindPath(grid, startNode, endNode) local openSet = BinaryHeap:new() -- 带fScore优先级的开放列表 local closedSet = {} -- 关闭列表:node -> true local gScore = {} -- 实际代价:node -> cost local cameFrom = {} -- 路径记录 openSet:push(startNode, 0) gScore[startNode] = 0 while #openSet.items > 0do local current = openSet:pop() if current == endNode then return ReconstructPath(cameFrom, current) end closedSet[current] = true for _, neighbor inipairs(grid:GetNeighbors(current)) do if closedSet[neighbor] or neighbor.isBlocked then goto continue end local tentativeG = gScore[current] + grid:Distance(current, neighbor) if tentativeG < (gScore[neighbor] or INF) then cameFrom[neighbor] = current gScore[neighbor] = tentativeG local fScore = tentativeG + grid:Heuristic(neighbor, endNode) openSet:push(neighbor, fScore) end ::continue:: end end returnnil-- 无路径 end
-- 路径回溯 functionReconstructPath(cameFrom, current) localpath = {current} while cameFrom[current] do current = cameFrom[current] table.insert(path, 1, current) -- 头部插入 end returnpath end
-- 打印详细对象结构 functionDeepToString(obj, indent) indent = indent or0 local prefix = string.rep(" ", indent) iftype(obj) ~= "table"then returntostring(obj) end local mt = getmetatable(obj) if mt and mt.__tostringand indent == 0then -- 顶层使用自定义格式 return mt.__tostring(obj) end local parts = {} table.insert(parts, "{\n") for k, v inpairs(obj) do local line = string.format("%s %s = %s,\n", prefix, tostring(k), DeepToString(v, indent + 1)) table.insert(parts, line) end table.insert(parts, prefix .. "}") returntable.concat(parts) end
-- 与其他语言不同 if0thenprint("0 is true") end-- 输出 if""thenprint("empty string is true") end-- 输出 ifnilthenelseprint("nil is false") end-- 输出 iffalsethenelseprint("false is false") end-- 输出
-- 游戏开发应用:配置合法性判断 functionUseSkill(id) localconfig = GetConfig(id) ifnotconfigthen-- 明确区分'无配置'和'配置为0' print("Skill not exists") return end -- 即使配置伤害为0,也合法执行 DealDamage(config.damage or0) -- 区分nil和0 end
-- 习惯性写法:显式比较 -- 坏:if value then (当value=0时逻辑错误) -- 好: if value ~= niland value > 0then -- 明确检查非nil且为正 end
-- 空字符串处理 if str and str ~= ""then -- 既非nil也非空串 end
口头表达建议:
“这是Lua区别于C/C++的重要特性,0和空串都是true。好处是区分了’未设置’和’值为零’。比如技能伤害配成0是正常,配成nil是配表错误。条件判断时,检查配置存在性用if config then,检查数值有效性用if value and value > 0。不要依赖隐式转换,永远显式比较。团队代码审查的一个重点就是防止if player.hp then这种代码,应该写成if player.hp > 0。还有空字符串拼接前检查,免得UI上显示’nil’字符串。这个特性让Lua的数据表达更精确,但需要团队统一编码规范防止踩坑。”
local IDGenerator = { current = 0, recycled = {}, -- 回收的ID复用 inUse = {} -- 追踪使用中ID(调试用) }
functionIDGenerator:Acquire() local id if #self.recycled > 0then id = table.remove(self.recycled) else self.current = self.current + 1 id = self.current end self.inUse[id] = true return id end
functionIDGenerator:Release(id) ifnotself.inUse[id] then error("Double release ID: " .. id) end self.inUse[id] = nil table.insert(self.recycled, id) end
functionIDGenerator:SafeTransaction(callback) -- 保存状态,出错回滚 local state = { current = self.current, recycledCount = #self.recycled } local ok, result = pcall(callback, self) ifnot ok then -- 回滚(简化示例,实际需深拷贝) self.current = state.current -- 清空并恢复recycled... error("Transaction failed: " .. result) end return result end
并发模拟(多协程):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
-- 确保在yield点数据一致性 functionCriticalSection(coFunc) local lock = false-- 简单锁 returnfunction(...) while lock do coroutine.yield() -- 等待解锁 end lock = true local result = {coFunc(...)} lock = false returntable.unpack(result) end end
-- 基础用法 functionLog(level, ...) local args = {...} -- 转换为table local message = table.concat(args, " ") print(string.format("[%s] %s", level, message)) end
Log("INFO", "Player", 101, "connected")
-- 转发参数(保持性能) functionEventTrigger(event, ...) local handlers = GetHandlers(event) for _, handler inipairs(handlers) do handler(event, ...) -- 直接转发,不构造table end end
-- 边界情况:nil参数 functionTestNil(...) local t = {...} -- [1]=1, [2]=nil, [3]=3,但#t可能为1(Lua 5.1) print(#t) -- 不确定! -- 安全做法:使用select local count = select('#', ...) -- 获取实际参数个数(含nil) for i = 1, count do local v = select(i, ...) -- 获取第i个参数 print(i, v) end end
-- 1. 全局表监控 functionMemoryTracker.CheckGlobals() local leaked = {} for k, v inpairs(_G) do iftype(v) == "table"and k ~= "package"and k ~= "_G"then -- 检查是否为用户模块 ifnotpackage.loaded[k] then leaked[k] = v end end end return leaked end
-- 2. 对象引用追踪(弱引用监控) local trackedObjects = setmetatable({}, {__mode = "k"})
functionMemoryTracker.Track(obj, tag) trackedObjects[obj] = { tag = tag, time = os.time(), traceback = debug.traceback() } end
functionMemoryTracker.Report() print("=== 可能泄漏的对象 ===") for obj, info inpairs(trackedObjects) do print(string.format("Tag: %s, Age: %ds", info.tag, os.time() - info.time)) print("Created at:", info.traceback) end end
-- 3. 自动快照对比 functionMemoryTracker.Snapshot() local count = collectgarbage("count") local objs = {} -- 遍历所有table(简化示例,实际用debug API) return { memory = count, objects = objs } end
functionMemoryTracker.Compare(old, new) print(string.format("内存变化: %.2f KB", new.memory - old.memory)) end
-- 使用示例 functionTestScene() local before = MemoryTracker.Snapshot() LoadScene("Battle") -- 战斗后返回 UnloadScene() collectgarbage("collect") local after = MemoryTracker.Snapshot() MemoryTracker.Compare(before, after) end
local StateMachine = {} StateMachine.__index = StateMachine
functionStateMachine:New() returnsetmetatable({ states = {}, current = nil, previous = nil, transitions = {} -- 转移条件表 }, self) end
functionStateMachine:AddState(name, state) state.name = name self.states[name] = state end
functionStateMachine:ChangeState(newStateName, ...) local newState = self.states[newStateName] ifnot newState then error("State not found: " .. newStateName) end ifself.current andself.current.OnExit then self.current:OnExit(newState) end self.previous = self.current self.current = newState if newState.OnEnter then newState:OnEnter(self.previous, ...) end end
functionStateMachine:Update(dt) ifself.current andself.current.OnUpdate then self.current:OnUpdate(dt) end -- 检查自动转移条件 ifself.transitions[self.current.name] then for _, trans inipairs(self.transitions[self.current.name]) do if trans.condition(self) then self:ChangeState(trans.to) break end end end end
-- 使用示例:敌人AI local EnemyAI = StateMachine:New()
-- 定义状态 EnemyAI:AddState("Idle", { OnEnter = function(self)print("开始待机") end, OnUpdate = function(self, dt) if PlayerNearby() then EnemyAI:ChangeState("Chase") end end })
EnemyAI:AddState("Chase", { OnEnter = function(self)print("开始追击") end, OnUpdate = function(self, dt) MoveTo(PlayerPos()) ifnot PlayerNearby() then EnemyAI:ChangeState("Idle") end end, OnExit = function(self)print("停止追击") end })
-- 普通递归(非尾调用) functionFactorialBad(n) if n <= 1thenreturn1end return n * FactorialBad(n - 1) -- 求值后还要乘法,不是尾调用 end
-- 尾递归优化版本 functionFactorialGood(n, acc) acc = acc or1 if n <= 1thenreturn acc end return FactorialGood(n - 1, n * acc) -- 纯调用,无后续操作 end
-- 游戏应用:状态机跳转(避免栈溢出) functionProcessState(stateStack) if #stateStack == 0thenreturnend local state = table.remove(stateStack) if state == "combat"then -- 处理 combat... -- 尾调用跳转到下一状态,不增加栈深 return ProcessState(stateStack) -- 尾调用! elseif state == "loot"then -- 处理 loot... return ProcessState(stateStack) end end
-- 组件表:按类型存储,数组结构保证缓存友好 local components = { Transform = {}, -- [entityId] = {x, y, z} Health = {}, -- [entityId] = {current, max} AI = {} -- [entityId] = {state, targetId} }
-- Entity生成 local nextId = 0 functionECS.CreateEntity() nextId = nextId + 1 return nextId end
functionECS.AddComponent(entityId, compType, data) ifnot components[compType] then components[compType] = {} end components[compType][entityId] = data end
functionECS.RemoveComponent(entityId, compType) if components[compType] then components[compType][entityId] = nil end end
-- System基类 local System = {} System.__index = System
functionSystem:New() returnsetmetatable({}, self) end
functionSystem:Update(dt)end-- 子类覆盖
-- 具体System:移动系统 local MoveSystem = setmetatable({}, {__index = System})
functionMoveSystem:Update(dt) local transforms = components.Transform local ais = components.AI for entityId, ai inpairs(ais) do local trans = transforms[entityId] if trans and ai.targetPos then -- 简单移向目标 local dx = ai.targetPos.x - trans.x local dz = ai.targetPos.z - trans.z trans.x = trans.x + dx * dt * 2 trans.z = trans.z + dz * dt * 2 end end end
-- 注册和驱动 local systems = {} functionECS.RegisterSystem(sys) table.insert(systems, sys) end
ECS.RegisterSystem(MoveSystem:New())
functionECS.Update(dt) for _, sys inipairs(systems) do sys:Update(dt) end end
-- 获取局部变量(调试器用) local i = 1 whiletruedo local name, value = debug.getlocal(2, i) -- 层级2的第i个局部变量 ifnot name thenbreakend print(name, value) i = i + 1 end
-- 设置钩子(性能分析) local counter = {} debug.sethook(function(event) local info = debug.getinfo(2, "nS") if info.name then counter[info.name] = (counter[info.name] or0) + 1 end end, "c", 100) -- 每100条指令触发
-- 发布版移除策略 -- 1. 编译时剥离(修改luaconf.h或XLua配置) -- 2. 运行时替换为空表 if IS_RELEASE then debug = { traceback = function(msg)return msg end-- 只保留基本错误信息 } end
functionCommandManager.Execute(cmd) local ok, err = pcall(cmd.execute, cmd.context) ifnot ok then print("Command failed:", err) returnfalse end table.insert(CommandManager.undoStack, cmd) -- 清空redo栈(新分支) CommandManager.redoStack = {} -- 限制历史长度 if #CommandManager.undoStack > CommandManager.maxHistory then table.remove(CommandManager.undoStack, 1) end returntrue end
functionCommandManager.Undo() if #CommandManager.undoStack == 0thenreturnend local cmd = table.remove(CommandManager.undoStack) pcall(cmd.undo, cmd.context) table.insert(CommandManager.redoStack, cmd) end
functionCommandManager.Redo() if #CommandManager.redoStack == 0thenreturnend local cmd = table.remove(CommandManager.redoStack) pcall(cmd.execute, cmd.context) table.insert(CommandManager.undoStack, cmd) end
-- 基础序列化(支持循环引用) functionSerializer.Serialize(obj, visited) visited = visited or {} if visited[obj] thenreturn'"<circular>"'end local t = type(obj) if t == "nil"thenreturn"nil" elseif t == "boolean"thenreturntostring(obj) elseif t == "number"then -- 处理NaN和Inf if obj ~= obj thenreturn"0/0"end if obj == math.hugethenreturn"1/0"end if obj == -math.hugethenreturn"-1/0"end returntostring(obj) elseif t == "string"then returnstring.format("%q", obj) -- 安全引号 elseif t == "table"then visited[obj] = true local parts = {"{"} for k, v inpairs(obj) do table.insert(parts, "[" .. Serializer.Serialize(k, visited) .. "]=") table.insert(parts, Serializer.Serialize(v, visited) .. ",") end table.insert(parts, "}") returntable.concat(parts) else error("Cannot serialize type: " .. t) end end
-- 反序列化(使用loadstring,注意安全) functionSerializer.Deserialize(str) -- 沙箱环境 local env = {math = math} local func, err = load("return " .. str, "deserialize", "t", env) ifnot func thenerror(err) end return func() end
functionScheduler.Spawn(func) local co = coroutine.create(func) table.insert(Scheduler.tasks, { coro = co, resumeAt = 0, -- 立即执行 status = "ready" }) return #Scheduler.tasks -- taskId end
functionScheduler.Wait(seconds) local task = Scheduler.GetCurrentTask() task.resumeAt = Scheduler.GetTime() + seconds task.status = "sleeping" coroutine.yield() end
functionScheduler.WaitUntil(condition) local task = Scheduler.GetCurrentTask() task.waitCondition = condition task.status = "waiting" coroutine.yield() end
functionScheduler.Update() local now = Scheduler.GetTime() local i = 1 while i <= #Scheduler.tasks do local task = Scheduler.tasks[i] if task.status == "sleeping"and now >= task.resumeAt then task.status = "ready" elseif task.status == "waiting"and task.waitCondition() then task.status = "ready" end if task.status == "ready"then Scheduler.currentTask = task local ok, err = coroutine.resume(task.coro) ifnot ok then print("Task error:", err) table.remove(Scheduler.tasks, i) i = i - 1 elseifcoroutine.status(task.coro) == "dead"then table.remove(Scheduler.tasks, i) i = i - 1 else -- 协程让出,可能改变了status i = i + 1 end else i = i + 1 end end end
-- 模拟table.pack(兼容5.1) functiontable.pack(...) return { n = select("#", ...), ... } end
-- 使用:捕获nil参数 localfunctionSafeVarargs(...) local args = table.pack(...) for i = 1, args.n do if args[i] == nilthen print(string.format("Arg %d is nil", i)) end end return args end
-- unpack使用:数组转参数列表 local arr = {1, 2, 3, nil, 5} local a, b, c, d, e = table.unpack(arr, 1, table.maxn(arr)) -- Lua 5.1用maxn print(a, b, c, d, e) -- 1 2 3 nil 5
-- 游戏应用:配置参数透传 functionCreateSkill(config, ...) local params = table.pack(...) -- params.n告知实际参数个数,即使中间有nil return Skill:New(config, params) end
functionAOP.Before(func, aspect) returnfunction(...) aspect(...) -- 前置逻辑 return func(...) end end
functionAOP.After(func, aspect) returnfunction(...) local result = {func(...)} aspect(...) returntable.unpack(result) end end
functionAOP.Around(func, aspect) returnfunction(...) return aspect(func, ...) end end
-- 游戏应用:性能监控切面 functionProfileAspect(func, ...) local start = os.clock() local result = {func(...)} local elapsed = os.clock() - start if elapsed > 0.016then-- 超过一帧 print(string.format("Slow function: %.4fs", elapsed)) print(debug.traceback()) end returntable.unpack(result) end
-- 应用切面到类方法 functionApplyProfiling(class) for name, method inpairs(class) do iftype(method) == "function"then class[name] = AOP.Around(method, ProfileAspect) end end end
-- 使用示例 local BattleCalc = {} functionBattleCalc:Damage(atk, def) -- 复杂计算 return atk * 2 - def end
-- 注册工厂 functionContainer.BindFactory(interface, factoryFunc) Container.factories[interface] = factoryFunc end
-- 解析依赖 functionContainer.Resolve(interface) local binding = Container.bindings[interface] ifnot binding then error("No binding for: " .. tostring(interface)) end if binding.singleton then ifnot Container.singletons[interface] then Container.singletons[interface] = binding.impl:New() end return Container.singletons[interface] else return binding.impl:New() end end
-- 字段注入 functionContainer.Inject(obj) for k, v inpairs(obj) do iftype(v) == "string"and v:sub(1, 1) == "@"then local interface = v:sub(2) obj[k] = Container.Resolve(interface) end end end
functionTestFramework.Describe(suiteName, fn) TestFramework.currentSuite = suiteName fn() TestFramework.currentSuite = nil end
functionTestFramework.It(testName, fn) local fullName = (TestFramework.currentSuite or"") .. " > " .. testName table.insert(TestFramework.tests, { name = fullName, fn = fn }) end
functionTestFramework.Expect(value) return { toBe = function(expected) if value ~= expected then error(string.format("Expected %s, got %s", tostring(expected), tostring(value)), 2) end end, toBeNear = function(expected, epsilon) ifmath.abs(value - expected) > epsilon then error(string.format("Expected %s near %s", value, expected)) end end, toThrow = function() local ok = pcall(value) if ok thenerror("Expected function to throw") end end } end
-- 1. 所有变量声明local,避免全局污染 localfunctionProcessData(data)-- 连函数也local local result = {} -- ... return result end
-- 2. 缓存频繁访问的C#对象和函数 local Time = CS.UnityEngine.Time local GetDeltaTime = Time.deltaTime -- 不行,这是属性,每次访问桥接 -- 正确做法:每帧缓存 local deltaTime functionUpdate() deltaTime = Time.deltaTime -- 使用deltaTime end
-- 3. 表预分配,避免自动扩容 local list = {} for i = 1, 1000do list[i] = i -- 预分配行为,优于table.insert end
-- 4. 使用对象池复用table local pool = {} functionGetTable() returntable.remove(pool) or {} end functionRecycleTable(t) for k inpairs(t) do t[k] = nilend-- 清理 table.insert(pool, t) end