这篇主要根据MeoHero项目简单介绍模块与包、垃圾回收的相关知识。
模块与包
一、什么是模块?
模块就像是一个工具箱,里面放着相关的功能。在这个项目中,我们可以看到很多模块:
require 'maps.rule.设置'
require 'maps.rule.player'
require 'maps.rule.rects'
require 'maps.rule.units'
require 'maps.rule.misc'
require 'maps.rule.hero'
require 'maps.rule.attack'
require 'maps.rule.win'
require 'maps.rule.kill_hero'
require 'maps.rule.gold'
require 'maps.rule.home'
require 'maps.rule.tp'
require 'maps.rule.visible'
require 'maps.rule.multiboard'
require 'maps.rule.tips'
require 'maps.rule.sound'
--require 'maps.rule.积分'
require 'maps.rule.新积分'
local jass = require 'jass.common'
jass.SetSkyModel([[Environment\Sky\LordaeronSummerSky\LordaeronSummerSky.mdx]])
这个文件展示了如何使用require来加载不同的模块。每个require就是在导入一个工具箱。
二、如何创建模块
基本的模块创建:
-- 创建一个模块 local myModule = {} -- 添加功能到模块 function myModule.sayHello() print("你好!") end -- 返回模块 return myModule
项目中的例子:
local jass = require 'jass.common'
local multiboard = {}
setmetatable(multiboard, multiboard)
--结构
local mt = {}
multiboard.__index = mt
这段代码展示了如何创建一个多面板模块。
三、如何使用模块
使用require加载模块:
-- 加载模块 local jass = require 'jass.common' local map = require 'maps.map'
项目中的例子:
local player = require 'ac.player'
local map = require 'maps.map'
local fogmodifier = require 'types.fogmodifier'
local hero = require 'types.hero'
local game = require 'types.game'
local jass = require 'jass.common'
local japi = require 'jass.japi'
local slk = require 'jass.slk'
local rect = require 'types.rect'
local multiboard = require 'types.multiboard'
四、包是什么?
包就是一组相关模块的集合,通常放在同一个目录下。看项目结构:
require 'maps.rule.设置'
require 'maps.rule.player'
require 'maps.rule.rects'
require 'maps.rule.units'
require 'maps.rule.misc'
require 'maps.rule.hero'
require 'maps.rule.attack'
require 'maps.rule.win'
require 'maps.rule.kill_hero'
require 'maps.rule.gold'
require 'maps.rule.home'
require 'maps.rule.tp'
require 'maps.rule.visible'
require 'maps.rule.multiboard'
require 'maps.rule.tips'
require 'maps.rule.sound'
--require 'maps.rule.积分'
require 'maps.rule.新积分'
这里的maps.rule就是一个包,包含了很多相关的模块。
五、模块的高级用法
- 局部模块:
local jass = require 'jass.common'
local slk = require 'jass.slk'
local setmetatable = setmetatable
local tostring = tostring
local math = math
local ac_event_dispatch = ac.event_dispatch
local ac_event_notify = ac.event_notify
local table_insert = table.insert
local table_remove = table.remove
local damage = {}
setmetatable(damage, damage)
这个例子展示了如何创建一个带有私有成员的模块。
- 模块的元表使用:
local item = {}
local mt = {}
ac.item = item
item.__index = mt
setmetatable(mt, skill)
六、实际运用
- 游戏系统的模块化:
local rect = require 'types.rect'
local circle = require 'types.circle'
local region = require 'types.region'
local effect = require 'types.effect'
local fogmodifier = require 'types.fogmodifier'
local move = require 'types.move'
local unit = require 'types.unit'
local attribute = require 'types.attribute'
local hero = require 'types.hero'
local damage = require 'types.damage'
local heal = require 'types.heal'
local mover = require 'types.mover'
local follow = require 'types.follow'
local texttag = require 'types.texttag'
local lightning = require 'types.lightning'
local path_block = require 'types.path_block'
local item = require 'types.item'
local game = require 'types.game'
local shop = require 'types.shop'
local sound = require 'types.sound'
local sync = require 'types.sync'
local response = require 'types.response'
local record = require 'types.record'
--初始化
rect.init()
damage.init()
move.init()
unit.init()
hero.init()
effect.init()
mover.init()
follow.init()
lightning.init()
texttag.init()
shop.init()
path_block.init()
game.init()
这段代码展示了如何组织游戏的各个系统模块。
七、模块加载路径
项目中的例子:
--测试版本和发布版本的脚本路径
if base.release then
package.path = package.path .. [[;Poi\]] .. base.version .. [[\?.lua;scripts\?.lua]]
end
这段代码展示了如何设置模块的搜索路径。
重要提示:
- 模块命名规范:
- 使用小写字母
- 用点号分隔层级
- 避免使用特殊字符
- 模块组织原则:
- 相关功能放在一起
- 一个文件一个模块
- 适当的目录结构
最佳实践:
-- 推荐的模块定义方式 local module = {} -- 私有变量 local private_var = 0 -- 公共函数 function module.public_func() -- 实现 end -- 私有函数 local function private_func() -- 实现 end return module
- 常见使用场景:
- 游戏系统模块化
- 功能模块划分
- 代码复用
记住:
- require只会执行一次
- 模块应该返回一个表
- 注意避免循环依赖
- 合理使用局部变量
垃圾回收
一、什么是垃圾回收?
垃圾回收就像是一个清洁工,负责清理程序中不再使用的内存空间。在这个项目中,我们可以看到一些相关的处理:
-- 当单位被删除时
function mt:remove()
-- 移除引用,让GC可以回收
self.owner = nil
self.handle = nil
-- 其他清理工作...
end
二、项目中的内存管理例子
对象的生命周期管理:
local effect = {} function effect:remove() -- 清理特效 if self.handle then jass.DestroyEffect(self.handle) self.handle = nil -- 解除引用,允许GC回收 end end
定时器的清理:
function helper:timer() local count = 0 local t = ac.loop(100, function(t) print(ac.clock()) count = count + 1 if count == 10 then t:remove() -- 及时移除不需要的定时器 end end) end
三、Lua垃圾回收的基本概念
引用计数:
-- 示例代码 local function createHero() local hero = { name = "小悟空", hp = 1000 } return hero -- hero的引用计数会增加 end local myHero = createHero() -- myHero引用了这个表 myHero = nil -- 解除引用,表可以被回收
循环引用:
-- 可能造成内存泄漏的代码 local function createPair() local a = {} local b = {} a.partner = b -- a引用b b.partner = a -- b引用a return a end -- 解决方法:手动解除引用 local pair = createPair() pair.partner.partner = nil -- 打破循环引用 pair = nil
四、内存优化技巧
重用表:
local damage_pool = {} -- 对象池 -- 从对象池获取对象 function damage.create() local dmg = table.remove(damage_pool) or {} -- 初始化属性... return dmg end -- 回收对象到对象池 function damage.recycle(dmg) -- 清理属性 for k in pairs(dmg) do dmg[k] = nil end table.insert(damage_pool, dmg) end
及时清理:
function mt:remove() if self.handle then -- 清理多面板 jass.DestroyMultiboard(self.handle) self.handle = 0 -- 清理其他引用 self.items = nil end end
五、常见的内存问题
内存泄漏:
-- 错误示例 local cache = {} function addToCache(key, value) cache[key] = value -- 没有清理机制,cache会一直增长 end -- 正确示例 function addToCache(key, value) cache[key] = value -- 添加过期机制 ac.wait(3600, function() cache[key] = nil end) end
大量临时对象:
-- 不推荐的写法 function updatePosition() for i = 1, 1000 do local pos = {x = i, y = i} -- 每次循环都创建新表 -- 使用pos... end end -- 推荐的写法 function updatePosition() local pos = {x = 0, y = 0} -- 重用同一个表 for i = 1, 1000 do pos.x = i pos.y = i -- 使用pos... end end
六、项目中的最佳实践
使用对象池:
local mover = {} local mover_pool = {} -- 移动器对象池 function mover.create() -- 从对象池获取或创建新对象 local mvr = table.remove(mover_pool) or {} return mvr end function mover.destroy(mvr) -- 回收到对象池 table.insert(mover_pool, mvr) end
及时移除事件监听:
hero:event '单位-死亡' (function() -- 事件回调中注意清理 if timer then timer:remove() timer = nil end end)
七、重要提示
- 基本原则:
- 及时解除不需要的引用
- 避免创建不必要的临时对象
- 使用对象池重用对象
- 注意清理事件监听器
调试技巧:
-- 开发模式下的内存追踪 if not base.release then -- 启用调试器 runtime.debugger = 4279 -- 错误处理 function base.error_handle(msg) print("---------------------------------------") print(tostring(msg) .. "\n") print(debug.traceback()) print("---------------------------------------") end end
- 性能优化:
- 使用local变量(访问更快)
- 预分配表的大小(如果知道大小)
- 复用对象而不是频繁创建
- 常见场景:
- 特效系统
- 子弹系统
- 伤害计算
- 技能系统
记住:
- 不要过早优化
- 先保证代码正确性
- 找到真正的性能瓶颈
- 合理使用垃圾回收机制
通过合理的内存管理,可以让你的游戏运行得更流畅!