这篇主要根据MeoHero项目简单介绍Lua函数、运算符的相关知识。

一、Lua函数

1. 函数的基本定义方式

-- 方式1: 最基本的函数定义
local function say_hello()
    print("你好!")
end

-- 方式2: 把函数赋值给变量
local print_damage = function(damage)
    print("造成伤害:" .. damage)
end

-- 方式3: 定义在表中的函数(方法)
local hero = {}
function hero.attack(target)
    print("攻击目标")
end

2. 函数参数

-- 基本参数
local function add_buff(hero, buff_name, duration)
    print(hero:get_name() .. "获得buff:" .. buff_name)
end

-- 可变参数
local function print_skills(hero_name, ...)
    print(hero_name .. "的技能:")
    for _, skill in ipairs({...}) do
        print(skill)
    end
end

-- 带默认值的参数(Lua没有直接语法,需要判断)
local function heal(hero, amount)
    amount = amount or 100  -- 如果amount为nil,就使用100
    hero:add_hp(amount)
end

3. 函数返回值

-- 单个返回值
local function get_hero_name()
    return "小悟空"
end

-- 多个返回值
local function get_hero_info()
    return "小悟空", 1000, 500  -- 返回名字、血量、魔法
end

-- 根据条件返回
local function check_damage(damage)
    if damage <= 0 then
        return false, "伤害无效"
    end
    return true, "造成伤害"
end

项目中的例子:

--判断单位是否存活
function mt:is_alive()
    return self._is_alive
end

--获取单位的所有者
function mt:get_owner()
    return self.owner
end

--获取单位的点
function mt:get_point()
    return self.point or self:get_position()
end

4. 闭包(函数内部定义的函数)

function create_skill_system()
    local skill_points = 0
    
    -- 这是一个闭包,可以访问外部的skill_points
    local function add_skill_point()
        skill_points = skill_points + 1
        print("当前技能点:", skill_points)
    end
    
    return add_skill_point
end

项目中的例子:

function mt:on_cast_channel()
    local hero = self.owner
    
    -- 这是一个闭包函数
    local function update_explosion()
        -- 可以访问外部的hero变量
        if not hero:is_alive() then
            return
        end
        -- 更新爆炸效果
    end
    
    -- 注册更新函数
    ac.loop(30, update_explosion)
end

5. 函数作为参数(回调函数)

-- 注册事件的例子
hero:event('单位-死亡', function()
    print("英雄死亡了!")
end)

-- 定时器的例子
ac.loop(1000, function()
    print("每秒执行一次")
end)

项目中的例子:

--注册英雄
ac.game:event '玩家-注册英雄' (function(self, player, hero)
    --英雄死亡后复活
    hero:event '单位-死亡' (function()
        local lv = hero:get_level()
        local time = math.floor(5 + lv * lv * 0.077)
        player:sendMsg(('你将在 |cffffff00%d|r 秒后复活'):format(time))
    end)
end)

6. 一些实用建议:

  • 函数命名要清晰易懂:

    -- 好的命名
    function hero:add_buff(buff_name)
    function hero:is_alive()
    function hero:get_owner()
    
    -- 不好的命名
    function do_something()
    function abc()
  • 一个函数最好只做一件事:

    -- 好的做法
    function hero:add_hp(amount)
    function hero:add_mp(amount)
    
    -- 不好的做法
    function hero:add_hp_and_mp_and_do_other_things()
  • 适当添加注释:

    --[[
      添加buff到英雄身上
      @param buff_name buff的名字
      @param duration 持续时间
    ]]
    function hero:add_buff(buff_name, duration)
  • 处理错误情况:

    function hero:cast_skill(skill_name)
      -- 检查参数
      if not skill_name then
          return false, "技能名字不能为空"
      end
      
      -- 检查条件
      if not self:is_alive() then
          return false, "英雄已死亡"
      end
      
      -- 执行操作
      return self:do_cast_skill(skill_name)
    end

记住:

  1. 多使用local函数,避免污染全局环境
  2. 函数名要有意义,表达其功能
  3. 参数和返回值要清晰
  4. 适当使用注释说明功能
  5. 处理各种可能的错误情况

