-- 动画播放实例,根据状态表来决定什么状态播放什么动画 -- 对外接口: --[[ self.Entity 骨骼动画的节点 self.StateValues 可以用来做数据绑定的状态值列表 self.OnStateFinished 状态播放完成的回调,参数形式OnStateFinish(animEntity, key , value) self.OnGetNextValue 状态播放完成后需要把value设置成什么的回调,,参数形式OnGetNextValue(animEntity, key , value),函数里要返回一个int,表示value要变成什么 self:setValue self:getValue --]] local AnimEntity = class("AnimEntity") -- 初始化,传进来initStatus是状态表,拥有多少个状态,以及各个状态的值 -- animation 动画名字 -- loop 是否循环播放 -- staticWhenEnd 是否静止在最后一帧 -- randomPosition 开始播放这个动画时是否使用随机位置 --[[ local status = { death = { 1 = {animation = "death" , loop = true}; }; skill = { 1 = {animation = "skill" , loop = false}; }; attack = { 1 = {animation = "attack" , loop = false}; }; move = { 0 = {animation = "stand" , loop = true}; 1 = {animation = "move" , loop = true}; }; } local function getAnimation(values) if values.death == 1 then return status.death[1]; elseif values.skill == 1 then return status.skill[1]; elseif values.attack == 1 then return status.attack[1]; elseif values.move == 1 then return status.move[1]; else return status.move[0]; end end local entity = AnimEntity:new(skeleton , status , getAnimation); --]] function AnimEntity:ctor(skeletonEntity , initStatus , callbackGetAnimation) self.Status = initStatus; self.StateValues = require("luaScript.Protocol.BindableArray")(); self.CallbackGetAnimation = callbackGetAnimation self.CurrentAnimation = nil; self.Entity = skeletonEntity; skeletonEntity.AnimEntity = self; self:initStatus(); end -- 初始化状态表 function AnimEntity:initStatus() for i , v in pairs(self.Status) do self.StateValues[i] = 0; -- 初始化所有动画长度 for ii , state in pairs(v) do state.OnStateFinished = {}; state.OnGetNextValue = {}; local clipFile; if self.Entity.getClipFile then clipFile = self.Entity:getClipFile(); end if clipFile then local clip = clipFile:getAnimationClip(state.animation); if clip == nil then print("获取模型动画" .. state.animation .. "失败" , self.Entity:getMeshFile()); else -- 这个动画的持续时间为毫秒 state.duration = clip:getDuration() / 1000 / clip:getSpeed(); if state.duration == nil then error("获取模型动画" .. state.animation .. "长度失败" , self.Entity:getMeshFile()); end -- 这里不用侦听了,因为我们统一通过Action来停止 -- 侦听动画结束回调 --self.Entity:getAnimationClip(state.animation):addEndListener(function()self:onPlayEnd(state.animation)end); end end end end -- 侦听状态改变通知 self.Entity:bindUpdate(self.StateValues , handler(self , self.updateState)); local function initAnimation(index) if self.CurrentAnimation then self:onChangeAnimation(nil , self.CurrentAnimation); end end self.Entity:runOnLoad(initAnimation); end --[[ -- 事件回调 function AnimEntity:onPlayEnd(animation) -- 动画播放完毕 -- 完成了一次循环,则把非循环动画停止 if self.CurrentAnimation ~= nil and animation == self.CurrentAnimation.animation and not self.CurrentAnimation.loop and not self.CurrentAnimation.staticWhenEnd then --print(tostring(self) .. "动画complete" .. animation); self.CurrentAnimation = nil; end end --]] -- 一个状态的动作是否无限长 function AnimEntity:isInfiniteByState(stateName, stateValue) local stateInfo = self.Status[stateName] if stateInfo ~= nil and stateInfo[stateValue] ~= nil then return stateInfo[stateValue].loop end return false end -- 改变状态值 function AnimEntity:setValue(state , value) self.StateValues[state] = value; end -- 获取状态值 function AnimEntity:getValue(state) return self.StateValues[state]; end function AnimEntity:addStateFinishListener(state , value , listener) local stateStatus = self.Status[state][value]; if stateStatus.OnStateFinishedLoop then print("AnimEntity:addStateFinishListener,添加无效", state, value) return end stateStatus.OnStateFinished[listener] = listener; end function AnimEntity:removeStateFinishListener(state , value , listener) local stateStatus = self.Status[state][value]; if stateStatus.OnStateFinishedLoop then --print("AnimEntity:removeStateFinishListener,移除无效, 遍历完成后会全部移除掉的", state, value) return end stateStatus.OnStateFinished[listener] = nil; end function AnimEntity:addNextValueListener(state , value , listener) local stateStatus = self.Status[state][value]; if stateStatus.OnGetNextValueLoop then print("AnimEntity:addNextValueListener,添加无效", state, value) return end stateStatus.OnGetNextValue[listener] = listener; end function AnimEntity:removeNextValueListener(state , value , listener) local stateStatus = self.Status[state][value]; if stateStatus.OnGetNextValueLoop then print("AnimEntity:removeNextValueListener,移除无效, 遍历完成后会全部移除掉的", state, value) return end stateStatus.OnGetNextValue[listener] = nil; end function AnimEntity:resetValues() for i , v in self.StateValues:pairs() do if v ~= 0 then self.StateValues[i] = 0; end end end -- 更新状态 function AnimEntity:updateState(entity , key , value) --print(tostring(self) .. "动画状态改变:" .. key .. " = " .. value); -- 找不到对应的状态名(则说明设置错了) if self.Status[key] == nil then print("找不到对应的状态名:" .. key); return end local state = self.Status[key][value]; if state then if tolua.isnull(state.Action) then -- 非循环状态,则自动停止 if not state.loop then local function onEnd() state.Action = nil; state.OnStateFinishedLoop = true -- 动画状态播放完成回调 for i , v in pairs(state.OnStateFinished) do v(self, key , value); end state.OnStateFinishedLoop = false state.OnStateFinished = {} --print(tostring(self) .. "动画状态播放完毕" .. state.animation); if not state.staticWhenEnd then local retValue = 0; state.OnGetNextValueLoop = true -- 获取播放完毕要设置的值 for i , v in pairs(state.OnGetNextValue) do local nextValue = v(self, key , value); if nextValue then retValue = nextValue; end end state.OnGetNextValueLoop = false state.OnGetNextValue = {} self:setValue(key , retValue); end end -- 当模型没有这个动画时duration在开始的时候并没有赋值 if state.duration ~= nil then -- 时间到了之后,自动把状态设置成0 state.Action = self.Entity:runActions(cc.DelayTime:create(state.duration) , onEnd); state.Action.OnEnd = onEnd; else -- 没有这个动画的立马回调 onEnd(); end end end end local anim = self.CallbackGetAnimation(self.StateValues) self:setAnimation(anim); end -- 设置当前状态 function AnimEntity:setAnimation(animation) if self.CurrentAnimation == animation then return; end local from = self.CurrentAnimation; self.CurrentAnimation = animation; if self.Entity:getModelScene() then self:onChangeAnimation(from , animation); end end -- 动画改变通知 function AnimEntity:onChangeAnimation(from , to) --print(tostring(self) .. "播放动画" .. to.animation); local fromClip; -- 先停止之前的动画 if from then fromClip = self.Entity:getAnimationClip(from.animation); -- 停止回调 if from.Action then if not tolua.isnull(from.Action) then from.Action.OnEnd(); self.Entity:stopAction(from.Action) end from.Action = nil; end end local targetClip = self.Entity:getAnimationClip(to.animation); if targetClip then if to.loop then targetClip:setRepeatCount(0); else targetClip:setRepeatCount(1); end if fromClip then --print("从" , fromClip:getId() , "切换到" , targetClip:getId()); fromClip:crossFade(targetClip , 200); else --print("直接播放" , targetClip:getId()); targetClip:play(); end -- 随机设置开始位置 if to.randomPosition then targetClip:setTimePosition(math.random(0 , targetClip:getDuration())); end end end return AnimEntity;