refactor: animated trait uses Animation

This commit is contained in:
cha0s 2019-03-19 18:06:08 -05:00
parent 042e6ae071
commit 72459bc0a9

View File

@ -1,10 +1,14 @@
import {compose} from '@avocado/core';
import {Image, Sprite} from '@avocado/graphics';
import {hasGraphics, AnimationView} from '@avocado/graphics';
import {Vector} from '@avocado/math';
import {Animation} from '@avocado/timing';
import {simpleState, Trait} from '../trait';
const decorate = compose(
simpleState('currentAnimation', {
track: true,
}),
simpleState('currentFrame', {
track: true,
}),
@ -14,80 +18,132 @@ class AnimatedBase extends Trait {
static defaultParams() {
return {
frameCount: 0,
frameRate: 0,
frameSize: [0, 0],
image: '',
animations: {},
};
}
static defaultState() {
return {
currentAnimation: 'idle',
currentFrame: 0,
};
}
initialize() {
this.sprite = undefined;
this.frameCount = this.params.get('frameCount');
this.frameRate = this.params.get('frameRate');
this.frameSize = this.params.get('frameSize');
this.image = this.params.get('image');
this.frameCaret = this.frameRate;
this.loadSpriteImageIfPossible();
this.animations = {};
this.animationViews = undefined;
this.animationsPromise = undefined;
}
loadSpriteImageIfPossible() {
if (!this.entity.container) {
loadAnimations() {
if (this.animationsPromise) {
return;
}
if (!this.image) {
return;
const animations = this.params.get('animations');
const animationPromises = [];
// Load all animations.
for (const key in animations) {
const uri = animations[key];
const promise = Animation.load(uri).then((animation) => {
// Zip with key to make populating animations and views trivial.
return {animation, key};
});
animationPromises.push(promise);
}
Image.load(this.image).then((image) => {
this.sprite = new Sprite(image);
this.entity.container.addChild(this.sprite);
this.updateFrameRect();
this.animationsPromise = Promise.all(animationPromises);
// Store keyed animations.
this.animations = {};
this.animationsPromise.then((animations) => {
animations.forEach(({animation, key}) => {
this.animations[key] = animation;
// Listen for index changes.
animation.on('indexChanged', () => {
if (this.entity.currentAnimation === key) {
this.entity.currentFrame = animation.index;
}
});
});
});
}
updateFrameRect() {
if (!this.sprite) {
loadAnimationImagesIfPossible() {
if (!this.entity.container) {
return;
}
const direction = ('direction' in this.entity) ? this.entity.direction : 0;
this.sprite.sourceRectangle = [
this.entity.currentFrame * this.frameSize[0],
direction * this.frameSize[1],
this.frameSize[0],
this.frameSize[1],
];
if (!this.animationsPromise) {
return;
}
if (this.animationViews) {
return;
}
// Store keyed animation views.
this.animationViews = {};
this.animationsPromise.then((animations) => {
animations.forEach(({animation, key}) => {
this.animationViews[key] = new AnimationView(animation);
// Ensure animation is made visible upfront.
const isCurrentAnimation = key === this.entity.currentAnimation;
if (isCurrentAnimation) {
this.entity.container.addChild(this.animationViews[key]);
}
});
});
}
listeners() {
return {
currentAnimationChanged: (oldKey) => {
// Reset old animation.
const oldAnimation = this.animations[oldKey];
if (oldAnimation) {
oldAnimation.reset();
}
// Only client/graphics.
if (!this.animationViews) {
return;
}
// Swap the animation.
const oldAnimationView = this.animationViews[oldKey];
if (oldAnimationView) {
this.entity.container.removeChild(oldAnimationView);
}
const animationView = this.animationViews[this.entity.currentAnimation];
if (animationView) {
this.entity.container.addChild(animationView);
}
},
currentFrameChanged: () => {
this.updateFrameRect();
// Animation index from current entity frame.
const animation = this.animations[this.entity.currentAnimation];
if (!animation) {
return;
}
animation.index = this.entity.currentFrame;
},
directionChanged: () => {
this.updateFrameRect();
// All animations track direction.
for (const key in this.animations) {
const animation = this.animations[key];
animation.direction = this.entity.direction;
}
},
tick: (elapsed) => {
this.frameCaret -= elapsed;
if (this.frameCaret > 0) {
// Only tick current animation.
const animation = this.animations[this.entity.currentAnimation];
if (!animation) {
return;
}
const newFrame = (this.entity.currentFrame + 1) % this.frameCount;
this.entity.currentFrame = newFrame;
if (this.frameCaret < 0) {
this.frameCaret = this.frameRate - this.frameCaret;
}
animation.tick(elapsed);
},
traitAdded: (trait) => {
if ('graphical' !== trait.constructor.type()) {
if (-1 === [
'animated',
'graphical'
].indexOf(trait.constructor.type())) {
return;
}
this.loadSpriteImageIfPossible();
this.loadAnimations();
this.loadAnimationImagesIfPossible();
},
};
}