dragonbones-runtime
Version:
the tools to build dragonbones file for diffrent framework
1,243 lines (1,132 loc) • 59.3 kB
text/typescript
/**
* 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