UNPKG

dragonbones-runtime

Version:

the tools to build dragonbones file for diffrent framework

1,075 lines (1,006 loc) 37.8 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 { /** * - Armature is the core of the skeleton animation system. * @see dragonBones.ArmatureData * @see dragonBones.Bone * @see dragonBones.Slot * @see dragonBones.Animation * @version DragonBones 3.0 * @language en_US */ /** * - 骨架是骨骼动画系统的核心。 * @see dragonBones.ArmatureData * @see dragonBones.Bone * @see dragonBones.Slot * @see dragonBones.Animation * @version DragonBones 3.0 * @language zh_CN */ export class Armature extends BaseObject implements IAnimatable { public static toString(): string { return "[class dragonBones.Armature]"; } private static _onSortSlots(a: Slot, b: Slot): number { return a._zOrder > b._zOrder ? 1 : -1; } /** * - Whether to inherit the animation control of the parent armature. * True to try to have the child armature play an animation with the same name when the parent armature play the animation. * @default true * @version DragonBones 4.5 * @language en_US */ /** * - 是否继承父骨架的动画控制。 * 如果该值为 true,当父骨架播放动画时,会尝试让子骨架播放同名动画。 * @default true * @version DragonBones 4.5 * @language zh_CN */ public inheritAnimation: boolean; /** * @private */ public userData: any; private _lockUpdate: boolean; private _bonesDirty: boolean; private _slotsDirty: boolean; private _zOrderDirty: boolean; private _flipX: boolean; private _flipY: boolean; /** * @internal * @private */ public _cacheFrameIndex: number; private readonly _bones: Array<Bone> = []; private readonly _slots: Array<Slot> = []; /** * @internal * @private */ public readonly _glueSlots: Array<Slot> = []; /** * @internal * @private */ public readonly _constraints: Array<Constraint> = []; private readonly _actions: Array<ActionData> = []; /** * @internal * @private */ public _armatureData: ArmatureData; private _animation: Animation = null as any; // Initial value. private _proxy: IArmatureProxy = null as any; // Initial value. private _display: any; /** * @internal * @private */ public _replaceTextureAtlasData: TextureAtlasData | null = null; // Initial value. private _replacedTexture: any; /** * @internal * @private */ public _dragonBones: DragonBones; private _clock: WorldClock | null = null; // Initial value. /** * @internal * @private */ public _parent: Slot | null; /** * @private */ protected _onClear(): void { if (this._clock !== null) { // Remove clock first. this._clock.remove(this); } for (const bone of this._bones) { bone.returnToPool(); } for (const slot of this._slots) { slot.returnToPool(); } for (const constraint of this._constraints) { constraint.returnToPool(); } if (this._animation !== null) { this._animation.returnToPool(); } if (this._proxy !== null) { this._proxy.dbClear(); } if (this._replaceTextureAtlasData !== null) { this._replaceTextureAtlasData.returnToPool(); } this.inheritAnimation = true; this.userData = null; this._lockUpdate = false; this._bonesDirty = false; this._slotsDirty = false; this._zOrderDirty = false; this._flipX = false; this._flipY = false; this._cacheFrameIndex = -1; this._bones.length = 0; this._slots.length = 0; this._glueSlots.length = 0; this._constraints.length = 0; this._actions.length = 0; this._armatureData = null as any; // this._animation = null as any; // this._proxy = null as any; // this._display = null; this._replaceTextureAtlasData = null; this._replacedTexture = null; this._dragonBones = null as any; // this._clock = null; this._parent = null; } private _sortBones(): void { const total = this._bones.length; if (total <= 0) { return; } const sortHelper = this._bones.concat(); let index = 0; let count = 0; this._bones.length = 0; while (count < total) { const bone = sortHelper[index++]; if (index >= total) { index = 0; } if (this._bones.indexOf(bone) >= 0) { continue; } if (bone._hasConstraint) { // Wait constraint. let flag = false; for (const constraint of this._constraints) { if (constraint._root === bone && this._bones.indexOf(constraint._target) < 0) { flag = true; break; } } if (flag) { continue; } } if (bone.parent !== null && this._bones.indexOf(bone.parent) < 0) { // Wait parent. continue; } this._bones.push(bone); count++; } } private _sortSlots(): void { this._slots.sort(Armature._onSortSlots); } /** * @internal * @private */ public _sortZOrder(slotIndices: Array<number> | Int16Array | null, offset: number): void { const slotDatas = this._armatureData.sortedSlots; const isOriginal = slotIndices === null; if (this._zOrderDirty || !isOriginal) { for (let i = 0, l = slotDatas.length; i < l; ++i) { const slotIndex = isOriginal ? i : (slotIndices as Array<number>)[offset + i]; if (slotIndex < 0 || slotIndex >= l) { continue; } const slotData = slotDatas[slotIndex]; const slot = this.getSlot(slotData.name); if (slot !== null) { slot._setZorder(i); } } this._slotsDirty = true; this._zOrderDirty = !isOriginal; } } /** * @internal * @private */ public _addBoneToBoneList(value: Bone): void { if (this._bones.indexOf(value) < 0) { this._bonesDirty = true; this._bones.push(value); } } /** * @internal * @private */ public _removeBoneFromBoneList(value: Bone): void { const index = this._bones.indexOf(value); if (index >= 0) { this._bones.splice(index, 1); } } /** * @internal * @private */ public _addSlotToSlotList(value: Slot): void { if (this._slots.indexOf(value) < 0) { this._slotsDirty = true; this._slots.push(value); } } /** * @internal * @private */ public _removeSlotFromSlotList(value: Slot): void { const index = this._slots.indexOf(value); if (index >= 0) { this._slots.splice(index, 1); } } /** * @internal * @private */ public _bufferAction(action: ActionData, append: boolean): void { if (this._actions.indexOf(action) < 0) { if (append) { this._actions.push(action); } else { this._actions.unshift(action); } } } /** * - Dispose the armature. (Return to the object pool) * @example * <pre> * removeChild(armature.display); * armature.dispose(); * </pre> * @version DragonBones 3.0 * @language en_US */ /** * - 释放骨架。 (回收到对象池) * @example * <pre> * removeChild(armature.display); * armature.dispose(); * </pre> * @version DragonBones 3.0 * @language zh_CN */ public dispose(): void { if (this._armatureData !== null) { this._lockUpdate = true; this._dragonBones.bufferObject(this); } } /** * @internal * @private */ public init( armatureData: ArmatureData, proxy: IArmatureProxy, display: any, dragonBones: DragonBones ): void { if (this._armatureData !== null) { return; } this._armatureData = armatureData; this._animation = BaseObject.borrowObject(Animation); this._proxy = proxy; this._display = display; this._dragonBones = dragonBones; this._proxy.dbInit(this); this._animation.init(this); this._animation.animations = this._armatureData.animations; } /** * @inheritDoc */ public advanceTime(passedTime: number): void { if (this._lockUpdate) { return; } if (this._armatureData === null) { console.warn("The armature has been disposed."); return; } else if (this._armatureData.parent === null) { console.warn("The armature data has been disposed.\nPlease make sure dispose armature before call factory.clear()."); return; } const prevCacheFrameIndex = this._cacheFrameIndex; // Update animation. this._animation.advanceTime(passedTime); // Sort bones and slots. if (this._bonesDirty) { this._bonesDirty = false; this._sortBones(); } if (this._slotsDirty) { this._slotsDirty = false; this._sortSlots(); } // Update bones and slots. if (this._cacheFrameIndex < 0 || this._cacheFrameIndex !== prevCacheFrameIndex) { let i = 0, l = 0; for (i = 0, l = this._bones.length; i < l; ++i) { this._bones[i].update(this._cacheFrameIndex); } for (i = 0, l = this._slots.length; i < l; ++i) { this._slots[i].update(this._cacheFrameIndex); } for (i = 0, l = this._glueSlots.length; i < l; ++i) { this._glueSlots[i]._updateGlueMesh(); } } // Do actions. if (this._actions.length > 0) { this._lockUpdate = true; for (const action of this._actions) { if (action.type === ActionType.Play) { this._animation.fadeIn(action.name); } } this._actions.length = 0; this._lockUpdate = false; } this._proxy.dbUpdate(); } /** * - Forces a specific bone or its owning slot to update the transform or display property in the next frame. * @param boneName - The bone name. (If not set, all bones will be update) * @param updateSlot - Whether to update the bone's slots. (Default: false) * @see dragonBones.Bone#invalidUpdate() * @see dragonBones.Slot#invalidUpdate() * @version DragonBones 3.0 * @language en_US */ /** * - 强制特定骨骼或其拥有的插槽在下一帧更新变换或显示属性。 * @param boneName - 骨骼名称。 (如果未设置,将更新所有骨骼) * @param updateSlot - 是否更新骨骼的插槽。 (默认: false) * @see dragonBones.Bone#invalidUpdate() * @see dragonBones.Slot#invalidUpdate() * @version DragonBones 3.0 * @language zh_CN */ public invalidUpdate(boneName: string | null = null, updateSlot: boolean = false): void { if (boneName !== null && boneName.length > 0) { const bone = this.getBone(boneName); if (bone !== null) { bone.invalidUpdate(); if (updateSlot) { for (const slot of this._slots) { if (slot.parent === bone) { slot.invalidUpdate(); } } } } } else { for (const bone of this._bones) { bone.invalidUpdate(); } if (updateSlot) { for (const slot of this._slots) { slot.invalidUpdate(); } } } } /** * - Check whether a specific point is inside a custom bounding box in a slot. * The coordinate system of the point is the inner coordinate system of the armature. * Custom bounding boxes need to be customized in Dragonbones Pro. * @param x - The horizontal coordinate of the point. * @param y - The vertical coordinate of the point. * @version DragonBones 5.0 * @language en_US */ /** * - 检查特定点是否在某个插槽的自定义边界框内。 * 点的坐标系为骨架内坐标系。 * 自定义边界框需要在 DragonBones Pro 中自定义。 * @param x - 点的水平坐标。 * @param y - 点的垂直坐标。 * @version DragonBones 5.0 * @language zh_CN */ public containsPoint(x: number, y: number): Slot | null { for (const slot of this._slots) { if (slot.containsPoint(x, y)) { return slot; } } return null; } /** * - Check whether a specific segment intersects a custom bounding box for a slot in the armature. * The coordinate system of the segment and intersection is the inner coordinate system of the armature. * Custom bounding boxes need to be customized in Dragonbones Pro. * @param xA - The horizontal coordinate of the beginning of the segment. * @param yA - The vertical coordinate of the beginning of the segment. * @param xB - The horizontal coordinate of the end point of the segment. * @param yB - The vertical coordinate of the end point of the segment. * @param intersectionPointA - The first intersection at which a line segment intersects the bounding box from the beginning to the end. (If not set, the intersection point will not calculated) * @param intersectionPointB - The first intersection at which a line segment intersects the bounding box from the end to the beginning. (If not set, the intersection point will not calculated) * @param normalRadians - The normal radians of the tangent of the intersection boundary box. [x: Normal radian of the first intersection tangent, y: Normal radian of the second intersection tangent] (If not set, the normal will not calculated) * @returns The slot of the first custom bounding box where the segment intersects from the start point to the end point. * @version DragonBones 5.0 * @language en_US */ /** * - 检查特定线段是否与骨架的某个插槽的自定义边界框相交。 * 线段和交点的坐标系均为骨架内坐标系。 * 自定义边界框需要在 DragonBones Pro 中自定义。 * @param xA - 线段起点的水平坐标。 * @param yA - 线段起点的垂直坐标。 * @param xB - 线段终点的水平坐标。 * @param yB - 线段终点的垂直坐标。 * @param intersectionPointA - 线段从起点到终点与边界框相交的第一个交点。 (如果未设置,则不计算交点) * @param intersectionPointB - 线段从终点到起点与边界框相交的第一个交点。 (如果未设置,则不计算交点) * @param normalRadians - 交点边界框切线的法线弧度。 [x: 第一个交点切线的法线弧度, y: 第二个交点切线的法线弧度] (如果未设置,则不计算法线) * @returns 线段从起点到终点相交的第一个自定义边界框的插槽。 * @version DragonBones 5.0 * @language zh_CN */ public intersectsSegment( xA: number, yA: number, xB: number, yB: number, intersectionPointA: { x: number, y: number } | null = null, intersectionPointB: { x: number, y: number } | null = null, normalRadians: { x: number, y: number } | null = null ): Slot | null { const isV = xA === xB; let dMin = 0.0; let dMax = 0.0; let intXA = 0.0; let intYA = 0.0; let intXB = 0.0; let intYB = 0.0; let intAN = 0.0; let intBN = 0.0; let intSlotA: Slot | null = null; let intSlotB: Slot | null = null; for (const slot of this._slots) { const intersectionCount = slot.intersectsSegment(xA, yA, xB, yB, intersectionPointA, intersectionPointB, normalRadians); if (intersectionCount > 0) { if (intersectionPointA !== null || intersectionPointB !== null) { if (intersectionPointA !== null) { let d = isV ? intersectionPointA.y - yA : intersectionPointA.x - xA; if (d < 0.0) { d = -d; } if (intSlotA === null || d < dMin) { dMin = d; intXA = intersectionPointA.x; intYA = intersectionPointA.y; intSlotA = slot; if (normalRadians) { intAN = normalRadians.x; } } } if (intersectionPointB !== null) { let d = intersectionPointB.x - xA; if (d < 0.0) { d = -d; } if (intSlotB === null || d > dMax) { dMax = d; intXB = intersectionPointB.x; intYB = intersectionPointB.y; intSlotB = slot; if (normalRadians !== null) { intBN = normalRadians.y; } } } } else { intSlotA = slot; break; } } } if (intSlotA !== null && intersectionPointA !== null) { intersectionPointA.x = intXA; intersectionPointA.y = intYA; if (normalRadians !== null) { normalRadians.x = intAN; } } if (intSlotB !== null && intersectionPointB !== null) { intersectionPointB.x = intXB; intersectionPointB.y = intYB; if (normalRadians !== null) { normalRadians.y = intBN; } } return intSlotA; } /** * - Get a specific bone. * @param name - The bone name. * @see dragonBones.Bone * @version DragonBones 3.0 * @language en_US */ /** * - 获取特定的骨骼。 * @param name - 骨骼名称。 * @see dragonBones.Bone * @version DragonBones 3.0 * @language zh_CN */ public getBone(name: string): Bone | null { for (const bone of this._bones) { if (bone.name === name) { return bone; } } return null; } /** * - Get a specific bone by the display. * @param display - The display object. * @see dragonBones.Bone * @version DragonBones 3.0 * @language en_US */ /** * - 通过显示对象获取特定的骨骼。 * @param display - 显示对象。 * @see dragonBones.Bone * @version DragonBones 3.0 * @language zh_CN */ public getBoneByDisplay(display: any): Bone | null { const slot = this.getSlotByDisplay(display); return slot !== null ? slot.parent : null; } /** * - Get a specific slot. * @param name - The slot name. * @see dragonBones.Slot * @version DragonBones 3.0 * @language en_US */ /** * - 获取特定的插槽。 * @param name - 插槽名称。 * @see dragonBones.Slot * @version DragonBones 3.0 * @language zh_CN */ public getSlot(name: string): Slot | null { for (const slot of this._slots) { if (slot.name === name) { return slot; } } return null; } /** * - Get a specific slot by the display. * @param display - The display object. * @see dragonBones.Slot * @version DragonBones 3.0 * @language en_US */ /** * - 通过显示对象获取特定的插槽。 * @param display - 显示对象。 * @see dragonBones.Slot * @version DragonBones 3.0 * @language zh_CN */ public getSlotByDisplay(display: any): Slot | null { if (display !== null) { for (const slot of this._slots) { if (slot.display === display) { return slot; } } } return null; } /** * @deprecated */ public addBone(value: Bone, parentName: string): void { console.assert(value !== null); value._setArmature(this); value._setParent(parentName.length > 0 ? this.getBone(parentName) : null); } /** * @deprecated */ public addSlot(value: Slot, parentName: string): void { const bone = this.getBone(parentName); console.assert(value !== null && bone !== null); value._setArmature(this); value._setParent(bone); } /** * @private */ public addConstraint(value: Constraint): void { if (this._constraints.indexOf(value) < 0) { this._constraints.push(value); } } /** * @deprecated */ public removeBone(value: Bone): void { console.assert(value !== null && value.armature === this); value._setParent(null); value._setArmature(null); } /** * @deprecated */ public removeSlot(value: Slot): void { console.assert(value !== null && value.armature === this); value._setParent(null); value._setArmature(null); } /** * - Get all bones. * @see dragonBones.Bone * @version DragonBones 3.0 * @language en_US */ /** * - 获取所有的骨骼。 * @see dragonBones.Bone * @version DragonBones 3.0 * @language zh_CN */ public getBones(): Array<Bone> { return this._bones; } /** * - Get all slots. * @see dragonBones.Slot * @version DragonBones 3.0 * @language en_US */ /** * - 获取所有的插槽。 * @see dragonBones.Slot * @version DragonBones 3.0 * @language zh_CN */ public getSlots(): Array<Slot> { return this._slots; } /** * - Whether to flip the armature horizontally. * @version DragonBones 5.5 * @language en_US */ /** * - 是否将骨架水平翻转。 * @version DragonBones 5.5 * @language zh_CN */ public get flipX(): boolean { return this._flipX; } public set flipX(value: boolean) { if (this._flipX === value) { return; } this._flipX = value; this.invalidUpdate(); } /** * - Whether to flip the armature vertically. * @version DragonBones 5.5 * @language en_US */ /** * - 是否将骨架垂直翻转。 * @version DragonBones 5.5 * @language zh_CN */ public get flipY(): boolean { return this._flipY; } public set flipY(value: boolean) { if (this._flipY === value) { return; } this._flipY = value; this.invalidUpdate(); } /** * - The animation cache frame rate, which turns on the animation cache when the set value is greater than 0. * There is a certain amount of memory overhead to improve performance by caching animation data in memory. * The frame rate should not be set too high, usually with the frame rate of the animation is similar and lower than the program running frame rate. * When the animation cache is turned on, some features will fail, such as the offset property of bone. * @example * <pre> * armature.cacheFrameRate = 24; * </pre> * @see dragonBones.DragonBonesData#frameRate * @see dragonBones.ArmatureData#frameRate * @version DragonBones 4.5 * @language en_US */ /** * - 动画缓存帧率,当设置的值大于 0 的时,将会开启动画缓存。 * 通过将动画数据缓存在内存中来提高运行性能,会有一定的内存开销。 * 帧率不宜设置的过高,通常跟动画的帧率相当且低于程序运行的帧率。 * 开启动画缓存后,某些功能将会失效,比如骨骼的 offset 属性等。 * @example * <pre> * armature.cacheFrameRate = 24; * </pre> * @see dragonBones.DragonBonesData#frameRate * @see dragonBones.ArmatureData#frameRate * @version DragonBones 4.5 * @language zh_CN */ public get cacheFrameRate(): number { return this._armatureData.cacheFrameRate; } public set cacheFrameRate(value: number) { if (this._armatureData.cacheFrameRate !== value) { this._armatureData.cacheFrames(value); // Set child armature frameRate. for (const slot of this._slots) { const childArmature = slot.childArmature; if (childArmature !== null) { childArmature.cacheFrameRate = value; } } } } /** * - The armature name. * @version DragonBones 3.0 * @language en_US */ /** * - 骨架名称。 * @version DragonBones 3.0 * @language zh_CN */ public get name(): string { return this._armatureData.name; } /** * - The armature data. * @see dragonBones.ArmatureData * @version DragonBones 4.5 * @language en_US */ /** * - 骨架数据。 * @see dragonBones.ArmatureData * @version DragonBones 4.5 * @language zh_CN */ public get armatureData(): ArmatureData { return this._armatureData; } /** * - The animation player. * @see dragonBones.Animation * @version DragonBones 3.0 * @language en_US */ /** * - 动画播放器。 * @see dragonBones.Animation * @version DragonBones 3.0 * @language zh_CN */ public get animation(): Animation { return this._animation; } /** * @pivate */ public get proxy(): IArmatureProxy { return this._proxy; } /** * - The EventDispatcher instance of the armature. * @version DragonBones 4.5 * @language en_US */ /** * - 该骨架的 EventDispatcher 实例。 * @version DragonBones 4.5 * @language zh_CN */ public get eventDispatcher(): IEventDispatcher { return this._proxy; } /** * - The display container. * The display of the slot is displayed as the parent. * Depending on the rendering engine, the type will be different, usually the DisplayObjectContainer type. * @version DragonBones 3.0 * @language en_US */ /** * - 显示容器实例。 * 插槽的显示对象都会以此显示容器为父级。 * 根据渲染引擎的不同,类型会不同,通常是 DisplayObjectContainer 类型。 * @version DragonBones 3.0 * @language zh_CN */ public get display(): any { return this._display; } /** * @private */ public get replacedTexture(): any { return this._replacedTexture; } public set replacedTexture(value: any) { if (this._replacedTexture === value) { return; } if (this._replaceTextureAtlasData !== null) { this._replaceTextureAtlasData.returnToPool(); this._replaceTextureAtlasData = null; } this._replacedTexture = value; for (const slot of this._slots) { slot.invalidUpdate(); slot.update(-1); } } /** * @inheritDoc */ public get clock(): WorldClock | null { return this._clock; } public set clock(value: WorldClock | null) { if (this._clock === value) { return; } if (this._clock !== null) { this._clock.remove(this); } this._clock = value; if (this._clock) { this._clock.add(this); } // Update childArmature clock. for (const slot of this._slots) { const childArmature = slot.childArmature; if (childArmature !== null) { childArmature.clock = this._clock; } } } /** * - Get the parent slot which the armature belongs to. * @see dragonBones.Slot * @version DragonBones 4.5 * @language en_US */ /** * - 该骨架所属的父插槽。 * @see dragonBones.Slot * @version DragonBones 4.5 * @language zh_CN */ public get parent(): Slot | null { return this._parent; } /** * @deprecated * @private */ public replaceTexture(texture: any): void { this.replacedTexture = texture; } /** * - Deprecated, please refer to {@link #eventDispatcher}. * @deprecated * @language en_US */ /** * - 已废弃,请参考 {@link #eventDispatcher}。 * @deprecated * @language zh_CN */ public hasEventListener(type: EventStringType): boolean { return this._proxy.hasDBEventListener(type); } /** * - Deprecated, please refer to {@link #eventDispatcher}. * @deprecated * @language en_US */ /** * - 已废弃,请参考 {@link #eventDispatcher}。 * @deprecated * @language zh_CN */ public addEventListener(type: EventStringType, listener: Function, target: any): void { this._proxy.addDBEventListener(type, listener, target); } /** * - Deprecated, please refer to {@link #eventDispatcher}. * @deprecated * @language en_US */ /** * - 已废弃,请参考 {@link #eventDispatcher}。 * @deprecated * @language zh_CN */ public removeEventListener(type: EventStringType, listener: Function, target: any): void { this._proxy.removeDBEventListener(type, listener, target); } /** * - Deprecated, please refer to {@link #cacheFrameRate}. * @deprecated * @language en_US */ /** * - 已废弃,请参考 {@link #cacheFrameRate}。 * @deprecated * @language zh_CN */ public enableAnimationCache(frameRate: number): void { this.cacheFrameRate = frameRate; } /** * - Deprecated, please refer to {@link #display}. * @deprecated * @language en_US */ /** * - 已废弃,请参考 {@link #display}。 * @deprecated * @language zh_CN */ public getDisplay(): any { return this._display; } } }