【游戏功能】
一、一只拎着剑的鸭子
换装系统首测的截图至今还在组里的翻车合集里:战士跑动时背上那把裁决之杖慢半拍地飘着,像鸭子拎着一根比自己长的杆子过河。原因很朴素——武器动画是独立播放的,起播时间比人物晚了十几毫秒,帧率又各自计数,跑得越久错位越大,攻击动作里更是人和刀各挥各的。武器不是一件独立的表现,它是人物动作的影子,影子的每一帧都必须跟本体严格同频。
二、机制原理:共用动作号与帧号的外显层
这套资源的巧妙之处在于武器与人物共用同一套坐标系:同样的动作编号、同样的方向编号、同样的帧数排布,武器图集就是人物图集的"另一半"。于是同步不需要任何通信——只要武器层与人物层从同一个状态源取数:人物更新动作,武器跟着更新动作;人物推进到第几帧,武器就画第几帧;锚点同为脚底中心,两层逐帧叠画,武器自然出现在手持的位置,因为美术在制作武器帧时就是对着人物帧一帧帧摆的位置。
mir8-weapon
三、正确写法:单状态源的外显叠加
local weaponLayer = class("weaponLayer")
function weaponLayer:ctor(owner)
self._owner = owner -- 只持引用, 不持状态
self._anim = CreateSequAnim("weapon", owner:GetWeaponID())
end
-- 每帧由人物驱动更新: 武器层自己不做任何决定
function weaponLayer:SyncFromOwner()
if not self._owner:GetWeaponVisible() then
self._anim:setVisible(false)
return
end
self._anim:setVisible(true)
self._anim:Play(self._owner:GetFrameIndex(), -- 同一帧号
self._owner:GetDir(), -- 同一方向
self._owner:IsLoop(), -- 同一循环语义
self._owner:GetSpeed()) -- 同一倍速
self._anim:setPosition(self._owner:GetPos()) -- 同一脚底锚点
self._anim:setLocalZOrder(self._owner:GetZ() + 1)
end
function gameActor:OnChangeWeapon(weaponID)
self._weaponAnim:SwitchAnim(weaponID, self._act, self._dir) -- 原动作原方向热切
end
四、四个必须钉死的细节
五、心法:让影子自己没有想法
外显同步的心法一句话:装饰层不产生状态,只消费状态。人物层是唯一的状态机,武器翅膀光环全是它的影子,影子的职责是"无条件像"。任何想在装饰层里加一点自己逻辑的冲动——自己缓一缓、自己排个队——都会在未来某个半拍的错位里现形。状态收敛到一处,表现发散到多层,这是外显系统乃至整个表现架构的总纲。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【语法算法】 元方法 __close 的错误传递就这两行: to-be-closed 变量离开作用域时自动调用 __clos…
【语法算法】 goto 向后跳的控制就这一行: ::retry:: 定义标签, goto retry 跳回标签——向后跳的全…
【语法算法】 元方法 __close 的多资源清理就这两行: 两个 to-be-closed 变量——声明的逆序清理——先关…
【语法算法】 随机种子的确定性本质就这一行: 固定种子意味着随机数序列完全确定——同样的种子产生同样的序列——随机种子的全部…
【语法算法】 协程的状态机就这一行: status 返回四种字符串之一:"suspended"、"running"、"nor…
【语法算法】 xpcall 的错误对象就这两行: xpcall 和 pcall 的本质区别就是第二个参数——错误处理函数——…