这篇主要根据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就是在导入一个工具箱。

二、如何创建模块

  1. 基本的模块创建:

    -- 创建一个模块
    local myModule = {}
    
    -- 添加功能到模块
    function myModule.sayHello()
     print("你好!")
    end
    
    -- 返回模块
    return myModule

项目中的例子:

local jass = require 'jass.common'

local multiboard = {}
setmetatable(multiboard, multiboard)

--结构
local mt = {}
multiboard.__index = mt

这段代码展示了如何创建一个多面板模块。

三、如何使用模块

  1. 使用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就是一个包,包含了很多相关的模块。

五、模块的高级用法

  1. 局部模块:
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)

这个例子展示了如何创建一个带有私有成员的模块。

  1. 模块的元表使用:
local item = {}
local mt = {}
ac.item = item

item.__index = mt
setmetatable(mt, skill)

六、实际运用

  1. 游戏系统的模块化:

    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

这段代码展示了如何设置模块的搜索路径。

重要提示:

  1. 模块命名规范:
  2. 使用小写字母
  3. 用点号分隔层级
  4. 避免使用特殊字符
  5. 模块组织原则:
  6. 相关功能放在一起
  7. 一个文件一个模块
  8. 适当的目录结构
  9. 最佳实践:

    -- 推荐的模块定义方式
    local module = {}
    
    -- 私有变量
    local private_var = 0
    
    -- 公共函数
    function module.public_func()
     -- 实现
    end
    
    -- 私有函数
    local function private_func()
     -- 实现
    end
    
    return module
  10. 常见使用场景:
  11. 游戏系统模块化
  12. 功能模块划分
  13. 代码复用

记住:

  1. require只会执行一次
  2. 模块应该返回一个表
  3. 注意避免循环依赖
  4. 合理使用局部变量

垃圾回收

一、什么是垃圾回收?

垃圾回收就像是一个清洁工,负责清理程序中不再使用的内存空间。在这个项目中,我们可以看到一些相关的处理:

-- 当单位被删除时
function mt:remove()
    -- 移除引用,让GC可以回收
    self.owner = nil
    self.handle = nil
    -- 其他清理工作...
end

二、项目中的内存管理例子

  1. 对象的生命周期管理:

    local effect = {}
    
    function effect:remove()
     -- 清理特效
     if self.handle then
         jass.DestroyEffect(self.handle)
         self.handle = nil  -- 解除引用,允许GC回收
     end
    end
  2. 定时器的清理:

    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垃圾回收的基本概念

  1. 引用计数:

    -- 示例代码
    local function createHero()
     local hero = {
         name = "小悟空",
         hp = 1000
     }
     return hero  -- hero的引用计数会增加
    end
    
    local myHero = createHero()  -- myHero引用了这个表
    myHero = nil  -- 解除引用,表可以被回收
  2. 循环引用:

    -- 可能造成内存泄漏的代码
    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

四、内存优化技巧

  1. 重用表:

    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
  2. 及时清理:

    function mt:remove()
     if self.handle then
         -- 清理多面板
         jass.DestroyMultiboard(self.handle)
         self.handle = 0
         -- 清理其他引用
         self.items = nil
     end
    end

五、常见的内存问题

  1. 内存泄漏:

    -- 错误示例
    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
  2. 大量临时对象:

    -- 不推荐的写法
    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

六、项目中的最佳实践

  1. 使用对象池:

    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
  2. 及时移除事件监听:

    hero:event '单位-死亡' (function()
     -- 事件回调中注意清理
     if timer then
         timer:remove()
         timer = nil
     end
    end)

七、重要提示

  1. 基本原则:
  2. 及时解除不需要的引用
  3. 避免创建不必要的临时对象
  4. 使用对象池重用对象
  5. 注意清理事件监听器
  6. 调试技巧:

    -- 开发模式下的内存追踪
    if not base.release then
     -- 启用调试器
     runtime.debugger = 4279
     
     -- 错误处理
     function base.error_handle(msg)
         print("---------------------------------------")
         print(tostring(msg) .. "\n")
         print(debug.traceback())
         print("---------------------------------------")
     end
    end
  7. 性能优化:
  8. 使用local变量(访问更快)
  9. 预分配表的大小(如果知道大小)
  10. 复用对象而不是频繁创建
  11. 常见场景:
  12. 特效系统
  13. 子弹系统
  14. 伤害计算
  15. 技能系统

记住:

  1. 不要过早优化
  2. 先保证代码正确性
  3. 找到真正的性能瓶颈
  4. 合理使用垃圾回收机制

通过合理的内存管理,可以让你的游戏运行得更流畅!