Hey everyone ‘Starling developers’, I have started a post to discuss’ MovieClip’ in ‘Starling’
Declaration: Because I cannot find the haxe starling API, I am looking for the as3 starling API, and I believe the API is consistent
I seem to have found a way to manually add keyframe code
Sets an action that will be executed whenever a certain frame is reached.
setFrameAction(frameID:int, action:Function):void
Do you also use this method to add keyframe code?
The equivalent method is here:
The previous method of implementing character animation playback (multiple Movieclip)
package;
import starling.core.Starling;
import starling.textures.Texture;
import openfl.Vector;
import starling.display.MovieClip;
import openfl.utils.Dictionary;
import starling.display.Sprite;
class Anim extends Sprite {
private var animations:Dictionary<String, MovieClip> = new Dictionary();
private var currentAnimation:MovieClip;
public var currentLabel:String;
public function new() {
super();
}
public function addAnimation(name:String, textures:Vector<Texture>, fps:Float = 12):Void {
final mc:MovieClip = new MovieClip(textures, fps);
animations[name] = mc;
}
public function gotoAndPlay(name:String, frame:Int = 0):Void {
if (currentLabel == name) {
return;
}
if (currentAnimation != null) {
currentAnimation.stop();
this.removeChild(currentAnimation);
Starling.current.juggler.remove(currentAnimation);
}
final newAnim:MovieClip = animations[name];
if (newAnim != null) {
currentAnimation = newAnim;
currentAnimation.currentFrame = frame;
currentAnimation.play();
currentLabel = name;
this.addChild(currentAnimation);
Starling.current.juggler.add(currentAnimation);
}
}
}
mc = new Anim();
mc.addAnimation("wait", Load.getInstance().ass.getTextures("wait_"), 7);
mc.addAnimation("move", Load.getInstance().ass.getTextures("move_"), 7);
this.addChild(mc);
mc.gotoAndPlay("wait", 0);
Now I only use one ‘MovieClip’ to play all the animations
package;
import openfl.Vector;
import openfl.utils.Dictionary;
import starling.core.Starling;
import starling.display.MovieClip;
import starling.textures.Texture;
import starling.display.Sprite;
import starling.events.EnterFrameEvent;
class De extends Sprite
{
private var _mc:MovieClip;
// ============ 你的写法,完全正确 ============
private var _anims:Dictionary<String, Dynamic> = new Dictionary();
// ===========================================
public var currentAnim:String = null;
private var _isSingleFrame:Bool = false;
private var _lastCheckedFrame:Int = -1;
private var _lastTriggerFrame:Int = -1;
public function new(textures:Vector<Texture>, fps:Float = 12)
{
super();
_mc = new MovieClip(textures, fps);
addChild(_mc);
Starling.current.juggler.add(_mc);
}
public function onEnterFrame(e:EnterFrameEvent = null):Void
{
if (currentAnim == null) return;
if (_isSingleFrame) return;
var anim:Dynamic = _anims[currentAnim];
if (anim == null) return;
var currentFrame:Int = _mc.currentFrame;
if (currentFrame != _lastCheckedFrame) {
_lastCheckedFrame = currentFrame;
_lastTriggerFrame = -1;
}
if (currentFrame == anim.end && _lastTriggerFrame != currentFrame) {
_lastTriggerFrame = currentFrame;
if (anim.loop) {
_mc.currentFrame = anim.start;
if (anim.start == anim.end) {
_mc.pause();
_isSingleFrame = true;
}
} else {
_mc.pause();
}
}
}
public function defineAnimations(configs:Array<Dynamic>):Void
{
_anims = new Dictionary<String, Dynamic>(); // 重置字典
for (cfg in configs) {
var anim:Dynamic = {
name: cfg.name,
start: cfg.start,
end: cfg.end,
fps: (cfg.fps != null) ? cfg.fps : _mc.fps,
loop: (cfg.loop != null) ? cfg.loop : true
};
_anims[cfg.name] = anim;
}
}
public function playAnim(name:String):Void
{
var anim:Dynamic = _anims[name];
if (anim == null) {
throw "未定义动画: " + name;
}
if (currentAnim == name) return;
currentAnim = name;
_mc.fps = anim.fps;
_mc.currentFrame = anim.start;
_lastCheckedFrame = anim.start;
_lastTriggerFrame = -1;
if (anim.start == anim.end) {
_isSingleFrame = true;
_mc.pause();
} else {
_isSingleFrame = false;
_mc.play();
}
}
public function update():Void
{
onEnterFrame();
}
public function get_movieClip():MovieClip return _mc;
}
mc = new De(Load.getInstance().ass.getTextures("ro1_"), 12);
mc.defineAnimations([
{name: "wait", start: 4, end: 4},
{
name: "move",
start: 2,
end: 7,
fps: 7,
loop: true
},
{
name: "att1",
start: 8,
end: 12,
fps: 7,
loop: true
},
{
name: "att2",
start: 13,
end: 17,
fps: 7,
loop: true
},
{name: "dx1", start: 18, end: 18},
{name: "dx2", start: 19, end: 19},
{name: "dx3", start: 20, end: 20},
{name: "dx4", start: 21, end: 21},
{name: "dx5", start: 22, end: 22},
]);
this.addChild(mc);
mc.playAnim("wait");
Which of the above two animation playback implementations do you prefer to use?
Each has its pros and cons. The single MovieClip approach is nice in that you’re not having to hide, or remove every MovieClip (animation) that’s not currently playing.
The downside however, is it’s less dynamic. You need to manually define the start and end frame attributes of every animation, which might change as a creative team updates assets. A single frame difference in one animation, could throw the whole lot out. That’d be some potential maintenance burden on you, or perhaps it’s manageable if assets are not likely to change at all.
I found that a single ‘MovieClip’ will drop frames when the FPS of the game card suddenly drops, such as “start==end” skipping frames without triggering, resulting in jumping to other segments of animation
So now I’m using the first method of multiple ‘MovieClips’ again
Ah good point. That’s probably the Juggler handling delta time which would result in frame skips if the rate drops too low.
There’s probably a few options around that, if you don’t mind handling frame advancement a bit more manually. Given you’re already using an EnterFrameEvent, you could advance the frame yourself there, and simply never add the MovieClip to the Juggler.
The caveats are:
- If you’re using framerates that don’t match your app, you’ll need to mathematically handle that.
- Animations will visually slow, when the framerate drops below normal.
- You miss out on the other features
Juggleroffers, like pausing and such.
…maybe just stick with the multiple MovieClips method ![]()
Hahaha, no more fuss, just use multiple ‘MovieClips’ directly
