骨骼动画原理
ž 由于手机游戏都受到容量的限制,
ž 一般不采用帧动画的方法制作角色的动画,而是将角色肢解,然后用肢解得到的各个部分重新组装成原来动画中的每个图片,
ž 而且这样角色的动画也可以更加丰富,
骨骼动画中的元素
ž 动画纹理图片
ž 骨骼Bone
ž 帧 Frame
ž 图层 layer
ž 动画 Animation
ž 一个骨骼就是肢解后的一个部分,主角的动画共有43个Bone。
ž 每个Bone绑定一个肢解的图块,描述图块需要4项数据:左上角在Image中的x坐标、y坐标、宽度和高度。
ž 每个骨骼都有一个唯一标识它的id。
ž 每个图块也有唯一标识的id
ž 在骨骼动画编辑器中定义他们之间的绑定
ž 骨骼和骨骼之间要定义父子关系,一般一个骨骼动画会有一个根节点骨骼
ž 父子关系决定了每个骨骼的运动依赖
Frame
ž Frame是每次画在屏幕上的内容,也是组成动作的一系列图片
ž 每个图层的当前帧叠加在一起组成了动画的Frame
ž 在CocoStudio中可以定义FrameEvent
ž 系统动画的播放中也定义了LOOP_COMPLETE 动画事件用于检测整个动画播放完成
图层Layer
ž Layer实际上是带有额外信息的骨骼图片块
ž Layer对原有矩形图片块做了各种变换操作,如旋转,缩放等
ž 在Cocostudio中骨骼也是一种Layer
Animation 动画
ž Animation就是连续播放的一系列Frame。一个动作中,每个Frame都有delay,表示在播放动作时这个Frame要持续画在屏幕上多少次。
显示过程
ž 每个Layer的显示过程分为三个部分——提取信息、计算数据和绘制图片
ž 对大图的处理会经过多次剪裁实现
使用Armature编辑器
ž 1创建骨骼
ž 2绑定图层
ž 3创建帧和关键帧
ž 4添加动画
ž 5导出文件
在战斗场景加入骨骼动画C++
ž ArmatureDataManager::getInstance()->addArmatureFileInfoAsync("armature/Cowboy.ExportJson",this, schedule_selector(TestAsynchronousLoading::dataLoaded)); ArmatureDataManager::getInstance()->addArmatureFileInfoAsync("armature/hero.ExportJson",this, schedule_selector(TestAsynchronousLoading::dataLoaded));
ž armature =Armature::create("Dragon");
ž armature->getAnimation()->playWithIndex(1);
ž //播放第二个动画
ž armature->getAnimation()->setSpeedScale(0.4f);
在战斗场景加入骨骼动画lua
ž ccs.ArmatureDataManager:getInstance():addArmatureFileInfoAsync("armature/knight.png","armature/knight.plist","armature/knight.xml",dataLoaded) ccs.ArmatureDataManager:getInstance():addArmatureFileInfoAsync("armature/weapon.png","armature/weapon.plist", "armature/weapon.xml", dataLoaded)
ž local armature =ccs.Armature:create("bear") armature:getAnimation():playWithIndex(0)
设置骨骼动画的播放速度
ž C++
ž armature->getAnimation()->setSpeedScale(0.4f);
ž Lua
ž armature:setScale(0.2)
侦听动画播放完成事件C++
ž armature->getAnimation()->
setMovementEventCallFunc(
CC_CALLBACK_0(TestAni::animationEvent,this));
ž addChild(armature);
ž void TestAnimationEvent::animationEvent(
ž Armature *armature, MovementEventTypemovementType, const std::string& movementID){
ž if(movementType == LOOP_COMPLETE) { if (movementID =="Fire") {
侦听动画播放完成事件Lua
ž armature:getAnimation():setMovementEventCallFunc(animationEvent)
ž local function animationEvent(
ž armatureBack,movementType,movementID)
ž local id = movementID
ž if movementType == ccs.MovementEventType.loopCompletethen if id == "Fire"then
ž 。。。。。。
ž end
ž end
ž end
动态修改骨骼显示NodeC++
ž std::string weapon[] ={"weapon_f-sword.png", "weapon_f-sword2.png","weapon_f-sword3.png", "weapon_f-sword4.png","weapon_f-sword5.png", "weapon_f-knife.png","weapon_f-hammer.png"};
ž for(int i = 0; i < 7; i++) {
ž Skin *skin = Skin::createWithSpriteFrameName(weapon[i].c_str());
ž armature->getBone("weapon")->addDisplay(skin,i); }
ž voidTest::onTouchesEnded(const std::vector<Touch*>& touches, Event* event){
ž ++displayIndex;
ž displayIndex= (displayIndex) % 8; armature->getBone("weapon")->
ž changeDisplayWithIndex(displayIndex,true);}
动态修改骨骼显示Nodelua
ž local weapon = {"weapon_f-sword.png","weapon_f-sword2.png", "weapon_f-sword3.png", "weapon_f-sword4.png", "weapon_f-sword5.png", "weapon_f-knife.png", "weapon_f-hammer.png", }
ž local i = 1
ž fori = 1,table.getn(weapon) do
ž local skin =ccs.Skin:createWithSpriteFrameName(weapon[i]) self.armature:getBone("weapon"):addDisplay(skin, i - 1)
ž end -- handling touch events
ž local function onTouchEnded(touches,event)
ž self.displayIndex = (self.displayIndex + 1) %(table.getn(weapon) - 1) self.armature:getBone("weapon"):changeDisplayWithIndex(self.displayIndex,true)
ž end
ž local listener =cc.EventListenerTouchAllAtOnce:create() listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCHES_ENDED)
ž local eventDispatcher =self:getEventDispatcher() eventDispatcher:addEventListenerWithSceneGraphPriority(listener, self)
在lua中播放一个骨骼动画,
--加载动画到内存 ccs.ArmatureDataManager:getInstance():addArmatureFileInfo(
"NewAniData/NewAniData0.png","NewAniData/NewAniData0.plist","NewAniData/NewAniData.ExportJson")
--从内存中取出动画
local armature=ccs.Armature:create("NewAniData")
--播放第一个动画
armature:getAnimation():play("move")
--把动画添加到屏幕
layer:addChild(armature)
armature:setPosition(self.winsize.width/2,self.winsize.height/2)
lua中添加一个帧事件处理
--添加帧事件处理动画--
local function onFrameEvent(bone,evt,originFrameIndex,currentFrameIndex)
print("骨骼编号"..tostring(bone).."事件名"..evt.."当前帧"..currentFrameIndex)
end
--设定帧事件的回调函数
armature:getAnimation():setFrameEventCallFunc(onFrameEvent)
--点击,获取骨骼,修改它的显示,动态修改骨骼显示Node lua
--首先定义一个全局的count
在构造中定义 self.count=1
--添加骨骼绑定的皮肤
local weapon = { "weapon_f-sword.png", "weapon_f-sword2.png","weapon_f-sword3.png",
"weapon_f-sword4.png", "weapon_f-sword5.png", "weapon_f-knife.png", "weapon_f-hammer.png", }
cc.SpriteFrameCache:getInstance():addSpriteFrames("weapon.plist")
local i = 1
for i = 1,table.getn(weapon) do
local skin = ccs.Skin:createWithSpriteFrameName(weapon[i])
armature:getBone("Layer18"):addDisplay(skin, i - 1)
end -- handling touch events
local lis=cc.EventListenerTouchOneByOne:create()
local function onTouchBegan(t,e)
self.count = (self.count + 1) % (table.getn(weapon) - 1)
armature:getBone("Layer18"):changeDisplayWithIndex(self.count, true)
end
lis:registerScriptHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN)
cc.Director:getInstance():getEventDispatcher():addEventListenerWithSceneGraphPriority(lis,layer)
--这样就实现了点击一下,就切换皮肤