UNPKG

dragonbones-runtime

Version:

the tools to build dragonbones file for diffrent framework

1,243 lines (1,132 loc) 59.3 kB
/** * The MIT License (MIT) * * Copyright (c) 2012-2017 DragonBones team and other contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ namespace dragonBones { /** * @private */ type GroupConfig = { name: string, version: number, position: number[], display: DisplayConfig[], frame: FrameConfig[], movie: MovieConfig[], animation: MovieConfig[], // 兼容旧数据 // Runtime offset: number, arrayBuffer: ArrayBuffer, displayFrameArray: Int16Array, rectangleArray: Float32Array, transformArray: Float32Array, colorArray: Int16Array, textures: egret.Texture[] }; /** * @private */ type DisplayConfig = { name: string, type: DisplayType, textureIndex?: number, regionIndex?: number, // Runtime texture: egret.Texture }; /** * @private */ type ActionAndEventConfig = { type: ActionType, name: string, data?: any, slot?: string }; /** * @private */ type MovieConfig = { name: string, frameRate: number, type?: number, action?: string, isNested?: boolean, hasChildAnimation?: boolean, // 兼容旧数据 slot: SlotConfig[], clip: ClipConfig[] }; /** * @private */ type SlotConfig = { name: string, blendMode?: BlendMode, action?: string }; /** * @private */ type ClipConfig = { name: string, playTimes: number, duration: number, scale: number, cacheTimeToFrameScale: number, p: number, s: number, frame: number[], // Runtime cacheRectangles: egret.Rectangle[] }; /** * @private */ type FrameConfig = { prev: number, next: number, position: number, actionAndEvent: ActionAndEventConfig[] }; /** * @private */ type CreateMovieHelper = { groupName: string, movieName: string, groupConfig: GroupConfig, movieConfig: MovieConfig }; /** * @private */ let _helpRectangle: egret.Rectangle = new egret.Rectangle(); /** * @private */ let _helpMatrix: egret.Matrix = new egret.Matrix(); /** * @private */ let _groupConfigMap: Map<GroupConfig> = {}; /** * @private */ function _findObjectInArray<T extends { name: string }>(array: T[], name: string): T | null { for (let i = 0, l = array.length; i < l; ++i) { const data = array[i]; if (data.name === name) { return data; } } return null; } /** * @private */ function _fillCreateMovieHelper(createMovieHelper: CreateMovieHelper): boolean { if (createMovieHelper.groupName) { const groupConfig = _groupConfigMap[createMovieHelper.groupName]; if (groupConfig) { const movieConfig = _findObjectInArray(groupConfig.movie || groupConfig.animation, createMovieHelper.movieName); if (movieConfig) { createMovieHelper.groupConfig = groupConfig; createMovieHelper.movieConfig = movieConfig; return true; } } } if (!createMovieHelper.groupName) { // || autoSearch Will be search all data, if do not give a data name or the autoSearch is true. for (let groupName in _groupConfigMap) { const groupConfig = _groupConfigMap[groupName]; if (!createMovieHelper.groupName) { // || groupConfig.autoSearch const movieConfig = _findObjectInArray(groupConfig.movie || groupConfig.animation, createMovieHelper.movieName); if (movieConfig) { createMovieHelper.groupName = groupName; createMovieHelper.groupConfig = groupConfig; createMovieHelper.movieConfig = movieConfig; return true; } } } } return false; } /** * 是否包含指定名称的动画组。 * @param groupName 动画组的名称。 * @version DragonBones 4.7 * @language zh_CN */ export function hasMovieGroup(groupName: string): boolean { return groupName in _groupConfigMap; } /** * 添加动画组。 * @param groupData 动画二进制数据。 * @param textureAtlas 贴图集或贴图集列表。 * @param groupName 为动画组指定一个名称,如果未设置,则使用数据中的名称。 * @version DragonBones 4.7 * @language zh_CN */ export function addMovieGroup(groupData: ArrayBuffer, textureAtlas: egret.Texture | egret.Texture[], groupName: string | null = null): void { if (groupData) { const byteArray = new egret.ByteArray(groupData); byteArray.endian = egret.Endian.LITTLE_ENDIAN; byteArray.position = 8; // TODO format const groupConfig = <GroupConfig>JSON.parse(byteArray.readUTF()); groupConfig.offset = byteArray.position; groupConfig.arrayBuffer = groupData; groupConfig.textures = []; const p = groupConfig.offset % 4; if (p) { groupConfig.offset += 4 - p; } for (let i = 0, l = groupConfig.position.length; i < l; i += 3) { switch (i / 3) { case 1: groupConfig.displayFrameArray = new Int16Array(groupConfig.arrayBuffer, groupConfig.offset + groupConfig.position[i], groupConfig.position[i + 1] / groupConfig.position[i + 2]); break; case 2: groupConfig.rectangleArray = new Float32Array(groupConfig.arrayBuffer, groupConfig.offset + groupConfig.position[i], groupConfig.position[i + 1] / groupConfig.position[i + 2]); break; case 3: groupConfig.transformArray = new Float32Array(groupConfig.arrayBuffer, groupConfig.offset + groupConfig.position[i], groupConfig.position[i + 1] / groupConfig.position[i + 2]); break; case 4: groupConfig.colorArray = new Int16Array(groupConfig.arrayBuffer, groupConfig.offset + groupConfig.position[i], groupConfig.position[i + 1] / groupConfig.position[i + 2]); break; } } groupName = groupName || groupConfig.name; if (_groupConfigMap[groupName]) { console.warn("Replace group: " + groupName); } _groupConfigMap[groupName] = groupConfig; // if (textureAtlas instanceof Array) { for (let i = 0, l = textureAtlas.length; i < l; ++i) { const texture = textureAtlas[i]; groupConfig.textures.push(texture); } } else { groupConfig.textures.push(textureAtlas); } } else { throw new Error(); } } /** * 移除动画组。 * @param groupName 动画组的名称。 * @version DragonBones 4.7 * @language zh_CN */ export function removeMovieGroup(groupName: string): void { const groupConfig = _groupConfigMap[groupName]; if (groupConfig) { delete _groupConfigMap[groupName]; } } /** * 移除所有的动画组。 * @param groupName 动画组的名称。 * @version DragonBones 4.7 * @language zh_CN */ export function removeAllMovieGroup(): void { for (let i in _groupConfigMap) { delete _groupConfigMap[i]; } } /** * 创建一个动画。 * @param movieName 动画的名称。 * @param groupName 动画组的名称,如果未设置,将检索所有的动画组,当多个动画组中包含同名的动画时,可能无法创建出准确的动画。 * @version DragonBones 4.7 * @language zh_CN */ export function buildMovie(movieName: string, groupName: string | null = null): Movie | null { const createMovieHelper = <CreateMovieHelper>{ movieName: movieName, groupName: groupName }; if (_fillCreateMovieHelper(createMovieHelper)) { const movie = new Movie(createMovieHelper); movie.clock = dragonBones.EgretFactory.factory.clock; return movie; } else { console.warn("No movie named: " + movieName); } return null; } /** * 获取指定动画组内包含的所有动画名称。 * @param groupName 动画组的名称。 * @version DragonBones 4.7 * @language zh_CN */ export function getMovieNames(groupName: string): string[] | null { const groupConfig = _groupConfigMap[groupName]; if (groupConfig) { const movieNameGroup = <string[]>[]; const movie = groupConfig.movie || groupConfig.animation; for (let i = 0, l = movie.length; i < l; ++i) { movieNameGroup.push(movie[i].name); } return movieNameGroup; } else { console.warn("No group named: " + groupName); } return null; } /** * 动画事件。 * @version DragonBones 4.7 * @language zh_CN */ export class MovieEvent extends egret.Event { /** * 动画剪辑开始播放。 * @version DragonBones 4.7 * @language zh_CN */ public static START: string = "start"; /** * 动画剪辑循环播放一次完成。 * @version DragonBones 4.7 * @language zh_CN */ public static LOOP_COMPLETE: string = "loopComplete"; /** * 动画剪辑播放完成。 * @version DragonBones 4.7 * @language zh_CN */ public static COMPLETE: string = "complete"; /** * 动画剪辑帧事件。 * @version DragonBones 4.7 * @language zh_CN */ public static FRAME_EVENT: string = "frameEvent"; /** * 动画剪辑声音事件。 * @version DragonBones 4.7 * @language zh_CN */ public static SOUND_EVENT: string = "soundEvent"; /** * 事件名称。 (帧标签的名称或声音的名称) * @version DragonBones 4.7 * @language zh_CN */ public name: string = ""; /** * 发出事件的插槽名称。 * @version DragonBones 4.7 * @language zh_CN */ public slotName: string = ""; /** * 发出事件的动画剪辑名称。 * @version DragonBones 4.7 * @language zh_CN */ public clipName: string = ""; /** * 发出事件的动画。 * @version DragonBones 4.7 * @language zh_CN */ public movie: Movie; /** * @private */ public constructor(type: string) { super(type); } //========================================= // 兼容旧数据 /** * @private */ public get armature(): any { return this.movie; } /** * @private */ public get bone(): any { return null; } /** * @private */ public get animationState(): any { return { name: this.clipName }; } /** * @private */ public get frameLabel(): any { return this.name; } /** * @private */ public get movementID(): any { return this.clipName; } //========================================= } /** * @private */ class MovieSlot extends egret.HashObject { public displayIndex: number = -1; public colorIndex: number = -1; public transformIndex: number = -1; public rawDisplay: egret.Bitmap = new egret.Bitmap(); public childMovies: Map<Movie> = {}; public config: SlotConfig; public displayConfig: DisplayConfig | null = null; public display: egret.DisplayObject; public childMovie: Movie | null = null; public colorFilter: egret.ColorMatrixFilter | null = null; public constructor(slotConfig: SlotConfig) { super(); this.display = this.rawDisplay; this.config = slotConfig; this.rawDisplay.name = this.config.name; if (!this.config.blendMode) { this.config.blendMode = BlendMode.Normal; } } public dispose(): void { this.rawDisplay = null as any; this.childMovies = null as any; this.config = null as any; this.displayConfig = null as any; this.display = null as any; this.childMovie = null as any; this.colorFilter = null as any; } } /** * 通过读取缓存的二进制动画数据来更新动画,具有良好的运行性能,同时对内存的占用也非常低。 * @see dragonBones.buildMovie * @version DragonBones 4.7 * @language zh_CN */ export class Movie extends egret.DisplayObjectContainer implements IAnimatable { private static _cleanBeforeRender(): void { } /** * 动画的播放速度。 [(-N~0): 倒转播放, 0: 停止播放, (0~1): 慢速播放, 1: 正常播放, (1~N): 快速播放] * @default 1 * @version DragonBones 4.7 * @language zh_CN */ public timeScale: number = 1; /** * 动画剪辑的播放速度。 [(-N~0): 倒转播放, 0: 停止播放, (0~1): 慢速播放, 1: 正常播放, (1~N): 快速播放] * (当再次播放其他动画剪辑时,此值将被重置为 1) * @default 1 * @version DragonBones 4.7 * @language zh_CN */ public clipTimeScale: number = 1; private _batchEnabled: boolean = true; private _isLockDispose: boolean = false; private _isDelayDispose: boolean = false; private _isStarted: boolean = false; private _isPlaying: boolean = false; private _isReversing: boolean = false; private _isCompleted: boolean = false; private _playTimes: number = 0; private _time: number = 0; private _currentTime: number = 0; private _currentPlayTimes: number = 0; private _cacheFrameIndex: number = 0; private _frameSize: number = 0; private _cacheRectangle: egret.Rectangle | null = null; private _clock: WorldClock | null = null; private _groupConfig: GroupConfig; private _config: MovieConfig; private _clipConfig: ClipConfig; private _currentFrameConfig: FrameConfig | null = null; private _clipArray: Int16Array; private _clipNames: string[] = []; private _slots: MovieSlot[] = []; private _childMovies: Movie[] = []; /** * @internal * @private */ public constructor(createMovieHelper: any) { super(); this._groupConfig = (createMovieHelper as CreateMovieHelper).groupConfig; this._config = (createMovieHelper as CreateMovieHelper).movieConfig; this._batchEnabled = !(this._config.isNested || this._config.hasChildAnimation); if (this._batchEnabled) { this.$renderNode = new egret.sys.GroupNode(); this.$renderNode.cleanBeforeRender = Movie._cleanBeforeRender; } this._clipNames.length = 0; for (let i = 0, l = this._config.clip.length; i < l; ++i) { this._clipNames.push(this._config.clip[i].name); } for (let i = 0, l = this._config.slot.length; i < l; ++i) { const slot = new MovieSlot(this._config.slot[i]); this._updateSlotBlendMode(slot); this._slots.push(slot); if (this._batchEnabled) { (<egret.sys.GroupNode>this.$renderNode).addNode(slot.rawDisplay.$renderNode); } else { this.addChild(slot.rawDisplay); } } this._frameSize = (1 + 1) * this._slots.length; // displayFrame, transformFrame. this.name = this._config.name; this.play(); this.advanceTime(0.000001); this.stop(); } private _configToEvent(config: ActionAndEventConfig, event: MovieEvent): void { event.movie = this; event.clipName = this._clipConfig.name; event.name = config.name; event.slotName = config.slot || ""; } private _onCrossFrame(frameConfig: FrameConfig): void { for (let i = 0, l = frameConfig.actionAndEvent.length; i < l; ++i) { const actionAndEvent = frameConfig.actionAndEvent[i]; if (actionAndEvent) { switch (actionAndEvent.type) { case ActionType.Sound: if (EgretFactory.factory.soundEventManager.hasEventListener(MovieEvent.SOUND_EVENT)) { const event = egret.Event.create(MovieEvent, MovieEvent.SOUND_EVENT); this._configToEvent(actionAndEvent, event); EgretFactory.factory.soundEventManager.dispatchEvent(event); egret.Event.release(event); } break; case ActionType.Frame: if (this.hasEventListener(MovieEvent.FRAME_EVENT)) { const event = egret.Event.create(MovieEvent, MovieEvent.FRAME_EVENT); this._configToEvent(actionAndEvent, event); this.dispatchEvent(event); egret.Event.release(event); } break; case ActionType.Play: if (actionAndEvent.slot) { const slot = this._getSlot(actionAndEvent.slot); if (slot && slot.childMovie) { slot.childMovie.play(actionAndEvent.name); } } else { this.play(actionAndEvent.name); } break; } } } } private _updateSlotBlendMode(slot: MovieSlot): void { let blendMode = ""; switch (slot.config.blendMode) { case BlendMode.Normal: blendMode = egret.BlendMode.NORMAL; break; case BlendMode.Add: blendMode = egret.BlendMode.ADD; break; case BlendMode.Erase: blendMode = egret.BlendMode.ERASE; break; default: break; } if (blendMode) { if (this._batchEnabled) { // RenderNode display. (<egret.sys.BitmapNode>slot.display.$renderNode).blendMode = egret.sys.blendModeToNumber(blendMode); } else { // Classic display. slot.display.blendMode = blendMode; } } } private _updateSlotColor(slot: MovieSlot, aM: number, rM: number, gM: number, bM: number, aO: number, rO: number, gO: number, bO: number): void { if ( rM !== 1 || gM !== 1 || bM !== 1 || rO !== 0 || gO !== 0 || bO !== 0 || aO !== 0 ) { if (!slot.colorFilter) { slot.colorFilter = new egret.ColorMatrixFilter(); } const colorMatrix = slot.colorFilter.matrix; colorMatrix[0] = rM; colorMatrix[6] = gM; colorMatrix[12] = bM; colorMatrix[18] = aM; colorMatrix[4] = rO; colorMatrix[9] = gO; colorMatrix[14] = bO; colorMatrix[19] = aO; slot.colorFilter.matrix = colorMatrix; if (this._batchEnabled) { // RenderNode display. (<egret.sys.BitmapNode>slot.display.$renderNode).filter = slot.colorFilter; (<egret.sys.BitmapNode>slot.display.$renderNode).alpha = 1.0; } else { // Classic display. let filters = slot.display.filters; if (!filters) { filters = []; } if (filters.indexOf(slot.colorFilter) < 0) { filters.push(slot.colorFilter); } slot.display.filters = filters; slot.display.$setAlpha(1.0); } } else { if (slot.colorFilter) { slot.colorFilter = null; } if (this._batchEnabled) { // RenderNode display. (<egret.sys.BitmapNode>slot.display.$renderNode).filter = null as any; (<egret.sys.BitmapNode>slot.display.$renderNode).alpha = aM; } else { // Classic display. slot.display.filters = null as any; slot.display.$setAlpha(aM); } } } private _updateSlotDisplay(slot: MovieSlot): void { const prevDisplay = slot.display || slot.rawDisplay; const prevChildMovie = slot.childMovie; if (slot.displayIndex >= 0) { slot.displayConfig = this._groupConfig.display[slot.displayIndex]; if (slot.displayConfig.type === DisplayType.Armature) { let childMovie = slot.displayConfig.name in slot.childMovies ? slot.childMovies[slot.displayConfig.name] : null; if (!childMovie) { childMovie = buildMovie(slot.displayConfig.name, this._groupConfig.name); if (childMovie) { slot.childMovies[slot.displayConfig.name] = childMovie; } } if (childMovie) { slot.display = childMovie; slot.childMovie = childMovie; } else { slot.display = slot.rawDisplay; slot.childMovie = null; } } else { slot.display = slot.rawDisplay; slot.childMovie = null; } } else { slot.displayConfig = null; slot.display = slot.rawDisplay; slot.childMovie = null; } if (slot.display !== prevDisplay) { if (prevDisplay) { this.addChild(slot.display); this.swapChildren(slot.display, prevDisplay); this.removeChild(prevDisplay); } // Update blendMode. this._updateSlotBlendMode(slot); } // Update frame. if (slot.display === slot.rawDisplay) { if (slot.displayConfig && slot.displayConfig.regionIndex !== null && slot.displayConfig.regionIndex !== undefined) { if (!slot.displayConfig.texture) { const textureAtlasTexture = this._groupConfig.textures[slot.displayConfig.textureIndex || 0]; const regionIndex = slot.displayConfig.regionIndex * 4; const x = this._groupConfig.rectangleArray[regionIndex]; const y = this._groupConfig.rectangleArray[regionIndex + 1]; const width = this._groupConfig.rectangleArray[regionIndex + 2]; const height = this._groupConfig.rectangleArray[regionIndex + 3]; slot.displayConfig.texture = new egret.Texture(); if (EgretFactory._isV5) { (slot.displayConfig.texture as any)["$bitmapData"] = (textureAtlasTexture as any)["$bitmapData"]; } else { slot.displayConfig.texture._bitmapData = textureAtlasTexture._bitmapData; } slot.displayConfig.texture.$initData( x, y, Math.min(width, textureAtlasTexture.textureWidth - x), Math.min(height, textureAtlasTexture.textureHeight - y), 0, 0, Math.min(width, textureAtlasTexture.textureWidth - x), Math.min(height, textureAtlasTexture.textureHeight - y), textureAtlasTexture.textureWidth, textureAtlasTexture.textureHeight ); } if (this._batchEnabled) { // RenderNode display. const texture = slot.displayConfig.texture; const bitmapNode = slot.rawDisplay.$renderNode as egret.sys.BitmapNode; egret.sys.RenderNode.prototype.cleanBeforeRender.call(slot.rawDisplay.$renderNode); if (EgretFactory._isV5) { bitmapNode.image = (texture as any)["$bitmapData"]; bitmapNode.drawImage( (texture as any).$bitmapX, (texture as any).$bitmapY, (texture as any).$bitmapWidth, (texture as any).$bitmapHeight, (texture as any).$offsetX, (texture as any).$offsetY, texture.textureWidth, texture.textureHeight ); bitmapNode.imageWidth = (texture as any)._sourceWidth; bitmapNode.imageHeight = (texture as any)._sourceHeight; } else { bitmapNode.image = texture._bitmapData; bitmapNode.drawImage( texture._bitmapX, texture._bitmapY, texture._bitmapWidth, texture._bitmapHeight, texture._offsetX, texture._offsetY, texture.textureWidth, texture.textureHeight ); bitmapNode.imageWidth = texture._sourceWidth; bitmapNode.imageHeight = texture._sourceHeight; } } else { // Classic display. slot.rawDisplay.visible = true; slot.rawDisplay.$setBitmapData(slot.displayConfig.texture); } } else { if (this._batchEnabled) { // RenderNode display. (<egret.sys.BitmapNode>slot.rawDisplay.$renderNode).image = null as any; } else { // Classic display. slot.rawDisplay.visible = false; slot.rawDisplay.$setBitmapData(null as any); } } } // Update child movie. if (slot.childMovie !== prevChildMovie) { if (prevChildMovie) { prevChildMovie.stop(); this._childMovies.slice(this._childMovies.indexOf(prevChildMovie), 1); } if (slot.childMovie) { if (this._childMovies.indexOf(slot.childMovie) < 0) { this._childMovies.push(slot.childMovie); } if (slot.config.action) { slot.childMovie.play(slot.config.action); } else { slot.childMovie.play(slot.childMovie._config.action); } } } } private _getSlot(name: string): MovieSlot | null { for (let i = 0, l = this._slots.length; i < l; ++i) { const slot = this._slots[i]; if (slot.config.name === name) { return slot; } } return null; } /** * @inheritDoc */ $render(): void { if (this._batchEnabled) { // RenderNode display. } else { // Classic display. super.$render(); } } /** * @inheritDoc */ $measureContentBounds(bounds: egret.Rectangle): void { if (this._batchEnabled && this._cacheRectangle) { // RenderNode display. bounds.setTo(this._cacheRectangle.x, this._cacheRectangle.y, this._cacheRectangle.width - this._cacheRectangle.x, this._cacheRectangle.height - this._cacheRectangle.y); } else { // Classic display. super.$measureContentBounds(bounds); } } /** * @inheritDoc */ $doAddChild(child: egret.DisplayObject, index: number, notifyListeners?: boolean): egret.DisplayObject { if (this._batchEnabled) { // RenderNode display. console.warn("Can not add child."); return null as any; } // Classic display. return super.$doAddChild(child, index, notifyListeners); } /** * @inheritDoc */ $doRemoveChild(index: number, notifyListeners?: boolean): egret.DisplayObject { if (this._batchEnabled) { // RenderNode display. console.warn("Can not remove child."); return null as any; } // Classic display. return super.$doRemoveChild(index, notifyListeners); } /** * 释放动画。 * @version DragonBones 3.0 * @language zh_CN */ public dispose(): void { if (this._isLockDispose) { this._isDelayDispose = true; } else { if (this._clock) { this._clock.remove(this); } if (this._slots) { for (let i = 0, l = this._slots.length; i < l; ++i) { this._slots[i].dispose(); } } this._isPlaying = false; this._cacheRectangle = null; this._clock = null; this._groupConfig = null as any; this._config = null as any; this._clipConfig = null as any; this._currentFrameConfig = null; this._clipArray = null as any; this._clipNames = null as any; this._slots = null as any; this._childMovies = null as any; } } /** * @inheritDoc */ public advanceTime(passedTime: number): void { if (this._isPlaying) { this._isLockDispose = true; if (passedTime < 0) { passedTime = -passedTime; } passedTime *= this.timeScale; this._time += passedTime * this.clipTimeScale; // Modify time. const duration = this._clipConfig.duration; const totalTime = duration * this._playTimes; let currentTime = this._time; let currentPlayTimes = this._currentPlayTimes; if (this._playTimes > 0 && (currentTime >= totalTime || currentTime <= -totalTime)) { this._isCompleted = true; currentPlayTimes = this._playTimes; if (currentTime < 0) { currentTime = 0; } else { currentTime = duration; } } else { this._isCompleted = false; if (currentTime < 0) { currentPlayTimes = Math.floor(-currentTime / duration); currentTime = duration - (-currentTime % duration); } else { currentPlayTimes = Math.floor(currentTime / duration); currentTime %= duration; } if (this._playTimes > 0 && currentPlayTimes > this._playTimes) { currentPlayTimes = this._playTimes; } } if (this._currentTime === currentTime) { return; } const cacheFrameIndex = Math.floor(currentTime * this._clipConfig.cacheTimeToFrameScale); if (this._cacheFrameIndex !== cacheFrameIndex) { this._cacheFrameIndex = cacheFrameIndex; const displayFrameArray = this._groupConfig.displayFrameArray; const transformArray = this._groupConfig.transformArray; const colorArray = this._groupConfig.colorArray; // let isFirst = true; let hasDisplay = false; let needCacheRectangle = false; const prevCacheRectangle = this._cacheRectangle; this._cacheRectangle = this._clipConfig.cacheRectangles[this._cacheFrameIndex]; if (this._batchEnabled && !this._cacheRectangle) { needCacheRectangle = true; this._cacheRectangle = new egret.Rectangle(); this._clipConfig.cacheRectangles[this._cacheFrameIndex] = this._cacheRectangle; } // Update slots. for (let i = 0, l = this._slots.length; i < l; ++i) { const slot = this._slots[i]; let clipFrameIndex = this._frameSize * this._cacheFrameIndex + i * 2; if (clipFrameIndex >= this._clipArray.length) { clipFrameIndex = this._frameSize * (this._cacheFrameIndex - 1) + i * 2; } const displayFrameIndex = this._clipArray[clipFrameIndex] * 2; if (displayFrameIndex >= 0) { const displayIndex = displayFrameArray[displayFrameIndex]; const colorIndex = displayFrameArray[displayFrameIndex + 1] * 8; const transformIndex = this._clipArray[clipFrameIndex + 1] * 6; let colorChange = false; if (slot.displayIndex !== displayIndex) { slot.displayIndex = displayIndex; colorChange = true; this._updateSlotDisplay(slot); } if (slot.colorIndex !== colorIndex || colorChange) { slot.colorIndex = colorIndex; if (slot.colorIndex >= 0) { this._updateSlotColor( slot, colorArray[colorIndex] * 0.01, colorArray[colorIndex + 1] * 0.01, colorArray[colorIndex + 2] * 0.01, colorArray[colorIndex + 3] * 0.01, colorArray[colorIndex + 4], colorArray[colorIndex + 5], colorArray[colorIndex + 6], colorArray[colorIndex + 7] ); } else { this._updateSlotColor(slot, 1, 1, 1, 1, 0, 0, 0, 0); } } hasDisplay = true; if (slot.transformIndex !== transformIndex) { slot.transformIndex = transformIndex; if (this._batchEnabled) { // RenderNode display. let matrix = (<egret.sys.BitmapNode>slot.display.$renderNode).matrix; if (!matrix) { matrix = (<egret.sys.BitmapNode>slot.display.$renderNode).matrix = new egret.Matrix(); } matrix.a = transformArray[transformIndex]; matrix.b = transformArray[transformIndex + 1]; matrix.c = transformArray[transformIndex + 2]; matrix.d = transformArray[transformIndex + 3]; matrix.tx = transformArray[transformIndex + 4]; matrix.ty = transformArray[transformIndex + 5]; } else { // Classic display. _helpMatrix.a = transformArray[transformIndex]; _helpMatrix.b = transformArray[transformIndex + 1]; _helpMatrix.c = transformArray[transformIndex + 2]; _helpMatrix.d = transformArray[transformIndex + 3]; _helpMatrix.tx = transformArray[transformIndex + 4]; _helpMatrix.ty = transformArray[transformIndex + 5]; slot.display.$setMatrix(_helpMatrix); } } // if (this._batchEnabled && needCacheRectangle && slot.displayConfig) { // RenderNode display. const matrix = (<egret.sys.BitmapNode>slot.display.$renderNode).matrix; _helpRectangle.x = 0; _helpRectangle.y = 0; _helpRectangle.width = slot.displayConfig.texture.textureWidth; _helpRectangle.height = slot.displayConfig.texture.textureHeight; matrix.$transformBounds(_helpRectangle); if (isFirst) { isFirst = false; this._cacheRectangle.x = _helpRectangle.x; this._cacheRectangle.width = _helpRectangle.x + _helpRectangle.width; this._cacheRectangle.y = _helpRectangle.y; this._cacheRectangle.height = _helpRectangle.y + _helpRectangle.height; } else { this._cacheRectangle.x = Math.min(this._cacheRectangle.x, _helpRectangle.x); this._cacheRectangle.width = Math.max(this._cacheRectangle.width, _helpRectangle.x + _helpRectangle.width); this._cacheRectangle.y = Math.min(this._cacheRectangle.y, _helpRectangle.y); this._cacheRectangle.height = Math.max(this._cacheRectangle.height, _helpRectangle.y + _helpRectangle.height); } } } else if (slot.displayIndex !== -1) { slot.displayIndex = -1; this._updateSlotDisplay(slot); } } // if (this._cacheRectangle) { if (hasDisplay && needCacheRectangle && isFirst && prevCacheRectangle) { this._cacheRectangle.x = prevCacheRectangle.x; this._cacheRectangle.y = prevCacheRectangle.y; this._cacheRectangle.width = prevCacheRectangle.width; this._cacheRectangle.height = prevCacheRectangle.height; } this.$invalidateContentBounds(); } } if (this._isCompleted) { this._isPlaying = false; } if (!this._isStarted) { this._isStarted = true; if (this.hasEventListener(MovieEvent.START)) { const event = egret.Event.create(MovieEvent, MovieEvent.START); event.movie = this; event.clipName = this._clipConfig.name; event.name = ""; event.slotName = ""; this.dispatchEvent(event); } } this._isReversing = this._currentTime > currentTime && this._currentPlayTimes === currentPlayTimes; this._currentTime = currentTime; // Action and event. const frameCount = this._clipConfig.frame ? this._clipConfig.frame.length : 0; if (frameCount > 0) { const currentFrameIndex = Math.floor(this._currentTime * this._config.frameRate); const currentFrameConfig = this._groupConfig.frame[this._clipConfig.frame[currentFrameIndex]]; if (this._currentFrameConfig !== currentFrameConfig) { if (frameCount > 1) { let crossedFrameConfig = this._currentFrameConfig; this._currentFrameConfig = currentFrameConfig; if (!crossedFrameConfig) { const prevFrameIndex = Math.floor(this._currentTime * this._config.frameRate); crossedFrameConfig = this._groupConfig.frame[this._clipConfig.frame[prevFrameIndex]]; if (this._isReversing) { } else { if ( this._currentTime <= crossedFrameConfig.position || this._currentPlayTimes !== currentPlayTimes ) { crossedFrameConfig = this._groupConfig.frame[crossedFrameConfig.prev]; } } } if (this._isReversing) { while (crossedFrameConfig !== currentFrameConfig) { this._onCrossFrame(crossedFrameConfig); crossedFrameConfig = this._groupConfig.frame[crossedFrameConfig.prev]; } } else { while (crossedFrameConfig !== currentFrameConfig) { crossedFrameConfig = this._groupConfig.frame[crossedFrameConfig.next]; this._onCrossFrame(crossedFrameConfig); } } } else { this._currentFrameConfig = currentFrameConfig; if (this._currentFrameConfig) { this._onCrossFrame(this._currentFrameConfig); } } } } if (this._currentPlayTimes !== currentPlayTimes) { this._currentPlayTimes = currentPlayTimes; if (this.hasEventListener(MovieEvent.LOOP_COMPLETE)) { const event = egret.Event.create(MovieEvent, MovieEvent.LOOP_COMPLETE); event.movie = this; event.clipName = this._clipConfig.name; event.name = ""; event.slotName = ""; this.dispatchEvent(event); egret.Event.release(event); } if (this._isCompleted && this.hasEventListener(MovieEvent.COMPLETE)) { const event = egret.Event.create(MovieEvent, MovieEvent.COMPLETE); event.movie = this; event.clipName = this._clipConfig.name; event.name = ""; event.slotName = ""; this.dispatchEvent(event); egret.Event.release(event); } } } this._isLockDispose = false; if (this._isDelayDispose) { this.dispose(); } } /** * 播放动画剪辑。 * @param clipName 动画剪辑的名称,如果未设置,则播放默认动画剪辑,或将暂停状态切换为播放状态,或重新播放上一个正在播放的动画剪辑。 * @param playTimes 动画剪辑需要播放的次数。 [-1: 使用动画剪辑默认值, 0: 无限循环播放, [1~N]: 循环播放 N 次] * @version DragonBones 4.7 * @language zh_CN */ public play(clipName: string | null = null, playTimes: number = -1): void { if (clipName) { let clipConfig: ClipConfig | null = null; for (let i = 0, l = this._config.clip.length; i < l; ++i) { const data = this._config.clip[i]; if (data.name === clipName) { clipConfig = data; } } if (clipConfig) { this._clipConfig = clipConfig; this._clipArray = new Int16Array(this._groupConfig.arrayBuffer, this._groupConfig.offset + this._groupConfig.position[0] + this._clipConfig.p, this._clipConfig.s / this._groupConfig.position[2]); if (!this._clipConfig.cacheRectangles) { this._clipConfig.cacheRectangles = []; } this._isPl