pxt-common-packages
Version:
Microsoft MakeCode (PXT) common packages
179 lines (156 loc) • 5.15 kB
text/typescript
/*
Animation library for sprites
*/
namespace animation {
//Handles all the updates
let animations: Animation[];
let animationStateStack: {
state: Animation[],
scene: scene.Scene
}[];
game.addScenePushHandler(oldScene => {
if (animations) {
if (!animationStateStack) animationStateStack = [];
animationStateStack.push({
state: animations,
scene: oldScene
});
animations = undefined;
}
});
game.addScenePopHandler(() => {
const scene = game.currentScene();
animations = undefined;
if (animationStateStack && animationStateStack.length) {
const nextState = animationStateStack.pop();
if (nextState.scene == scene) {
animations = nextState.state;
} else {
animationStateStack.push(nextState);
}
}
});
export class Animation {
sprites: Sprite[];
frames: Image[];
index: number;
interval: number;
action: number;
lastTime: number;
constructor(action: number, interval: number) {
this.interval = interval;
this.index = -1;
this.action = action;
this.frames = [];
this.sprites = [];
this.lastTime = control.millis();
this._init();
}
_init() {
if (!animations) {
animations = [];
game.eventContext().registerFrameHandler(scene.ANIMATION_UPDATE_PRIORITY, () => {
animations.forEach(anim => anim.update());
});
}
animations.push(this);
}
update() {
let currentTime = control.millis();
let dt = currentTime - this.lastTime;
if (dt >= this.interval && this.frames.length) {
this.index = (this.index + 1) % this.frames.length;
this.lastTime = currentTime;
}
this.sprites = this.sprites.filter(sprite => !(sprite.flags & sprites.Flag.Destroyed));
this.sprites.forEach(sprite => {
if (sprite._action === this.action) {
let newImage = this.getImage();
//Update only if the image has changed
if (sprite.image !== newImage) {
sprite.setImage(newImage);
}
}
});
}
getImage() {
return this.frames[this.index];
}
getAction() {
return this.action;
}
getInterval() {
return this.interval;
}
setInterval(interval: number) {
this.interval = interval;
}
/**
* Add an image frame to an animation
*/
//% blockId=addAnimationFrame
//% block="add frame $frame=screen_image_picker to $this=variables_get(anim)"
//% group="Advanced"
//% weight=40
//% help=animation/add-animation-frame
addAnimationFrame(frame: Image) {
this.frames[++this.index] = frame;
}
registerSprite(sprite: Sprite) {
if (this.sprites.indexOf(sprite) === -1) {
this.sprites.push(sprite);
}
}
}
//% shim=ENUM_GET
//% blockId=action_enum_shim
//% block="%arg"
//% group="Advanced"
//% enumName="ActionKind"
//% enumMemberName="action"
//% enumPromptHint="e.g. Walking, Idle, Jumping, ..."
//% enumInitialMembers="Walking, Idle, Jumping"
//% weight=10
export function _actionEnumShim(arg: number) {
// This function should do nothing, but must take in a single
// argument of type number and return a number value.
return arg;
}
/**
* Create an animation
*/
//% blockId=createAnimation
//% block="create animation of $action=action_enum_shim with interval $interval ms"
//% group="Advanced"
//% interval.defl=1000
//% blockSetVariable="anim"
//% weight=50
//% help=animation/create-animation
export function createAnimation(action: number, interval: number) {
return new Animation(action, interval);
}
/**
* Attach an animation to a sprite
*/
//% blockId=attachAnimation
//% block="attach animation $set=variables_get(anim) to sprite $sprite=variables_get(mySprite)"
//% sprite.defl=mySprite
//% group="Advanced"
//% weight=30
//% help=animation/attach-animation
export function attachAnimation(sprite: Sprite, set: Animation) {
set.registerSprite(sprite);
}
/**
* Set an animation action to a sprite
*/
//% blockId=setAction
//% block="activate animation $action=action_enum_shim on $sprite=variables_get(mySprite)"
//% sprite.defl=mySprite
//% group="Advanced"
//% weight=20
//% help=animation/set-action
export function setAction(sprite: Sprite, action: number) {
sprite._action = action;
}
}