playcanvas
Version:
PlayCanvas WebGL game engine
317 lines (314 loc) • 8.69 kB
JavaScript
import { EventHandler } from '../../../core/event-handler.js';
import { math } from '../../../core/math/math.js';
import { Asset } from '../../asset/asset.js';
import { SPRITE_RENDERMODE_SIMPLE } from '../../../scene/constants.js';
class SpriteAnimationClip extends EventHandler {
static{
this.EVENT_PLAY = 'play';
}
static{
this.EVENT_PAUSE = 'pause';
}
static{
this.EVENT_RESUME = 'resume';
}
static{
this.EVENT_STOP = 'stop';
}
static{
this.EVENT_END = 'end';
}
static{
this.EVENT_LOOP = 'loop';
}
constructor(component, data){
super(), this._evtSetMeshes = null;
this._component = component;
this._frame = 0;
this._sprite = null;
this._spriteAsset = null;
this.spriteAsset = data.spriteAsset;
this.name = data.name;
this.fps = data.fps || 0;
this.loop = data.loop || false;
this._playing = false;
this._paused = false;
this._time = 0;
}
get duration() {
if (this._sprite) {
const fps = this.fps || Number.MIN_VALUE;
return this._sprite.frameKeys.length / Math.abs(fps);
}
return 0;
}
set frame(value) {
this._setFrame(value);
const fps = this.fps || Number.MIN_VALUE;
this._setTime(this._frame / fps);
}
get frame() {
return this._frame;
}
get isPaused() {
return this._paused;
}
get isPlaying() {
return this._playing;
}
set sprite(value) {
if (this._sprite) {
this._evtSetMeshes?.off();
this._evtSetMeshes = null;
this._sprite.off('set:pixelsPerUnit', this._onSpritePpuChanged, this);
this._sprite.off('set:atlas', this._onSpriteMeshesChange, this);
if (this._sprite.atlas) {
this._sprite.atlas.off('set:texture', this._onSpriteMeshesChange, this);
}
}
this._sprite = value;
if (this._sprite) {
this._evtSetMeshes = this._sprite.on('set:meshes', this._onSpriteMeshesChange, this);
this._sprite.on('set:pixelsPerUnit', this._onSpritePpuChanged, this);
this._sprite.on('set:atlas', this._onSpriteMeshesChange, this);
if (this._sprite.atlas) {
this._sprite.atlas.on('set:texture', this._onSpriteMeshesChange, this);
}
}
if (this._component.currentClip === this) {
let mi;
if (!value || !value.atlas) {
mi = this._component._meshInstance;
if (mi) {
mi.deleteParameter('texture_emissiveMap');
mi.deleteParameter('texture_opacityMap');
}
this._component._hideModel();
} else {
if (value.atlas.texture) {
mi = this._component._meshInstance;
if (mi) {
mi.setParameter('texture_emissiveMap', value.atlas.texture);
mi.setParameter('texture_opacityMap', value.atlas.texture);
}
if (this._component.enabled && this._component.entity.enabled) {
this._component._showModel();
}
}
if (this.time && this.fps) {
this.time = this.time;
} else {
this.frame = this.frame;
}
}
}
}
get sprite() {
return this._sprite;
}
set spriteAsset(value) {
const assets = this._component.system.app.assets;
let id = value;
if (value instanceof Asset) {
id = value.id;
}
if (this._spriteAsset !== id) {
if (this._spriteAsset) {
const prev = assets.get(this._spriteAsset);
if (prev) {
this._unbindSpriteAsset(prev);
}
}
this._spriteAsset = id;
if (this._spriteAsset) {
const asset = assets.get(this._spriteAsset);
if (!asset) {
this.sprite = null;
assets.on(`add:${this._spriteAsset}`, this._onSpriteAssetAdded, this);
} else {
this._bindSpriteAsset(asset);
}
} else {
this.sprite = null;
}
}
}
get spriteAsset() {
return this._spriteAsset;
}
set time(value) {
this._setTime(value);
if (this._sprite) {
this.frame = Math.min(this._sprite.frameKeys.length - 1, Math.floor(this._time * Math.abs(this.fps)));
} else {
this.frame = 0;
}
}
get time() {
return this._time;
}
_onSpriteAssetAdded(asset) {
this._component.system.app.assets.off(`add:${asset.id}`, this._onSpriteAssetAdded, this);
if (this._spriteAsset === asset.id) {
this._bindSpriteAsset(asset);
}
}
_bindSpriteAsset(asset) {
asset.on('load', this._onSpriteAssetLoad, this);
asset.on('remove', this._onSpriteAssetRemove, this);
if (asset.resource) {
this._onSpriteAssetLoad(asset);
} else {
this._component.system.app.assets.load(asset);
}
}
_unbindSpriteAsset(asset) {
if (!asset) {
return;
}
asset.off('load', this._onSpriteAssetLoad, this);
asset.off('remove', this._onSpriteAssetRemove, this);
if (asset.resource && !asset.resource.atlas) {
this._component.system.app.assets.off(`load:${asset.data.textureAtlasAsset}`, this._onTextureAtlasLoad, this);
}
}
_onSpriteAssetLoad(asset) {
if (!asset.resource) {
this.sprite = null;
} else {
if (!asset.resource.atlas) {
const atlasAssetId = asset.data.textureAtlasAsset;
const assets = this._component.system.app.assets;
assets.off(`load:${atlasAssetId}`, this._onTextureAtlasLoad, this);
assets.once(`load:${atlasAssetId}`, this._onTextureAtlasLoad, this);
} else {
this.sprite = asset.resource;
}
}
}
_onTextureAtlasLoad(atlasAsset) {
const spriteAsset = this._spriteAsset;
if (spriteAsset instanceof Asset) {
this._onSpriteAssetLoad(spriteAsset);
} else {
this._onSpriteAssetLoad(this._component.system.app.assets.get(spriteAsset));
}
}
_onSpriteAssetRemove(asset) {
this.sprite = null;
}
_onSpriteMeshesChange() {
if (this._component.currentClip === this) {
this._component._showFrame(this.frame);
}
}
_onSpritePpuChanged() {
if (this._component.currentClip === this) {
if (this.sprite.renderMode !== SPRITE_RENDERMODE_SIMPLE) {
this._component._showFrame(this.frame);
}
}
}
_update(dt) {
if (this.fps === 0) return;
if (!this._playing || this._paused || !this._sprite) return;
const dir = this.fps < 0 ? -1 : 1;
const time = this._time + dt * this._component.speed * dir;
const duration = this.duration;
const end = time > duration || time < 0;
this._setTime(time);
let frame = this.frame;
if (this._sprite) {
frame = Math.floor(this._sprite.frameKeys.length * this._time / duration);
} else {
frame = 0;
}
if (frame !== this._frame) {
this._setFrame(frame);
}
if (end) {
if (this.loop) {
this.fire('loop');
this._component.fire('loop', this);
} else {
this._playing = false;
this._paused = false;
this.fire('end');
this._component.fire('end', this);
}
}
}
_setTime(value) {
this._time = value;
const duration = this.duration;
if (this._time < 0) {
if (this.loop) {
this._time = this._time % duration + duration;
} else {
this._time = 0;
}
} else if (this._time > duration) {
if (this.loop) {
this._time %= duration;
} else {
this._time = duration;
}
}
}
_setFrame(value) {
if (this._sprite) {
this._frame = math.clamp(value, 0, this._sprite.frameKeys.length - 1);
} else {
this._frame = value;
}
if (this._component.currentClip === this) {
this._component._showFrame(this._frame);
}
}
_destroy() {
if (this._spriteAsset) {
const assets = this._component.system.app.assets;
this._unbindSpriteAsset(assets.get(this._spriteAsset));
}
if (this._sprite) {
this.sprite = null;
}
if (this._spriteAsset) {
this.spriteAsset = null;
}
}
play() {
if (this._playing) {
return;
}
this._playing = true;
this._paused = false;
this.frame = 0;
this.fire('play');
this._component.fire('play', this);
}
pause() {
if (!this._playing || this._paused) {
return;
}
this._paused = true;
this.fire('pause');
this._component.fire('pause', this);
}
resume() {
if (!this._paused) return;
this._paused = false;
this.fire('resume');
this._component.fire('resume', this);
}
stop() {
if (!this._playing) return;
this._playing = false;
this._paused = false;
this._time = 0;
this.frame = 0;
this.fire('stop');
this._component.fire('stop', this);
}
}
export { SpriteAnimationClip };