这个分类目录主要分享博主DarkSheep在魔兽地图编辑器的学思笔记、经验和技巧。
魔兽地图编辑器Lua作图教程5：X-LIK框架④物体编辑：技能
一、学习案例（一）TPL模版.md中的案例这是一个技能模板的链式调用定义，创建了一个名为"技能例子"的无目标技能，并设置了冷却时间、魔法消耗等属性，还定义了技能生效时触发的回调函数。技能模板定义部分TPL_ABILITY.DEMO = AbilityTpl() :name("技能例子") // 设置技能名称 :targetType(ability.targetType.none) // 设置目标类型为无目标 :icon("black") // 设置技能图标 :coolDownAdv(10, 0) // 设置基础冷却时间10秒，成长值0 :mpCostAdv(100, 0) // 设置基础魔法消耗100，成长值0 :onEvent(EVENT.Ability.Effective, // 绑定技能生效事件 function(effectiveData) echo("技能释放", effectiveData.triggerUnit:owner()) // 事件触发时打印信息 end)技能添加到单位部分local slot = (whichUnit):abilitySlot() // 获取单位的技能栏 // 三种添加技能的方式: slot:push(Ability(TPL_ABILITY.DEMO)) // 方式1: 通过Ability对象添加到最后可用格子 slot:push(TPL_ABILITY.DEMO) // 方式2: 直接通过模板添加到最后一个可用格子 slot:push(TPL_ABILITY.DEMO, 3) // 方式3: 添加到指定序号(3)的格子这部分展示了三种将技能添加到单位技能栏的方法，可以灵活地通过技能对象或直接通过模板添加，还能指定添加到特定格子位置。整个代码段展示了TPL系统的两个核心功能：通过模板定义可复用的技能配置灵活地将技能应用到单位上（二）demo.lua中的案例这个例子位于projects\mylua\scripts\globals\tpl\ability\demo.lua-- 技能模板定义 - Ability Template Definition ---@param abData eventOnAbilityGet 技能获取事件数据 TPL_ABILITY.DEMO = AbilityTpl() -- 创建新的技能模板 :name("剑之勇气") -- 技能名称 :description( -- 技能描述 { "越战越勇，每减少10%的HP则会增加10点攻击力", -- 基础效果说明 "效果最多叠加9层，最大90点攻击力", -- 效果上限说明 }) :targetType(ability.targetType.pas) -- 目标类型：被动技能 :icon("ReplaceableTextures\\CommandButtons\\BTNArcaniteMelee.blp") -- 技能图标路径 :onEvent(eventKind.abilityGet, -- 技能获取事件 function(abData) local ak = abData.triggerAbility:id() -- 获取技能唯一ID ---@param evtData eventOnClassAfterChange HP变化事件数据 event.syncRegister(abData.triggerUnit, eventKind.classAfterChange .. "hpCur", ak, function(evtData) local u = evtData.triggerUnit -- 获取触发单位 BuffClear(u, { key = "剑之勇气" .. ak }) -- 清除旧buff local cur = evtData.new -- 当前HP值 if (cur > 0) then -- 检查单位是否存活 local hp = u:hp() -- 最大HP值 local n = math.floor((hp - cur) / hp * 10) -- 计算buff层数(每损失10%HP为1层) if (n > 0) then -- 检查是否需要应用buff local atk = 10 * n -- 计算攻击力加成(每层+10) Buff({ -- 创建buff效果 object = u, -- 作用目标 key = "剑之勇气" .. ak, -- buff唯一标识 name = "剑之勇气" .. n .. "层", -- buff显示名称 signal = buffSignal.up, -- 增益型buff icon = "ReplaceableTextures\\CommandButtons\\BTNArcaniteMelee.blp", -- buff图标 text = colour.hex(colour.gold, n), -- 显示文本(金色层数) description = { -- buff详细描述 colour.hex(colour.gold, n .. "层") .. "剑之勇气", -- 层数显示 colour.hex(colour.lawngreen, "攻击：+" .. atk), -- 攻击加成显示 }, ---@param buffObj Unit purpose = function(buffObj) -- buff生效逻辑 buffObj:attack("+=" .. atk) -- 增加攻击力 effector.attach(buffObj, "SmallBuildingFire0", "origin") -- 附加特效 end, ---@param buffObj Unit rollback = function(buffObj) -- buff失效逻辑 buffObj:attack("-=" .. atk) -- 移除攻击加成 effector.detach(buffObj, "SmallBuildingFire0", "origin") -- 移除特效 end }) end end end) end) :onEvent(eventKind.abilityLose, -- 技能失去事件 function(abData) local ak = abData.triggerAbility:id() -- 获取技能ID event.syncUnregister(abData.triggerUnit, eventKind.classAfterChange .. "hpCur", ak) -- 取消事件监听 BuffClear(abData.triggerUnit, { key = "剑之勇气" .. ak }) -- 清除buff（三）demo2.lua中的案例这个例子位于projects\mylua\scripts\globals\tpl\ability\demo2.lua-- 技能模板定义 - 盾之结晶 ---@param abData eventOnAbilityGet 技能获取事件数据 TPL_ABILITY.DEMO2 = AbilityTpl() -- 创建新的技能模板 :name("盾之结晶") -- 技能名称 :description( -- 技能描述 { "越战越勇，每减少10%的HP则会增加1点防御力", -- 基础效果说明 "效果最多叠加9层，最大9点防御力", -- 效果上限说明 }) :targetType(ability.targetType.pas) -- 目标类型：被动技能 :icon("ReplaceableTextures\\CommandButtons\\BTNAdvancedUnholyArmor.blp") -- 技能图标路径(高级邪恶护甲) :onEvent(eventKind.abilityGet, -- 技能获取事件 function(abData) local ak = abData.triggerAbility:id() -- 获取技能唯一ID ---@param evtData eventOnClassAfterChange HP变化事件数据 event.syncRegister(abData.triggerUnit, eventKind.classAfterChange .. "hpCur", ak, function(evtData) local u = evtData.triggerUnit -- 获取触发单位 BuffClear(u, { key = "盾之结晶" .. ak }) -- 清除旧buff local cur = evtData.new -- 当前HP值 if (cur > 0) then -- 检查单位是否存活 local hp = u:hp() -- 最大HP值 local n = math.floor((hp - cur) / hp * 10) -- 计算buff层数(每损失10%HP为1层) if (n > 0) then -- 检查是否需要应用buff local def = n -- 计算防御力加成(每层+1) Buff({ -- 创建buff效果 object = u, -- 作用目标 key = "盾之结晶" .. ak, -- buff唯一标识 name = "盾之结晶" .. n .. "层", -- buff显示名称 signal = buffSignal.up, -- 增益型buff icon = "ReplaceableTextures\\CommandButtons\\BTNAdvancedUnholyArmor.blp", -- buff图标 text = colour.hex(colour.gold, n), -- 显示文本(金色层数) description = { -- buff详细描述 colour.hex(colour.gold, n .. "层") .. "盾之结晶", -- 层数显示 colour.hex(colour.lawngreen, "防御：+" .. def), -- 防御加成显示 }, ---@param buffObj Unit purpose = function(buffObj) -- buff生效逻辑 buffObj:defend("+=" .. def) -- 增加防御力 effector.attach(buffObj, "AIdaTarget", "overhead") -- 附加头顶特效 end, ---@param buffObj Unit rollback = function(buffObj) -- buff失效逻辑 buffObj:defend("-=" .. def) -- 移除防御加成 effector.detach(buffObj, "AIdaTarget", "overhead") -- 移除头顶特效 end }) end end end) end) :onEvent(eventKind.abilityLose, -- 技能失去事件 function(abData) local ak = abData.triggerAbility:id() -- 获取技能ID event.syncUnregister(abData.triggerUnit, eventKind.classAfterChange .. "hpCur", ak) -- 取消事件监听 BuffClear(abData.triggerUnit, { key = "盾之结晶" .. ak }) -- 清除buff end)整体结构：两个文件都使用AbilityTpl()创建技能模板都设置了技能名称(name)、描述(description)、目标类型(targetType)和图标(icon)都实现了两个事件处理：abilityGet: 当单位获得该技能时触发abilityLose: 当单位失去该技能时触发事件处理机制：在abilityGet事件中，都注册了一个监听单位HP变化的同步事件(event.syncRegister)当HP变化时，会根据当前HP百分比计算buff层数都使用Buff系统来管理技能效果在abilityLose事件中，都会取消事件监听并清除buff技能效果对比：demo.lua - "剑之勇气":效果：每减少10%HP增加10点攻击力，最多叠加9层(共90点攻击力)视觉：附加"SmallBuildingFire0"特效到单位原点(origin)文本：显示当前层数和增加的攻击力数值demo2.lua - "盾之结晶":效果：每减少10%HP增加1点防御力，最多叠加9层(共9点防御力)视觉：附加"AIdaTarget"特效到单位头顶(overhead)文本：显示当前层数和增加的防御力数值二、学习输出（一）War3原生技能图标数据表NameArtUbertip收费ReplaceableTextures\CommandButtons\BTNReturnGoods.blp向某个玩家收取固定数额的金子和木材。命令光环ReplaceableTextures\PassiveButtons\PASBTNGnollCommandAura.blp增加附近单位的攻击力。操纵死尸ReplaceableTextures\CommandButtons\BTNAnimateDead.blp复活<ACad,DataA1>个阵亡单位。持续时间为<ACad,Dur1>秒。荆棘光环ReplaceableTextures\PassiveButtons\PASBTNThorns.blp给周围单位提供荆棘光环的保护，如果近战型的敌人来攻击它们就会受到每次相当于自身<ACah,DataA1,%>%攻击力的伤害。反魔法外壳ReplaceableTextures\CommandButtons\BTNAntiMagicShell.blp使目标单位对所有魔法免疫。n持续<ACam,Dur1>秒。强击光环ReplaceableTextures\PassiveButtons\PASBTNTrueShot.blp提高周围友军单位<ACat,DataA1,%>%的远程攻击力。专注光环ReplaceableTextures\PassiveButtons\PASBTNDevotion.blp提高周围单位<ACav,DataA1>点的护甲。辉煌光环ReplaceableTextures\PassiveButtons\PASBTNBrilliance.blp增加周围单位每秒<ACba,DataA1>点的魔法恢复速度。嗜血术ReplaceableTextures\CommandButtons\BTNBloodLustOn.blp在<ACbb,Dur1>秒内增加目标单位<ACbb,DataA1,%>%的攻击速度和<ACbb,DataB1,%>%的移动速度。火焰呼吸ReplaceableTextures\CommandButtons\BTNFireForTheCannon.blp对敌人呼出带有攻击力的火焰，造成<ACbc,DataA1>点伤害力。（二）ItemTpl物品模板属性表分类属性/方法参数类型可选值/范围示例说明基础属性levelMaxnumber1-game.expParams.ability.maxLv技能最大等级 levelnumber1-levelMax当前技能等级 expnumber0-game.abilityExpNeeds(maxLv)当前经验值 levelUpNeedPointnumber≥0升级所需技能点目标设置targetTypestringability.target中的类型技能目标类型 targetFilterfunction(self:Ability,targetUnit:Unit):boolean目标过滤函数动画设置spellAnimationstring动画名称施法动作 castKeepAnimationstring动画名称持续施法动作以上2张表格资源回复可见下载地址：[hide]已经和单位、物品整合到一起了~https://gitee.com/china_darksheep/xlik-learn[/hide]
魔兽地图编辑器Lua作图教程5：X-LIK框架③物体编辑：物品
一、学习案例（一）TPL模版.md中的案例TPL_ITEM.DEMO = ItemTpl() :modelAlias("TreasureChest") -- 宝箱模型 :name("物品例子") :bindAbility(TPL_ABILITY.DEMO) :icon("black") :worth({ gold = 10 }) :onEvent(EVENT.Item.Get, function(getData) echo("获得物品", getData.triggerUnit:owner()) end)ItemTpl() 调用：调用library/class/vast/itemTpl.lua中的构造函数。创建一个新的物品模板实例。返回的对象支持链式调用:modelAlias("TreasureChest")设置模型别名，内部会转换为实际模型路径。对应itemTpl.lua中的model()方法。:name("物品例子")设置物品名称，继承自Tpl基类的方法。:bindAbility(TPL_ABILITY.DEMO)绑定技能模板。对应itemTpl.lua中的bindAbilityTpl()方法。TPL_ABILITY.DEMO是另一个技能模板。:icon("black")设置物品图标。继承自Tpl基类的方法。:worth({ gold = 10 })设置物品价值，继承自Tpl基类的方法。这里设置金币价值为10:onEvent(EVENT.Item.Get, function(getData) echo("获得物品", getData.triggerUnit:owner()) end)注册物品获取事件处理器。-- 对应itemTpl.lua中的onEvent()方法。当单位获得该物品时触发回调。getData包含事件相关信息：triggerUnit: 触发事件的单位，owner(): 获取单位所属玩家。（二）demo.lua中的案例这个案例位于：projects\mylua\scripts\globals\tpl\item\demo.luaTPL_ITEM.DEMO = ItemTpl() --:model("???") -- 默认就是物品宝箱 :name("勇气之剑") :description("就是一把剑") :bindAbilityTpl(TPL_ABILITY.DEMO) :icon("ReplaceableTextures\\CommandButtons\\BTNArcaniteMelee.blp") :worth({ gold = 10 }) :onEvent(eventKind.itemGet, function(getData) echo(getData.triggerUnit:name() .. " 获得物品 " .. getData.triggerItem:name()) end)这个例子和TPL模版.md中的案例几乎一样，只是多了：:description("就是一把剑") --物品描述:icon("ReplaceableTextures\CommandButtons\BTNArcaniteMelee.blp") --使用了具体的图标路径，即War3内置的图标资源。:bindAbilityTpl(TPL_ABILITY.DEMO) --对应itemTpl.lua中的方法名变更。功能相同，都是绑定技能模板。二、学习输出（一）War3原生物品图标数据表物品ID物品名字物品图标afac阿利亚之笛ReplaceableTextures\CommandButtons\BTNAlleriaFlute.blpajen古之忍耐姜歌ReplaceableTextures\CommandButtons\BTNJanggo.blpamrc召唤护身符ReplaceableTextures\CommandButtons\BTNAmulet.blpanfg远古雕像ReplaceableTextures\CommandButtons\BTNClayFigurine.blpankh重生十字章ReplaceableTextures\CommandButtons\BTNAnkh.blparsc神秘卷轴ReplaceableTextures\CommandButtons\BTNBansheeAdept.blp（二）ItemTpl物品模板属性表分类方法参数类型可选值/范围示例说明等级经验expnumbernil≥0，不超过最大经验值exp(100)设置/获取物品当前经验值，自动限制范围 levelMaxnumbernil1 ≤ value ≤ game.expParams.item.maxLvlevelMax(5)设置/获取物品最大等级，不超过系统配置最大值 levelnumbernil1 ≤ value ≤ _levelMaxlevel(3)设置/获取物品当前等级，自动限制在1和_maxLevel之间技能绑定bindAbilityTplAbilityTplnil有效的AbilityTpl对象bindAbilityTpl(TPL_ABILITY.DEMO)绑定技能模板，实例化物品时会转为具体技能模型显示modelstringnil合法模型路径或别名model("war3mapModel\sword.mdl")设置/获取模型路径，支持别名系统 modelScalenumbernil>0的缩放系数modelScale(1.2)设置/获取模型缩放比例 animateScalenumbernil>0的动画速度系数animateScale(0.8)设置/获取动画播放速度物品属性autoUsebooleanniltrue/falseautoUse(true)设置/获取是否捡取后自动使用 consumablebooleanniltrue/falseconsumable(true)设置/获取是否为消耗品(次数用完自动销毁) pawnablebooleanniltrue/falsepawnable(false)设置/获取是否可出售 dropablebooleanniltrue/falsedropable(false)设置/获取是否可丢弃 chargesnumbernil≥0的整数charges(3)设置/获取使用次数(0表示无限)以上2张表格资源回复可见下载地址：[hide]已经和单位整合到一起了~https://gitee.com/china_darksheep/xlik-learn[/hide]
魔兽地图编辑器Lua作图教程5：X-LIK框架②物体编辑：单位
在《魔兽地图编辑器Lua作图教程5：X-LIK框架①概述》我们已经通过输入.\xlik.exe new mylua新建了一个lua项目。它位于：projects\mylua文件夹下。B站火魂侠大佬是从防守演示图开始学习之旅。那么我作为一个萌新，之前最熟悉的还是WE地图编辑器了，所以另辟蹊径，从物体编辑开始记录我的学习情况。一、前言通过看火魂侠大佬的视频，以及阅读框架自带的TPL模版.md文档，我们很容易发现X-LIK框架物编使用的TPL，即template，意为模板、标准。技能、物品、单位都可以基于Tpl来构建。与传统物编对比特性传统物编TPL系统修改方式需要导出导入代码实时修改数据绑定松散关联强类型关联事件系统有限触发条件完整生命周期事件动态调整需要重启地图运行时立即生效二、学习案例（一）TPL模版.md中的案例TPL_UNIT.Demo = UnitTpl("Footman") -- 此处的Footman指引用语音，默认为nil/空字符串 -- 基础属性 :name("步兵") -- string 单位名称 :model("TheCaptain") -- string 单位模型路径 :icon("unit/TheCaptain") -- string 单位图标路径 :scale(1.2) -- number 模型缩放比例(1.0为原始大小) :preAbility() -- 预置技能 -- 等级系统 :level(1) -- number 初始等级(1-100) -- 战斗系统 :hp(100) -- number 最大生命值 :hpRegen(0) -- number 生命恢复/秒 :mp(100) -- number 最大魔法值 :mpRegen(5) -- number 魔法恢复/秒 :move(300) -- number 移动速度(游戏标准单位) :material(UNIT_MATERIAL.metal) -- enum 材质类型：flesh(血肉), rock(石材), metal(金属), wood(木质) :weaponSound("metal_slice_medium") -- string 武器音效路径 -- 攻击系统 :attack(10) -- number 基础攻击力 :attackSpaceBase(1.0) -- number 攻击间隔(秒) :attackRange(100) -- number 攻击范围(像素) :assault(Assault({ -- table 高级攻击配置 mode = "missile", -- 攻击类型： -- "melee": 近战 -- "missile": 投射物 -- "artillery": 抛物线投射物 model = "FaerieDragonMissile", -- string 投射物模型 homing = true, -- boolean 是否追踪目标 height = 200, -- number 投射物飞行高度 speed = 1200 -- number 投射物速度(像素/秒) }))（二）demo.lua中的案例这个案例位于：projects\mylua\scripts\globals\tpl\unit\demo.luaTPL_UNIT.DEMO = UnitTpl("Footman", "avatar") :preAbility({ TPL_ABILITY.DEMO2 }) :preItem({ TPL_ITEM.DEMO }) :name("步兵") :model("Footman") :weaponSound("metal_slice_medium") :move(200) :attack(10) :hp(150) :hpRegen(3) :reborn(3)UnitTpl("Footman", "avatar"): 创建一个新的单位模板，语音类型为"Footman"，图标为"avatar"。:preAbility({ TPL_ABILITY.DEMO2 }): 为该单位预设技能，这里是TPL_ABILITY.DEMO2。:preItem({ TPL_ITEM.DEMO }): 为该单位预设物品，这里是TPL_ITEM.DEMO。:name("步兵"): 设置单位的名称为"步兵"。:model("Footman"): 设置单位的模型为"Footman"。:weaponSound("metal_slice_medium"): 设置单位武器的声音为"metal_slice_medium"。:move(200): 设置单位的移动速度为200。:attack(10): 设置单位的攻击力为10。:hp(150): 设置单位的生命值为150。:hpRegen(3): 设置单位每秒生命值恢复3点。:reborn(3): 设置单位重生时间为3秒（指单位被击败后重新出现的时间）。三、学习输出（一）War3原生物编数据表:model需要填模型文件名，:icon需要填图标路径。可以从WE里查看，也可以用mpqeditor打开mpq查找。但是我嫌麻烦，所以直接整理了一张《War3原生物编数据表.xlsx》。部分内容如下：物编ID名字模型文件图标 - 游戏界面IDNamefileArtEcen半神人units\nightelf\HeroKeeperoftheGrove\HeroKeeperoftheGroveReplaceableTextures\CommandButtons\BTNKeeperOfTheGrove.blpEdem恶魔猎手units\nightelf\HeroDemonHunter\HeroDemonHunterReplaceableTextures\CommandButtons\BTNHeroDemonHunter.blpEdmm恶魔猎手units\nightelf\HeroDemonHunter\HeroDemonHunterReplaceableTextures\CommandButtons\BTNMetamorphosis.blpEevi恶魔猎手units\nightelf\EvilIllidan\IllidanEvilReplaceableTextures\CommandButtons\BTNEvilIllidan.blpEevm恶魔猎手units\nightelf\EvilIllidan\IllidanEvilReplaceableTextures\CommandButtons\BTNMetamorphosis.blpEfur丛林守护者units\nightelf\Furion\FurionReplaceableTextures\CommandButtons\BTNFurion.blpEkgg丛林守护者units\nightelf\HeroKeeperoftheGroveGhost\HeroKeeperoftheGroveGhostReplaceableTextures\CommandButtons\BTNKeeperGhostBlue.blpEmfr丛林守护者units\nightelf\MalFurion\MalFurionReplaceableTextures\CommandButtons\BTNFurion.blp（二）UnitTpl单位模板属性表当前xlik框架没有代码补全提示，所以我们还需要一张UnitTpl的属性枚举表，即除了案例中的:hp、:move、:level等属性外，还有哪些属性能用。我又整理了一张《UnitTpl单位模板属性表.xlsx》，部分内容如下：分类方法参数类型可选值/范围示例说明文本属性:name()string任意字符串:name("精英步兵")单位显示名称 :properName()string任意字符串:properName("队长")单位头衔/称谓等级属性:level()number1-100:level(5)当前等级 :levelMax()number1-100:levelMax(10)可达到的最大等级状态属性:hp()number1-999999:hp(5000)最大生命值 :hpRegen()number0-1000:hpRegen(15)生命恢复/秒 :mp()number0-9999:mp(200)魔法值上限 :mpRegen()number0-100:mpRegen(2)魔法恢复/秒战斗属性:move()number0-522:move(350)移动速度 :attack()number1-99999:attack(150)基础攻击力 :attackPoint()number0.0-1.5:attackPoint(0.75)攻击动作触发点 :defend()number0-99:defend(5)护甲值以上2张表格资源回复可见下载地址：[hide]https://gitee.com/china_darksheep/xlik-learn[/hide]四、实战环节demo里是小兵大战，大家在实战环节可以做英雄大战。在位于projects\mylua\scripts\globals\tpl\unit的目录下新建hero.lua的文件--- 阿尔萨斯 TPL_UNIT.As = UnitTpl("HeroDeathKnight", "HeroDeathKnight") :model("HeroDeathKnight") :name("阿尔萨斯") :level(5) :levelMax(20) :hp(230) :hpRegen(3) :mp(150) :mpRegen(0.5) :move(300) :attack(20) :defend(30)在位于projects\mylua\scripts\process\test.lua的目录下修改test.lua的文件 --- 双英对打 local bubble = self:bubble() bubble.eff = effector.agile("Echo", 0, 0, 0) effector.rotateZ(bubble.eff, 270) bubble.unit = Unit(Player(1), TPL_UNIT.DEMO, 0, -100, 270) bubble.unit2 = Unit(Player(2), TPL_UNIT.DEMO2, 0, -100, 270) --- 新增英雄对打 local bubble1 = self:bubble() bubble1.unit = Unit(Player(1), TPL_UNIT.As, 0, 200, 270)控制台输入.\xlik.exe run mylua测试项目，进入游戏后可以看到我们创建的死亡骑士单位了。五、遇到的问题1、使用UnitTpl模板创建的单位默认是带有6格物品栏的，那么能不能创建1个带有3个物品栏的单位呢？2、虽然我对死亡骑士设置了:level(5)，但是进入游戏后似乎并没有生效，仍然显示level 1，是什么原因？3、案例里的:icon()说是单位图标，是哪里的显示图标呢？4、使用UnitTpl模板创建英雄模型（单位），却没有F1头像，是不是相当于一个披着英雄模型的普通单位？
魔兽地图编辑器Lua作图教程5：X-LIK框架①概述
一、什么是X-LIK框架X-LIK 是来自阿魂小站的一个魔兽争霸3地图开发框架，可在 windows 平台上运行。它提供一套完整的War3地图开发框架(xlik-sdk)，通过声明式配置简化地图开发流程，支持资源管理和自动化构建，实现核心功能与项目特定功能的解耦。{span class="text-red-600"}最宝贵的是这个框架项目里提供了很多markdown文档和演示地图，无疑是黑夜里的一盏明灯。{/span}当然也有视频资源：bilibili火魂侠的X-LIK系列教程二、核心功能资源管理：支持多种游戏资源(音乐、字体、图片、模型等)提供资源测试和验证机制自动资源加载和路径管理脚本系统：无require的自动加载机制全局定义和流程代码分离支持脚本执行顺序控制扩展架构：主核心库(library)提供基础功能项目子库(project/library)支持定制扩展模块化设计(UI、技能、类系统等)三、框架获取点击跳转以下地址，可以通过Git clone获取，也可以直接下载zip文件解压后使用https://gitee.com/xlik-tutorial/xlik-sdk四、使用前的准备工作1、安装Visual Studio Code，并安装官网推荐插件，如Emmylua、Color Highlight等小知识：Emmylua插件装完以后，代码文件上会有很多红色波浪线。看着很不舒服。这是因为Emmylua更新后增加了很多代码诊断的功能。所以，可以在项目根目录创建一个名字为.emmyrc.json的文件来屏蔽，文件内容如下：{ "diagnostics": { "disable": [ "undefined-global" ] ｝ }2、双击xlik.exe会生成env.yaml文件3、配置env.yaml文件中的魔兽争霸3客户端文件路径。这里要注意路径里不使用\，而是使用/。如：# 魔兽争霸3客户端文件路径 # Warcraft 3 client file path war3: "F:/Warcraft III Frozen Throne"4、修改env.yaml文件中assets资源目录路径。这个文件夹目前位于vendor文件夹下。所以路径要改成：# assets资源目录 # assets resource directory assets: "vendor/assets"5、使用VS code打开xlik-sdk的项目文件夹五、X-LIK框架的学习之旅（一）熟悉终端命令1、新建lua项目点击VS code菜单终端——新建终端——输入：.\xlik.exe new mylua。注意这里mylua可以自定义名称。恭喜你！你已经新建了一张空白地图，你可以做左侧资源管理器里的project目录下看到mylua的文件夹。2、使用we编辑地图信息继续在终端里输入.\xlik.exe we mylua，可以打开we编辑器对地图进行编辑。这里的we编辑器使用的是vendor目录的WE编辑器。建议不要折腾怎么换编辑器，直接用完事。3、使用run运行地图继续在终端里输入.\xlik.exe run mylua，可以打开魔兽测试地图。注意，如果输入命令后，魔兽窗口一闪而过，多半是魔兽客户端有问题，推荐重下载阿魂小站推荐的纯净版。（二）下载演示项目一共有四个演示项目，其中Gitee有3个，分别是xlik-defence（防守）、xlik-tower（塔防）、xlik-demo（tpl和process演示），Github有1个，demo-retro（SLK演示）。火魂侠目前在B站有详细视频教程，这里就不赘述了。（三）熟悉框架结构框架里已经有了详细md介绍。这里主要想说的是，一般不改动library核心文件夹里的代码。主要的增删改在projects项目文件夹。└── project_demo - 项目目录 ├── assets - 项目资源引入, 必须写在此名字目录下，不要把流程代码写里面 ├── slk - slk物编配置引入, 必须写在此名字目录下，不要把流程代码写里面 ├── scripts - 项目脚本代码，必须写在此名字目录下 │ ├── globals - 全局定义（仅供参考） │ │ ├── setup - 用于定义游戏设定（仅供参考） │ │ ├── tpl - 用于建立对象模版（仅供参考） │ └── process - 项目流程代码，流程以 start 开始 ├── library -（·）局部子核心库（只编写对核心库的自定义拓展，文件对应结构须与全局library一致） │ └── encrypt.yaml -（·）混淆规则 └── w3x - 地图文件（没事别乱改，使用we命令修改参数，保存后自动生成备份） ├── resource - 地图独立资源（原始资源载入方式） ├── map - 地图lni（不要轻易改，除非你懂） ├── table - ini式的物编（不要改，不搭理物编） └── war3mapMap.blp - 略缩图（不用改）
魔兽地图编辑器Lua作图教程4：雪月Lua框架⑥触发器（Trigger）模块
这个小节主要介绍下雪月Lua框架xLua.module.Trigger 文件夹xLua.module.Trigger 文件夹提供了与触发器（Trigger）相关的功能，包括创建、操作和管理触发器。1. 触发器的创建与注册创建触发器触发器可以通过 trigger:new() 方法创建。每个触发器可以包含多个事件和动作。local trigger = require "xLua.module.Trigger" -- 创建一个新的触发器 local myTrigger = trigger:new()注册事件触发器需要注册事件来监听特定的游戏事件。例如，单位死亡、玩家按下某个键等。-- 注册一个事件到触发器 myTrigger:registerEvent("单位-死亡", function(event, params) print("单位死亡事件被触发") end)2. 添加动作添加动作到触发器触发器可以在事件触发时执行一系列动作。这些动作可以是简单的函数调用，也可以是复杂的逻辑处理。-- 添加动作到触发器 myTrigger:addAction(function(event, params) print("执行触发器的动作") end)3. 启用和禁用触发器启用触发器触发器默认是启用状态，但可以通过 enable() 方法显式启用。-- 启用触发器 myTrigger:enable()禁用触发器有时需要临时禁用触发器，以防止其响应事件。-- 禁用触发器 myTrigger:disable()4. 销毁触发器销毁触发器当不再需要触发器时，可以通过 destroy() 方法销毁它，释放相关资源。-- 销毁触发器 myTrigger:destroy()5. 示例以下是一个简单的示例，展示了如何创建、添加动作触发器。 ocal function say_hello() print("你好!") end local trg2 = trigger:new() trg2:action(say_hello())
魔兽地图编辑器Lua作图教程4：雪月Lua框架⑪热更新
雪月Lua框架的热更新非常简单，只需要调用就行了。代码解释local storm = require 'jass.storm' --- 热更新.注意别死循环。 ---@param Entry string 入口文件(require用的路径) 一般是 Main function hotFix( Entry ) package.loaded[ Entry ] = nil xpcall(require, function (msg) print(msg, debug.traceback()) end, Entry) end引入 jass.storm 模块：local storm = require 'jass.storm'这行代码引入了 jass.storm 模块。jass.storm 是魔兽争霸III（Warcraft III）中用于JASS（Jass Ascension Scripting System）的Lua绑定库。它提供了与JASS交互的功能。定义 hotFix 函数：--- 热更新.注意别死循环。 ---@param Entry string 入口文件(require用的路径) 一般是 Main function hotFix( Entry )hotFix 是一个函数，用于执行热更新操作。Entry 是一个字符串参数，表示要加载的入口文件的路径（通常是 Main 或其他模块的路径）。清除已加载的模块：package.loaded[ Entry ] = nilpackage.loaded 是一个表，存储了所有已加载的模块。通过将 package.loaded[ Entry ] 设置为 nil，可以清除之前加载的模块，以便重新加载最新的版本。使用 xpcall 加载模块：xpcall(require, function (msg) print(msg, debug.traceback()) end, Entry)xpcall 是一个保护调用函数，用于捕获和处理可能发生的错误。require 是Lua的标准函数，用于加载和返回模块。function (msg) 是一个错误处理函数，当 require 执行过程中发生错误时会被调用。msg 是错误信息。debug.traceback() 返回调用堆栈信息，有助于调试。如何使用假设你有一个脚本 热更新.lua，并且你希望在游戏运行时动态更新这个脚本。以下是使用 hotFix 函数的步骤：创建脚本： 在 scripts 文件夹中创建一个 热更新.lua 文件，内容如下：-- 热更新.lua print("热更新.lua 加载成功！") -- 其他逻辑代码调用 hotFix 函数： 在你的入口脚本main.lua中调用 hotFix 函数，以加载和更新 热更新.lua。-- main.lua -- 引入热更新脚本 require 'xLua.Main' --载入框架 -- 可以用一个玩家输入事件来执行热更新 local trg1 = cj.CreateTrigger() cj.TriggerRegisterPlayerChatEvent(trg1, cj.Player(0), "100", true) cj.TriggerAddAction(trg1, function () hotFix('热更新') cj.DisplayTimedTextToPlayer(cj.Player(0),0,0,30,"热更新成功") end) -- 其他触发器逻辑 -- ...修改并测试：启动魔兽争霸III并加载你的地图。修改 热更新.lua 文件，例如添加新的打印语句：-- 热更新.lua print("热更新.lua 加载成功！") print("这是新的打印语句！") -- 其他逻辑代码保存 热更新.lua 文件。观察游戏中的输出，确保新的打印语句被正确加载。注意事项路径问题：确保 Entry 参数的路径正确，并且脚本文件在游戏运行时可访问。路径通常是相对于 scripts 文件夹的相对路径。避免死循环：确保 hotFix 函数不会被无限递归调用，否则会导致死循环。错误处理：在实际应用中，添加更多的错误处理逻辑，以确保热更新过程中的稳定性。
魔兽地图编辑器Lua作图教程4：雪月Lua框架⑨窗口、键盘、鼠标、UI模块
这个小节主要介绍下雪月Lua框架xLua\module\Window、xLua\module\Keyboard 和 xLua\module\Mouse 这三个文件夹内与窗口、键盘和鼠标相关的方法。1. 窗口模块文件路径: xLua\module\Window\init.lua描述:xLua\module\Window 模块提供了创建和管理窗口的功能。主要方法:window:new(params):描述: 创建一个新的窗口对象。参数:params (table): 包含创建窗口所需参数的表，如 x, y, w, h, title 等。x (number): 窗口左上角的X坐标。y (number): 窗口左上角的Y坐标。w (number): 窗口的宽度。h (number): 窗口的高度。title (string, 可选): 窗口的标题。返回值: 返回一个窗口对象。示例:local myWindow = window:new({ x = 0.1, y = 0.1, w = 0.3, h = 0.3, title = "My Window" })2. 键盘模块文件路径: xLua\module\Keyboard\default.lua描述:xLua\module\Keyboard 模块提供了注册和处理键盘事件的功能。主要方法:keyboard:hotkey(eventNmae, combKey, key, callback):描述: 注册一个热键事件。参数:eventNmae (string): 事件名称，如 "键盘-按下" 或 "键盘-松开"。combKey (int): 组合键，可以多个组合键相加，如 xconst.keyboard.COMB_CTRL。key (int): 主要按键，如 xconst.keyboard.KEY_F5。callback (function): 回调函数，接收两个参数：event 和 params。返回值: 返回事件对象。示例:local event_f5 = keyboard:hotkey('键盘-按下', 0, xconst.keyboard.KEY_F5, function(event, params) print("F5 键被按下") end)3. 鼠标模块文件路径: xLua\module\Mouse\init.lua描述:xLua\module\Mouse 模块提供了注册和处理鼠标事件的功能。主要方法:mouse:event(eventName, callback):描述: 注册一个鼠标事件。参数:eventName (string): 事件名称，如 "鼠标-按下"、"鼠标-松开"、"鼠标-点击"、"鼠标-双击"、"鼠标-移动"、"鼠标-滚轮"。callback (function): 回调函数，接收两个参数：event 和 params。返回值: 返回事件对象。示例:local event_mouse_click = mouse:event('鼠标-点击', function(event, params) print("鼠标点击位置:", params.x, params.y) end)综合示例下面是一个综合示例，展示了如何使用 window、keyboard 和 mouse 模块来创建窗口、注册键盘热键和鼠标事件。-- 引入必要的模块 require 'xLua.module.Window' require 'xLua.module.Keyboard' require 'xLua.module.Mouse' -- 创建一个窗口 local myWindow = window:new({ x = 0.1, y = 0.1, w = 0.3, h = 0.3, title = "My Window" }) -- 注册一个键盘热键 (F5) local event_f5 = keyboard:hotkey('键盘-按下', 0, xconst.keyboard.KEY_F5, function(event, params) print("F5 键被按下") -- 在窗口中显示消息 myWindow:msg("F5 键被按下") end) -- 注册一个鼠标点击事件 local event_mouse_click = mouse:event('鼠标-点击', function(event, params) print("鼠标点击位置:", params.x, params.y) -- 在窗口中显示消息 myWindow:msg("鼠标点击位置: " .. params.x .. ", " .. params.y) end) -- 注册一个鼠标移动事件 local event_mouse_move = mouse:event('鼠标-移动', function(event, params) -- 打印鼠标移动位置 print("鼠标移动位置:", params.x, params.y) end)注意事项常量使用: 确保你使用了正确的常量，如 xconst.keyboard.KEY_F5 和 xconst.keyboard.COMB_CTRL。事件回调: 回调函数中的 params 参数包含了事件的详细信息，如按键码、鼠标位置等。窗口方法: 窗口对象 myWindow 可能包含其他方法，如 msg 用于在窗口中显示消息。UI模块xLua.module.ui 文件夹包含了一系列用于创建和管理用户界面（UI）元素的模块。这些模块提供了丰富的功能，可以用来构建复杂的用户界面，包括按钮、文本框、图标、面板等。主要模块base.lua: UI 基类，所有 UI 元素都继承自这个基类。img.lua: 图像元素。text.lua: 文本元素。icon.lua: 图标元素。slot.lua: 插槽元素。button.lua: 按钮元素。radiobox.lua: 单选框元素。checkbox.lua: 复选框元素。verticalscrollbar.lua: 垂直滚动条元素。panel.lua: 面板元素。window.lua: 窗口元素。tooltip.lua: 提示框元素。game: 游戏特定的 UI 元素。event.lua: UI 事件处理。使用案例下面是一个具体的案例，展示了如何使用 xLua.module.ui 模块创建一个简单的用户界面，包括一个窗口、一个按钮和一个文本框。案例：创建一个包含按钮和文本框的窗口-- 引入必要的模块 local ui = require "xLua.module.ui" local xui = require "xui" -- 创建一个新的窗口 local myWindow = xui.window:new{ title = "示例窗口", width = 0.3, height = 0.2, x = 0.35, y = 0.35, visible = true, } -- 创建一个文本框 local myText = xui.text:new{ parent = myWindow, text = "点击按钮更新文本", x = 0.05, y = 0.4, width = 0.9, height = 0.2, align = "center", } -- 创建一个按钮 local myButton = xui.button:new{ parent = myWindow, value = "点击我", x = 0.35, y = 0.1, width = 0.3, height = 0.2, on_click = function(self, key) -- 更新文本框内容 myText.text = "按钮已点击！" print("按钮已点击！") end, } -- 显示窗口 myWindow:show()
魔兽地图编辑器Lua作图教程4：雪月Lua框架⑩事件、事件池模块
分析 xLua.module.Event 文件夹内的方法xLua.module.Event 模块主要用于处理事件（Event）系统。事件系统在游戏开发中非常常见，用于处理各种游戏事件，如玩家输入、单位事件、触发器事件等。以下是 xLua.module.Event 模块的主要方法及其使用说明：主要方法reg(event_name, params)功能: 注册一个事件。参数:event_name (string): 事件名称。params (table): 包含以下字段的表：type (string): 事件类型，例如 'unit'、'player' 等。unit (unit, 可选): 单位对象（如果事件类型是单位事件）。player (player, 可选): 玩家对象（如果事件类型是玩家事件）。返回值: 无。reReg(event_name, params)功能: 重新注册一个事件。参数:event_name (string): 事件名称。params (table, 可选): 重新注册事件时的参数。返回值: 无。notify(event_name, action, params)功能: 通知事件池中的事件。参数:event_name (string): 事件名称。action (string): 操作类型，例如 '注册事件'、'重新注册事件' 等。params (table): 包含事件参数的表。返回值: 无。update(event_name, params)功能: 更新事件池中的事件。参数:event_name (string): 事件名称。params (table): 包含事件参数的表。返回值: 无。使用案例下面是一个具体的案例，展示了如何使用 xLua.module.Event 模块注册、更新和处理事件。案例：注册和处理玩家事件-- 引入必要的模块 local eventpool = require "xLua.module.EventPool" local playerModule = require "xLua.module.Player" local eventModule = require "xLua.module.Event" -- 获取玩家对象（假设玩家1） local player1 = playerModule[1] -- 定义一个事件处理函数 local function onPlayerChat(event, params) print("玩家 " .. params.player.id .. " 发送了消息: " .. params.message) end -- 注册玩家聊天事件 eventModule.reg('玩家-聊天消息', { type = 'player', player = player1, on_update = onPlayerChat }) -- 模拟玩家发送聊天消息 local function simulatePlayerChat(player, message) eventpool:update('玩家-聊天消息', { player = player, message = message }) end -- 模拟玩家1发送聊天消息 simulatePlayerChat(player1, "Hello, World!")扩展案例：注册和处理单位事件下面是一个更复杂的案例，展示了如何使用 xLua.module.Event 模块注册、更新和处理单位事件。扩展案例：注册和处理单位事件-- 引入必要的模块 local eventpool = require "xLua.module.EventPool" local playerModule = require "xLua.module.Player" local unitModule = require "xLua.module.Unit" local eventModule = require "xLua.module.Event" -- 获取玩家对象（假设玩家1） local player1 = playerModule[1] -- 创建一个新的单位（假设单位类型为 'hpea'） local unit = unitModule:new({ player = player1, id = 'hpea', x = 500, y = 500 }) print("单位创建成功，位置: x=" .. unit.x .. ", y=" .. unit.y) -- 定义一个事件处理函数 local function onUnitDeath(event, params) print("单位 " .. params.unit.id .. " 死亡") end -- 注册单位死亡事件 eventModule.reg('单位-死亡', { type = 'unit', unit = unit, on_update = onUnitDeath }) -- 模拟单位死亡事件 local function simulateUnitDeath(unit) eventpool:update('单位-死亡', { unit = unit }) end -- 模拟单位死亡 simulateUnitDeath(unit)完整案例：注册和处理多种事件下面是一个更完整的案例，展示了如何注册和处理多种类型的事件，包括玩家事件和单位事件。完整案例：注册和处理多种事件-- 引入必要的模块 local eventpool = require "xLua.module.EventPool" local playerModule = require "xLua.module.Player" local unitModule = require "xLua.module.Unit" local eventModule = require "xLua.module.Event" local timer = require "xLua.module.Timer" -- 获取玩家对象（假设玩家1） local player1 = playerModule[1] -- 创建一个新的单位（假设单位类型为 'hpea'） local unit = unitModule:new({ player = player1, id = 'hpea', x = 500, y = 500 }) print("单位创建成功，位置: x=" .. unit.x .. ", y=" .. unit.y) -- 定义一个玩家聊天事件处理函数 local function onPlayerChat(event, params) print("玩家 " .. params.player.id .. " 发送了消息: " .. params.message) end -- 注册玩家聊天事件 eventModule.reg('玩家-聊天消息', { type = 'player', player = player1, on_update = onPlayerChat }) -- 定义一个单位死亡事件处理函数 local function onUnitDeath(event, params) print("单位 " .. params.unit.id .. " 死亡") end -- 注册单位死亡事件 eventModule.reg('单位-死亡', { type = 'unit', unit = unit, on_update = onUnitDeath }) -- 模拟玩家发送聊天消息 local function simulatePlayerChat(player, message) eventpool:update('玩家-聊天消息', { player = player, message = message }) end -- 模拟单位死亡事件 local function simulateUnitDeath(unit) eventpool:update('单位-死亡', { unit = unit }) end -- 模拟玩家1发送聊天消息 simulatePlayerChat(player1, "Hello, World!") -- 模拟单位死亡 simulateUnitDeath(unit) -- 使用计时器模拟玩家发送聊天消息 local chatTimer = timer:new() chatTimer:start(5, function(event, params) simulatePlayerChat(player1, "这是定时消息") end, false) -- 使用计时器模拟单位死亡 local deathTimer = timer:new() deathTimer:start(10, function(event, params) simulateUnitDeath(unit) end, false)总结xLua.module.Event 模块提供了一套完整的事件处理机制，包括注册、更新和处理事件。通过上述案例，你可以了解如何使用这些方法来处理游戏中的各种事件，从而实现更复杂的游戏逻辑和交互。分析 xLua.module.EventPool 文件夹内的方法xLua.module.EventPool 模块主要用于管理事件池（Event Pool），事件池是事件驱动编程中的一个重要概念，用于存储和分发事件。事件池可以处理各种类型的事件，如玩家输入、单位事件、触发器事件等。以下是 xLua.module.EventPool 模块的主要方法及其使用说明：主要方法new(event_name)功能: 创建一个新的事件池。参数:event_name (string): 事件名称。返回值: 事件池对象 (eventPool 类型)。update(event_name, params)功能: 更新事件池中的事件。参数:event_name (string): 事件名称。params (table): 包含事件参数的表。返回值: 无。reg(event_name, params)功能: 注册一个事件到事件池。参数:event_name (string): 事件名称。params (table): 包含以下字段的表：type (string): 事件类型，例如 'unit'、'player' 等。unit (unit, 可选): 单位对象（如果事件类型是单位事件）。player (player, 可选): 玩家对象（如果事件类型是玩家事件）。on_update (function, 可选): 事件处理函数。返回值: 无。reReg(event_name, params)功能: 重新注册一个事件到事件池。参数:event_name (string): 事件名称。params (table, 可选): 重新注册事件时的参数。返回值: 无。notify(event_name, action, params)功能: 通知事件池中的事件。参数:event_name (string): 事件名称。action (string): 操作类型，例如 '注册事件'、'重新注册事件' 等。params (table): 包含事件参数的表。返回值: 无。使用案例下面是一个具体的案例，展示了如何使用 xLua.module.EventPool 模块创建、注册、更新和处理事件。案例：使用事件池管理玩家聊天和单位死亡事件-- 引入必要的模块 local eventpool = require "xLua.module.EventPool" local playerModule = require "xLua.module.Player" local unitModule = require "xLua.module.Unit" local timer = require "xLua.module.Timer" -- 获取玩家对象（假设玩家1） local player1 = playerModule[1] -- 创建一个新的单位（假设单位类型为 'hpea'） local unit = unitModule:new({ player = player1, id = 'hpea', x = 500, y = 500 }) print("单位创建成功，位置: x=" .. unit.x .. ", y=" .. unit.y) -- 创建一个新的事件池 local playerChatPool = eventpool:new('玩家-聊天消息') local unitDeathPool = eventpool:new('单位-死亡') -- 定义一个玩家聊天事件处理函数 local function onPlayerChat(event, params) print("玩家 " .. params.player.id .. " 发送了消息: " .. params.message) end -- 定义一个单位死亡事件处理函数 local function onUnitDeath(event, params) print("单位 " .. params.unit.id .. " 死亡") end -- 注册玩家聊天事件 playerChatPool:reg('玩家-聊天消息', { type = 'player', player = player1, on_update = onPlayerChat }) -- 注册单位死亡事件 unitDeathPool:reg('单位-死亡', { type = 'unit', unit = unit, on_update = onUnitDeath }) -- 模拟玩家发送聊天消息 local function simulatePlayerChat(player, message) playerChatPool:update('玩家-聊天消息', { player = player, message = message }) end -- 模拟单位死亡事件 local function simulateUnitDeath(unit) unitDeathPool:update('单位-死亡', { unit = unit }) end -- 模拟玩家1发送聊天消息 simulatePlayerChat(player1, "Hello, World!") -- 模拟单位死亡 simulateUnitDeath(unit) -- 使用计时器模拟玩家发送聊天消息 local chatTimer = timer:new() chatTimer:start(5, function(event, params) simulatePlayerChat(player1, "这是定时消息") end, false) -- 使用计时器模拟单位死亡 local deathTimer = timer:new() deathTimer:start(10, function(event, params) simulateUnitDeath(unit) end, false)完整案例：使用事件池管理多种事件下面是一个更完整的案例，展示了如何使用 xLua.module.EventPool 模块创建、注册、更新和处理多种类型的事件，包括玩家事件和单位事件。完整案例：使用事件池管理多种事件-- 引入必要的模块 local eventpool = require "xLua.module.EventPool" local playerModule = require "xLua.module.Player" local unitModule = require "xLua.module.Unit" local timer = require "xLua.module.Timer" -- 获取玩家对象（假设玩家1） local player1 = playerModule[1] -- 创建一个新的单位（假设单位类型为 'hpea'） local unit = unitModule:new({ player = player1, id = 'hpea', x = 500, y = 500 }) print("单位创建成功，位置: x=" .. unit.x .. ", y=" .. unit.y) -- 创建一个新的事件池 local playerChatPool = eventpool:new('玩家-聊天消息') local unitDeathPool = eventpool:new('单位-死亡') -- 定义一个玩家聊天事件处理函数 local function onPlayerChat(event, params) print("玩家 " .. params.player.id .. " 发送了消息: " .. params.message) end -- 定义一个单位死亡事件处理函数 local function onUnitDeath(event, params) print("单位 " .. params.unit.id .. " 死亡") end -- 注册玩家聊天事件 playerChatPool:reg('玩家-聊天消息', { type = 'player', player = player1, on_update = onPlayerChat }) -- 注册单位死亡事件 unitDeathPool:reg('单位-死亡', { type = 'unit', unit = unit, on_update = onUnitDeath }) -- 模拟玩家发送聊天消息 local function simulatePlayerChat(player, message) playerChatPool:update('玩家-聊天消息', { player = player, message = message }) end -- 模拟单位死亡事件 local function simulateUnitDeath(unit) unitDeathPool:update('单位-死亡', { unit = unit }) end -- 模拟玩家1发送聊天消息 simulatePlayerChat(player1, "Hello, World!") -- 模拟单位死亡 simulateUnitDeath(unit) -- 使用计时器模拟玩家发送聊天消息 local chatTimer = timer:new() chatTimer:start(5, function(event, params) simulatePlayerChat(player1, "这是定时消息") end, false) -- 使用计时器模拟单位死亡 local deathTimer = timer:new() deathTimer:start(10, function(event, params) simulateUnitDeath(unit) end, false) -- 重新注册玩家聊天事件 local function onPlayerChatUpdated(event, params) print("更新后的玩家 " .. params.player.id .. " 发送了消息: " .. params.message) end playerChatPool:reReg('玩家-聊天消息', { on_update = onPlayerChatUpdated }) -- 模拟玩家1发送更新后的聊天消息 simulatePlayerChat(player1, "更新后的消息")
魔兽地图编辑器Lua作图教程4：雪月Lua框架⑧区域、可见度修正器、镜头模块
从这节开始，我把一些小模块集中介绍。矩形（Rect）xLua.module.Rect 模块主要用于处理矩形（Rect）对象。矩形对象在游戏开发中非常常见，用于表示区域、碰撞检测、UI布局等。主要方法new(x, y, width, height)功能: 创建一个新的矩形对象。参数:x (number): 矩形左下角的 x 坐标。y (number): 矩形左下角的 y 坐标。width (number): 矩形的宽度。height (number): 矩形的高度。返回值: 矩形对象 (rect 类型)。contains(x, y)功能: 检查给定的点是否在矩形内。参数:x (number): 点的 x 坐标。y (number): 点的 y 坐标。返回值: 布尔值，如果点在矩形内则返回 true，否则返回 false。intersects(otherRect)功能: 检查当前矩形是否与另一个矩形相交。参数:otherRect (rect): 另一个矩形对象。返回值: 布尔值，如果两个矩形相交则返回 true，否则返回 false。move(dx, dy)功能: 移动矩形。参数:dx (number): 沿 x 轴移动的距离。dy (number): 沿 y 轴移动的距离。返回值: 无。resize(dWidth, dHeight)功能: 调整矩形的大小。参数:dWidth (number): 宽度的变化量。dHeight (number): 高度的变化量。返回值: 无。getCenter()功能: 获取矩形的中心点坐标。参数: 无。返回值: 包含 x 和 y 坐标的表。getArea()功能: 获取矩形的面积。参数: 无。返回值: 矩形的面积（number 类型）。使用案例下面是一个具体的案例，展示了如何使用 xLua.module.Rect 模块创建、移动、调整大小、检查包含关系和相交关系的矩形。案例：创建和操作矩形-- 引入 Rect 模块 local rectModule = require "xLua.module.Rect" -- 创建一个新的矩形 local rect1 = rectModule.new(10, 10, 50, 30) print("矩形1: x=" .. rect1.x .. ", y=" .. rect1.y .. ", width=" .. rect1.width .. ", height=" .. rect1.height) -- 创建另一个矩形 local rect2 = rectModule.new(30, 20, 40, 20) print("矩形2: x=" .. rect2.x .. ", y=" .. rect2.y .. ", width=" .. rect2.width .. ", height=" .. rect2.height) -- 检查点是否在矩形内 local pointInside = rect1:contains(20, 20) print("点 (20, 20) 是否在矩形1内: " .. tostring(pointInside)) -- 移动矩形1 rect1:move(10, 10) print("移动后的矩形1: x=" .. rect1.x .. ", y=" .. rect1.y .. ", width=" .. rect1.width .. ", height=" .. rect1.height) -- 调整矩形1的大小 rect1:resize(20, 10) print("调整大小后的矩形1: x=" .. rect1.x .. ", y=" .. rect1.y .. ", width=" .. rect1.width .. ", height=" .. rect1.height) -- 检查两个矩形是否相交 local isIntersecting = rect1:intersects(rect2) print("矩形1和矩形2是否相交: " .. tostring(isIntersecting)) -- 获取矩形1的中心点 local center = rect1:getCenter() print("矩形1的中心点: x=" .. center.x .. ", y=" .. center.y) -- 获取矩形1的面积 local area = rect1:getArea() print("矩形1的面积: " .. area)迷雾（Fog）xLua.module.Fog 模块主要用于处理游戏中的迷雾（Fog）效果。迷雾效果用于控制玩家视野范围，增加游戏的策略性和挑战性。主要方法rect(params)功能: 设置指定矩形区域的迷雾状态。参数:params (table): 包含以下字段的表：player (player): 玩家对象。state (string): 迷雾状态，可选值包括 'visible'（可见）、'fogged'（迷雾）、'hidden'（隐藏）。rect (rect): 矩形区域对象。返回值: 无。circle(params)功能: 设置指定圆形区域的迷雾状态。参数:params (table): 包含以下字段的表：player (player): 玩家对象。state (string): 迷雾状态，可选值包括 'visible'（可见）、'fogged'（迷雾）、'hidden'（隐藏）。x (number): 圆心的 x 坐标。y (number): 圆心的 y 坐标。radius (number): 圆的半径。返回值: 无。all(params)功能: 设置整个地图的迷雾状态。参数:params (table): 包含以下字段的表：player (player): 玩家对象。state (string): 迷雾状态，可选值包括 'visible'（可见）、'fogged'（迷雾）、'hidden'（隐藏）。返回值: 无。toggle(params)功能: 切换指定矩形区域的迷雾状态。参数:params (table): 包含以下字段的表：player (player): 玩家对象。rect (rect): 矩形区域对象。返回值: 无。使用案例下面是一个具体的案例，展示了如何使用 xLua.module.Fog 模块设置和操作迷雾效果。案例：设置和操作迷雾效果-- 引入必要的模块 local fog = require "xLua.module.Fog" local rectModule = require "xLua.module.Rect" local playerModule = require "xLua.module.Player" -- 获取玩家对象（假设玩家1） local player1 = playerModule[1] -- 创建一个新的矩形区域 local rect1 = rectModule.new(100, 100, 200, 150) print("矩形区域: x=" .. rect1.x .. ", y=" .. rect1.y .. ", width=" .. rect1.width .. ", height=" .. rect1.height) -- 设置矩形区域为可见 fog:rect({ player = player1, state = 'visible', rect = rect1 }) print("矩形区域设置为可见") -- 创建一个新的圆形区域 local circleX = 300 local circleY = 300 local circleRadius = 100 print("圆形区域: x=" .. circleX .. ", y=" .. circleY .. ", radius=" .. circleRadius) -- 设置圆形区域为迷雾 fog:circle({ player = player1, state = 'fogged', x = circleX, y = circleY, radius = circleRadius }) print("圆形区域设置为迷雾") -- 设置整个地图为隐藏 fog:all({ player = player1, state = 'hidden' }) print("整个地图设置为隐藏") -- 切换矩形区域的迷雾状态 fog:toggle({ player = player1, rect = rect1 }) print("矩形区域的迷雾状态已切换")下面是一个更复杂的案例，展示了如何动态更新迷雾效果，例如在游戏过程中根据玩家的位置动态显示或隐藏某些区域。扩展案例：动态更新迷雾效果-- 引入必要的模块 local fog = require "xLua.module.Fog" local rectModule = require "xLua.module.Rect" local playerModule = require "xLua.module.Player" local timer = require "xLua.module.Timer" -- 获取玩家对象（假设玩家1） local player1 = playerModule[1] -- 创建一个新的矩形区域 local rect1 = rectModule.new(100, 100, 200, 150) print("矩形区域: x=" .. rect1.x .. ", y=" .. rect1.y .. ", width=" .. rect1.width .. ", height=" .. rect1.height) -- 设置矩形区域为可见 fog:rect({ player = player1, state = 'visible', rect = rect1 }) print("矩形区域设置为可见") -- 创建一个新的圆形区域 local circleX = 300 local circleY = 300 local circleRadius = 100 print("圆形区域: x=" .. circleX .. ", y=" .. circleY .. ", radius=" .. circleRadius) -- 设置圆形区域为迷雾 fog:circle({ player = player1, state = 'fogged', x = circleX, y = circleY, radius = circleRadius }) print("圆形区域设置为迷雾") -- 设置整个地图为隐藏 fog:all({ player = player1, state = 'hidden' }) print("整个地图设置为隐藏") -- 定义一个计时器，每秒切换矩形区域的迷雾状态 local toggleTimer = timer:new() toggleTimer:start(1, function(event, params) fog:toggle({ player = player1, rect = rect1 }) print("矩形区域的迷雾状态已切换") end, true) -- 定义一个计时器，每2秒切换圆形区域的迷雾状态 local circleToggleTimer = timer:new() circleToggleTimer:start(2, function(event, params) fog:circle({ player = player1, state = fog:circle({ player = player1, x = circleX, y = circleY, radius = circleRadius }) == 'fogged' and 'visible' or 'fogged', x = circleX, y = circleY, radius = circleRadius }) print("圆形区域的迷雾状态已切换") end, true)镜头（Camera）xLua.module.Camera 模块主要用于控制游戏中的镜头（Camera）。镜头控制用于调整玩家的视野范围、跟随单位或特定位置等。主要方法setZ(height)功能: 设置镜头的高度（Z轴位置）。参数:height (number): 镜头的高度。返回值: 无。getZ()功能: 获取镜头的高度（Z轴位置）。参数: 无。返回值: 镜头的高度（number 类型）。setXY(x, y, time)功能: 平移镜头到指定的 x 和 y 坐标。参数:x (number): 目标 x 坐标。y (number): 目标 y 坐标。time (number): 平移时间（秒）。返回值: 无。SetWideScreen(enable)功能: 启用或禁用宽屏模式。参数:enable (boolean): 是否启用宽屏模式。返回值: 无。IsWideScreen()功能: 检查是否启用了宽屏模式。参数: 无。返回值: 布尔值，如果启用了宽屏模式则返回 true，否则返回 false。BlackBorders(top, bottom)功能: 设置镜头的黑边范围。参数:top (number): 顶部黑边比例（0 到 1）。bottom (number): 底部黑边比例（0 到 1）。返回值: 无。GetView()功能: 获取当前镜头的视图矩阵。参数: 无。返回值: 视图矩阵对象（Vec3 类型）。WorldToScreen(worldPos)功能: 将世界坐标转换为屏幕坐标。参数:worldPos (table): 包含 x 和 y 字段的世界坐标。返回值: 屏幕坐标（Vec3 类型）。使用案例下面是一个具体的案例，展示了如何使用 xLua.module.Camera 模块控制镜头的高度、位置、宽屏模式和黑边范围。案例：控制镜头-- 引入必要的模块 local camera = require "xLua.module.Camera" local timer = require "xLua.module.Timer" -- 设置镜头高度 camera.setZ(1000) print("镜头高度设置为: " .. camera.getZ()) -- 平移镜头到指定位置 camera.setXY(500, 500, 2) print("镜头平移到 (500, 500) 用时 2 秒") -- 启用宽屏模式 camera.SetWideScreen(true) print("宽屏模式已启用: " .. tostring(camera.IsWideScreen())) -- 设置黑边范围 camera.BlackBorders(0.020, 0.130) print("黑边范围设置为: 顶部 0.020, 底部 0.130") -- 获取当前视图矩阵 local viewMatrix = camera.GetView() print("当前视图矩阵: x=" .. viewMatrix.x .. ", y=" .. viewMatrix.y .. ", z=" .. viewMatrix.z) -- 将世界坐标转换为屏幕坐标 local worldPos = {x = 500, y = 500} local screenPos = camera.WorldToScreen(worldPos) print("世界坐标 (500, 500) 转换为屏幕坐标: x=" .. screenPos.x .. ", y=" .. screenPos.y) -- 使用计时器动态调整镜头位置 local moveTimer = timer:new() moveTimer:start(5, function(event, params) camera.setXY(1000, 1000, 2) print("镜头平移到 (1000, 1000) 用时 2 秒") end, false) -- 使用计时器动态调整镜头高度 local heightTimer = timer:new() heightTimer:start(10, function(event, params) camera.setZ(1500) print("镜头高度设置为: " .. camera.getZ()) end, false)扩展案例：跟随单位移动下面是一个更复杂的案例，展示了如何使用 xLua.module.Camera 模块动态跟随一个单位移动。扩展案例：跟随单位移动-- 引入必要的模块 local camera = require "xLua.module.Camera" local unitModule = require "xLua.module.Unit" local timer = require "xLua.module.Timer" -- 获取玩家对象（假设玩家1） local player1 = playerModule[1] -- 创建一个新的单位（假设单位类型为 'hpea'） local unit = unitModule:new({ player = player1, id = 'hpea', x = 500, y = 500 }) print("单位创建成功，位置: x=" .. unit.x .. ", y=" .. unit.y) -- 启用宽屏模式 camera.SetWideScreen(true) print("宽屏模式已启用: " .. tostring(camera.IsWideScreen())) -- 设置黑边范围 camera.BlackBorders(0.020, 0.130) print("黑边范围设置为: 顶部 0.020, 底部 0.130") -- 定义一个计时器，每秒更新镜头位置以跟随单位 local followTimer = timer:new() followTimer:start(1, function(event, params) if unit and unit.x and unit.y then camera.setXY(unit.x, unit.y, 1) print("镜头跟随单位，位置: x=" .. unit.x .. ", y=" .. unit.y) end end, true) -- 定义一个计时器，每5秒调整镜头高度 local heightTimer = timer:new() heightTimer:start(5, function(event, params) local currentHeight = camera.getZ() local newHeight = currentHeight + 100 camera.setZ(newHeight) print("镜头高度调整为: " .. newHeight) end, true) -- 定义一个计时器，每10秒切换宽屏模式 local wideScreenTimer = timer:new() wideScreenTimer:start(10, function(event, params) local isWideScreen = camera.IsWideScreen() camera.SetWideScreen(not isWideScreen) print("宽屏模式已切换: " .. tostring(camera.IsWideScreen())) end, true)
魔兽地图编辑器Lua作图教程4：雪月Lua框架⑦中心计时器（Timer）模块
这个小节主要介绍下雪月Lua框架xLua.module.Timer 文件夹xLua.module.Timer 模块主要用于创建和管理计时器（Timers）。计时器在魔兽地图制作中非常常见，用于在特定时间间隔执行某些操作，例如刷兵、动画、状态更新、冷却时间等。主要方法new()功能: 创建一个新的计时器对象。返回值: 计时器对象 (timer 类型)。start(interval, callback, repeat)功能: 启动计时器。参数:interval (number): 计时器间隔时间（秒）。callback (function): 计时器触发时执行的回调函数。repeat (boolean, 可选): 是否重复执行，默认为 false。返回值: 无。stop()功能: 停止计时器。参数: 无。返回值: 无。pause()功能: 暂停计时器。参数: 无。返回值: 无。resume()功能: 恢复计时器。参数: 无。返回值: 无。del()功能: 删除计时器，释放相关资源。参数: 无。返回值: 无。使用案例下面是一个具体的案例，展示了如何使用 xLua.module.Timer 模块创建、启动、停止和删除计时器。案例：创建一个每秒打印一次消息的计时器-- 引入 Timer 模块 local timer = require "xLua.module.Timer" -- 创建一个新的计时器 local myTimer = timer:loop(2, function() print("计时器触发了！当前时间: " .. os.date("%H:%M:%S", os.time())) end)