二、Lua的运算符

1. 算术运算符

-- 加法 +
local damage = base_damage + bonus_damage  -- 100 + 50 = 150

-- 减法 -
local current_hp = max_hp - damage  -- 1000 - 150 = 850

-- 乘法 *
local crit_damage = damage * 2  -- 150 * 2 = 300

-- 除法 /
local damage_reduce = damage / 2  -- 150 / 2 = 75

-- 取余 %
local gold = 125 % 100  -- 25(余数)

-- 负号 -
local direction = -180  -- 负数

从项目代码可以看到实际运用:

            local player = hero:get_owner()
            local time = math.floor(5 + lv * lv * 0.077)

2. 关系运算符

-- 等于 ==
if hero_level == 6 then
    print("获得大招!")
end

-- 不等于 ~=
if target ~= nil then
    hero:attack(target)
end

-- 大于 >
if damage > 100 then
    print("高伤害!")
end

-- 小于 <
if hp < 100 then
    print("血量危险!")
end

-- 大于等于 >=
if level >= 18 then
    print("满级!")
end

-- 小于等于 <=
if mp <= 0 then
    print("没蓝了!")
end

从项目代码看实际应用:

    if block_chance > 100 then block_chance = 100 end
    if block_rate > 100 then block_rate = 100 end

3. 逻辑运算符

-- 与 and
if hero:is_alive() and hero:has_mana() then
    hero:cast_skill()
end

-- 或 or
local real_damage = damage or 0  -- 如果damage是nil就用0

-- 非 not
if not hero:is_dead() then
    hero:move()
end

从项目代码看实际应用:

        if hero.skill_points <= 0 then
            return
        end
        self.sub_skill:upgrade(1, skill or self)
        hero:addSkillPoint(-1)
        if not self:is_ability_enable() and hero:get_owner() == ac.player.self then
            jass.ForceUICancel()
        end
        if __on_cast_shot then
            __on_cast_shot(self, skill or self)
        end

4. 字符串连接运算符

-- 使用 .. 连接字符串
local name = "小悟空"
local level = 10
local text = name .. "达到" .. level .. "级"  -- "小悟空达到10级"

5. 一些特殊的运算技巧

a) 数学运算:

local sin = math.sin
function math.sin(r)
    return sin(r * rad)
end

--余弦
local cos = math.cos
function math.cos(r)
    return cos(r * rad)
end

--正切
local tan = math.tan
function math.tan(r)
    return tan(r * rad)
end

--反正弦
local asin = math.asin
function math.asin(v)
    return asin(v) * deg
end

--反余弦
local acos = math.acos
function math.acos(v)
    return acos(v) * deg
end

--反正切
local atan = math.atan
function math.atan(v1, v2)
    return atan(v1, v2) * deg
end

这段代码展示了如何处理数学计算。

b) 伤害计算:

function mt:freshDamageInfo()
    local atk = self:get '攻击'
    local pene, pener = self:get '破甲', self:get '穿透'
    local crit, critr = self:get '暴击', self:get '暴击伤害'
    local crit_up = crit * (critr/100 - 1) / 100 + 1
    local damage = atk * crit_up * (self:getDamageRate() / 100.0) * 60
    self:setStr(damage)
    return damage
end

这段代码展示了游戏中的复杂运算。

重要提示:

  1. 运算符优先级:

    -- 优先级从高到低
    -- ^          乘方
    -- not - #    一元运算符
    -- * / %      乘除
    -- + -        加减
    -- ..         字符串连接
    -- < > <= >=  比较
    -- == ~=      相等
    -- and        逻辑与
    -- or         逻辑或
    
    -- 使用括号可以改变优先级
    local result = (1 + 2) * 3  -- 9
    local result2 = 1 + 2 * 3   -- 7
  2. 注意事项:
  3. 除法运算会得到浮点数结果
  4. 字符串连接要注意效率问题
  5. 比较nil值要小心
  6. 逻辑运算要注意短路效果

这些运算符在游戏开发中经常用于:

  • 伤害计算
  • 等级判断
  • 状态检查
  • 文本拼接
  • 坐标计算
  • 时间控制