//-----------------------------------------------------------------------------
// Sprite_Animation
//
// The sprite for displaying an animation.
/**
* The sprite for displaying an animation.
*
* @class
* @extends Sprite
*/
function Sprite_Animation() {
this.initialize(...arguments);
}
Sprite_Animation.prototype = Object.create(Sprite.prototype);
Sprite_Animation.prototype.constructor = Sprite_Animation;
Sprite_Animation.prototype.initialize = function() {
Sprite.prototype.initialize.call(this);
this.initMembers();
};
/**
* Initialize sprite variables
*/
Sprite_Animation.prototype.initMembers = function() {
this._targets = [];
this._animation = null;
this._mirror = false;
this._delay = 0;
this._previous = null;
this._effect = null;
this._handle = null;
this._playing = false;
this._started = false;
this._frameIndex = 0;
this._maxTimingFrames = 0;
this._flashColor = [0, 0, 0, 0];
this._flashDuration = 0;
this._viewportSize = 4096;
this.z = 8;
};
Sprite_Animation.prototype.destroy = function(options) {
Sprite.prototype.destroy.call(this, options);
if (this._handle) {
this._handle.stop();
}
this._effect = null;
this._handle = null;
this._playing = false;
this._started = false;
};
// prettier-ignore
/**
* Set up the animation sprite
*
* @param {Array] targets - The targets of the animation
* @param {Object} animation - The animation object to play
* @param {boolean} mirror - If the animation should mirror
* @param {number} delay - Frame delay for the animation
* @param {Sprite_Animation|Sprite_AnimationMV|null} previous - The previous animation sprite
*/
Sprite_Animation.prototype.setup = function(
targets, animation, mirror, delay, previous
) {
this._targets = targets;
this._animation = animation;
this._mirror = mirror;
this._delay = delay;
this._previous = previous;
this._effect = EffectManager.load(animation.effectName);
this._playing = true;
const timings = animation.soundTimings.concat(animation.flashTimings);
for (const timing of timings) {
if (timing.frame > this._maxTimingFrames) {
this._maxTimingFrames = timing.frame;
}
}
};
Sprite_Animation.prototype.update = function() {
Sprite.prototype.update.call(this);
if (this._delay > 0) {
this._delay--;
} else if (this._playing) {
if (!this._started && this.canStart()) {
if (this._effect) {
if (this._effect.isLoaded) {
this._handle = Graphics.effekseer.play(this._effect);
this._started = true;
} else {
EffectManager.checkErrors();
}
} else {
this._started = true;
}
}
if (this._started) {
this.updateEffectGeometry();
this.updateMain();
this.updateFlash();
}
}
};
/**
* Check if the animation can start playing
*
* @return {boolean} True if the animation can start
*/
Sprite_Animation.prototype.canStart = function() {
if (this._previous && this.shouldWaitForPrevious()) {
return !this._previous.isPlaying();
} else {
return true;
}
};
/**
* Check if the animation should wait for the previous animation to finish
*
* @return {boolean} True if animation should wait for the previous animation to finish
*/
Sprite_Animation.prototype.shouldWaitForPrevious = function() {
// [Note] Older versions of Effekseer were very heavy on some mobile
// devices. We don't need this anymore.
return false;
};
/**
* Update for effect geometry
*/
Sprite_Animation.prototype.updateEffectGeometry = function() {
const scale = this._animation.scale / 100;
const r = Math.PI / 180;
const rx = this._animation.rotation.x * r;
const ry = this._animation.rotation.y * r;
const rz = this._animation.rotation.z * r;
if (this._handle) {
this._handle.setLocation(0, 0, 0);
this._handle.setRotation(rx, ry, rz);
this._handle.setScale(scale, scale, scale);
this._handle.setSpeed(this._animation.speed / 100);
}
};
/**
* Update for main effects
*/
Sprite_Animation.prototype.updateMain = function() {
this.processSoundTimings();
this.processFlashTimings();
this._frameIndex++;
this.checkEnd();
};
/**
* Handles playing sound effects at the right frame
*/
Sprite_Animation.prototype.processSoundTimings = function() {
for (const timing of this._animation.soundTimings) {
if (timing.frame === this._frameIndex) {
AudioManager.playSe(timing.se);
}
}
};
/**
* Handles showing flashes at the right frame
*/
Sprite_Animation.prototype.processFlashTimings = function() {
for (const timing of this._animation.flashTimings) {
if (timing.frame === this._frameIndex) {
this._flashColor = timing.color.clone();
this._flashDuration = timing.duration;
}
}
};
/**
* Check if the animation has ended. Sets playing to false if ended
*/
Sprite_Animation.prototype.checkEnd = function() {
if (
this._frameIndex > this._maxTimingFrames &&
this._flashDuration === 0 &&
!(this._handle && this._handle.exists)
) {
this._playing = false;
}
};
/**
* Update for flash effects
*/
Sprite_Animation.prototype.updateFlash = function() {
if (this._flashDuration > 0) {
const d = this._flashDuration--;
this._flashColor[3] *= (d - 1) / d;
for (const target of this._targets) {
target.setBlendColor(this._flashColor);
}
}
};
/**
* Check if the animation is playing
*
* @return {boolean} True if animation is currently playing
*/
Sprite_Animation.prototype.isPlaying = function() {
return this._playing;
};
/**
* Sets the rotation of the animation
*
* @param {number} x - The x value of the rotation
* @param {number} y - The y value of the rotation
* @param {number} z - The z value of the rotation
*/
Sprite_Animation.prototype.setRotation = function(x, y, z) {
if (this._handle) {
this._handle.setRotation(x, y, z);
}
};
Sprite_Animation.prototype._render = function(renderer) {
if (this._targets.length > 0 && this._handle && this._handle.exists) {
this.onBeforeRender(renderer);
this.setProjectionMatrix(renderer);
this.setCameraMatrix(renderer);
this.setViewport(renderer);
Graphics.effekseer.beginDraw();
Graphics.effekseer.drawHandle(this._handle);
Graphics.effekseer.endDraw();
this.resetViewport(renderer);
this.onAfterRender(renderer);
}
};
/**
* Sets the projection matrix for the animation
*
* @param {PIXI.Renderer} The renderer for the game
*/
Sprite_Animation.prototype.setProjectionMatrix = function(renderer) {
const x = this._mirror ? -1 : 1;
const y = -1;
const p = -(this._viewportSize / renderer.view.height);
// prettier-ignore
Graphics.effekseer.setProjectionMatrix([
x, 0, 0, 0,
0, y, 0, 0,
0, 0, 1, p,
0, 0, 0, 1,
]);
};
/**
* Sets the camera matrix for the animation
*/
Sprite_Animation.prototype.setCameraMatrix = function(/*renderer*/) {
// prettier-ignore
Graphics.effekseer.setCameraMatrix([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, -10, 1
]);
};
/**
* Sets the viewport for the animation
*
* @param {PIXI.Renderer} The renderer for the game
*/
Sprite_Animation.prototype.setViewport = function(renderer) {
const vw = this._viewportSize;
const vh = this._viewportSize;
const vx = this._animation.offsetX - vw / 2;
const vy = this._animation.offsetY - vh / 2;
const pos = this.targetPosition(renderer);
renderer.gl.viewport(vx + pos.x, vy + pos.y, vw, vh);
};
/**
* Sets the target position for the animation
*
* @param {PIXI.Renderer} The renderer for the game
* @return {Point} The point representing the x and y coordinates of the animation
*/
Sprite_Animation.prototype.targetPosition = function(renderer) {
const pos = new Point();
if (this._animation.displayType === 2) {
pos.x = renderer.view.width / 2;
pos.y = renderer.view.height / 2;
} else {
for (const target of this._targets) {
const tpos = this.targetSpritePosition(target);
pos.x += tpos.x;
pos.y += tpos.y;
}
pos.x /= this._targets.length;
pos.y /= this._targets.length;
}
return pos;
};
/**
* Gets the target sprite's position
*
* @param {Sprite} The sprite to show the animation on
* @return {Point} A point representing the x/y coordinates where the sprite is
*/
Sprite_Animation.prototype.targetSpritePosition = function(sprite) {
const point = new Point(0, -sprite.height / 2);
if (this._animation.alignBottom) {
point.y = 0;
}
sprite.updateTransform();
return sprite.worldTransform.apply(point);
};
/**
* Resets the viewport
*
* @param {PIXI.Renderer} The renderer for the game
*/
Sprite_Animation.prototype.resetViewport = function(renderer) {
renderer.gl.viewport(0, 0, renderer.view.width, renderer.view.height);
};
/**
* Handling before render occurs
*
* @param {PIXI.Renderer} The renderer for the game
*/
Sprite_Animation.prototype.onBeforeRender = function(renderer) {
renderer.batch.flush();
renderer.geometry.reset();
};
/**
* Handling after render occurs
*
* @param {PIXI.Renderer} The renderer for the game
*/
Sprite_Animation.prototype.onAfterRender = function(renderer) {
renderer.texture.reset();
renderer.geometry.reset();
renderer.state.reset();
renderer.shader.reset();
renderer.framebuffer.reset();
};