Citrus Engine + Dragon Bones: The Cyborg
By converting all graphics to MovieClip then it will marked as Skeleton/Armature by Dragon Bones. So it have independent animation. In this case it's the blinking eye.
Originally, this DB .fla example will only have 3 skeletons cyborg, armOutside, and armInside, but we've convert the head's graphics into MovieClip, and add the blinking animation, so now you can see, it become a DB skeleton.
package
{
import citrus.core.starling.StarlingState;
import citrus.objects.platformer.nape.Hero;
import citrus.objects.platformer.nape.Platform;
import citrus.physics.nape.Nape;
import citrus.view.starlingview.StarlingArt;
import dragonBones.Armature;
import dragonBones.Bone;
import dragonBones.events.AnimationEvent;
import dragonBones.factorys.StarlingFactory;
import dragonBones.objects.XMLDataParser;
import flash.events.TimerEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.ui.Keyboard;
import flash.utils.Timer;
import starling.textures.Texture;
import starling.textures.TextureAtlas;
public class GameState extends StarlingState
{
[Embed(source="../assets/texture.png")]
private static const CYBORG_PNG:Class;
[Embed(source="../assets/texture.xml",mimeType="application/octet-stream")]
private static const CYBORG_TEX_DATA:Class;
[Embed(source="../assets/skeleton.xml",mimeType="application/octet-stream")]
private static const CYBORG_SKELETON:Class;
private var factory:StarlingFactory;
private var armature:Armature;
private var rightHand:Bone;
private var leftHand:Bone;
private var weaponID:int = 1;
private var canShoot:Boolean = true;
private var shootTimer:Timer;
public function GameState()
{
super();
}
override public function initialize():void
{
super.initialize();
StarlingArt.setLoopAnimations(["idle"]);
var nape:Nape = new Nape("nape");
nape.visible = true;
add(nape);
var floor:Platform = new Platform("floor", {width: 1400, height: 20});
floor.x = 700;
floor.y = stage.stageHeight;
add(floor);
for (var i:int = 1; i <= 2; i++)
{
var wall:Platform = new Platform("wall" + String(i), {width: 20, height: 400});
wall.y = 200;
if (i == 1)
{
wall.x = 0;
}
else
{
wall.x = 1400;
}
add(wall);
}
var atlas:TextureAtlas = new TextureAtlas(Texture.fromBitmap(new CYBORG_PNG()), XML(new CYBORG_TEX_DATA()));
factory = new StarlingFactory();
factory.addSkeletonData(XMLDataParser.parseSkeletonData(XML(new CYBORG_SKELETON())));
factory.addTextureAtlas(atlas, "CyborgCitrus");
armature = factory.buildArmature("cyborg");
rightHand = armature.getBone("armOutside");
leftHand = armature.getBone("armInside");
var hero:Hero = new Hero("hero", {registration: "topLeft", width: 70, height: 200});
hero.view = armature;
hero.x = 700;
hero.y = 100;
add(hero);
view.camera.setUp(hero, new Rectangle(0, 0, 1400, 400), new Point(.5, .5));
_ce.input.keyboard.addKeyAction("ChangeWeapon", Keyboard.A);
_ce.input.keyboard.addKeyAction("Shoot", Keyboard.S);
shootTimer = new Timer(300, 0);
shootTimer.addEventListener(TimerEvent.TIMER, shootHandler);
}
override public function update(timeDelta:Number):void
{
super.update(timeDelta);
if (_ce.input.isDoing("Shoot") && canShoot)
{
rightHand.childArmature.animation.gotoAndPlay("weapon" + weaponID + "shoot", .1);
leftHand.childArmature.animation.gotoAndPlay("weapon" + weaponID + "shoot", .1);
rightHand.childArmature.addEventListener(AnimationEvent.COMPLETE, animComplete, false, 0, true);
leftHand.childArmature.addEventListener(AnimationEvent.COMPLETE, animComplete, false, 0, true);
canShoot = false;
shootTimer.start();
}
if (_ce.input.justDid("ChangeWeapon"))
{
changeWeapon();
}
}
private function shootHandler(event:TimerEvent):void
{
canShoot = true;
shootTimer.stop();
}
private function changeWeapon():void
{
if (weaponID < 4)
{
weaponID++;
}
else
{
weaponID = 1;
}
rightHand.childArmature.animation.gotoAndPlay("weapon" + weaponID);
leftHand.childArmature.animation.gotoAndPlay("weapon" + weaponID);
switch (weaponID)
{
case 1:
shootTimer.delay = 500;
break;
case 2:
case 4:
shootTimer.delay = 1000;
break;
case 3:
shootTimer.delay = 300;
break;
}
}
private function animComplete(event:AnimationEvent):void
{
rightHand.childArmature.animation.gotoAndPlay("weapon" + weaponID, .1);
leftHand.childArmature.animation.gotoAndPlay("weapon" + weaponID, .1);
rightHand.childArmature.removeEventListener(AnimationEvent.COMPLETE, animComplete);
leftHand.childArmature.removeEventListener(AnimationEvent.COMPLETE, animComplete);
}
}
}
From the hand bones, access its childArmature, then access the animation property, and finally move to the target frame using gotoAndPlay() method.
The process is similar like how we change to shooting animation before.
Alright, the tutorial is done.
Now you can achieve a nested animation and also weapon switching feature for your game, just like the old Flash movieclip, with gotoAndPlay and so on.
Well, not as straight forward than movieclip, but still, this is a great feature from Dragon Bones.
And finally, the source code.
.png)

.png)
.png)

4 comments: