UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

318 lines (317 loc) 8.34 kB
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 EVENT_PLAY = "play"; static EVENT_PAUSE = "pause"; static EVENT_RESUME = "resume"; static EVENT_STOP = "stop"; static EVENT_END = "end"; static EVENT_LOOP = "loop"; _evtSetMeshes = null; _component; _frame = 0; _sprite = null; _spriteAsset = null; _playing = false; _paused = false; _time = 0; name; fps = 0; loop = false; constructor(component, data) { super(); this._component = component; this.fps = data.fps || 0; this.loop = data.loop || false; this.name = data.name; this.spriteAsset = data.spriteAsset; } 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.removeFromLayers(); } 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.addToLayers(); } } 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; } // When sprite asset is added bind it _onSpriteAssetAdded(asset) { this._component.system.app.assets.off(`add:${asset.id}`, this._onSpriteAssetAdded, this); if (this._spriteAsset === asset.id) { this._bindSpriteAsset(asset); } } // Hook up event handlers on sprite 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); } } // When sprite asset is loaded make sure the texture atlas asset is loaded too // If so then set the sprite, otherwise wait for the atlas to be loaded first _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; } } } // When atlas is loaded try to reset the sprite asset _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; } // If the meshes are re-created make sure // we update them in the mesh instance _onSpriteMeshesChange() { if (this._component.currentClip === this) { this._component._showFrame(this.frame); } } // Update frame if ppu changes for 9-sliced sprites _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 };