/** * 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. */ using System; using System.Collections.Generic; namespace DragonBones { /// /// - Armature is the core of the skeleton animation system. /// /// /// /// /// /// DragonBones 3.0 /// en_US /// /// - 骨架是骨骼动画系统的核心。 /// /// /// /// /// /// DragonBones 3.0 /// zh_CN public class Armature : BaseObject, IAnimatable { private static int _OnSortSlots(Slot a, Slot b) { if (a._zOrder > b._zOrder) { return 1; } else if (a._zOrder < b._zOrder) { return -1; } return 0;//fixed slots sort error } /// /// - 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. /// /// true /// DragonBones 4.5 /// en_US /// /// - 是否继承父骨架的动画控制。 /// 如果该值为 true,当父骨架播放动画时,会尝试让子骨架播放同名动画。 /// /// true /// DragonBones 4.5 /// zh_CN public bool inheritAnimation; /// public object userData; private bool _lockUpdate; private bool _slotsDirty; private bool _zOrderDirty; private bool _flipX; private bool _flipY; /// /// internal int _cacheFrameIndex; private readonly List _bones = new List(); private readonly List _slots = new List(); /// /// internal readonly List _constraints = new List(); private readonly List _actions = new List(); /// /// public ArmatureData _armatureData; private AnimationDB _animation = null; // Initial value. private IArmatureProxy _proxy = null; // Initial value. private object _display; /// /// internal TextureAtlasData _replaceTextureAtlasData = null; // Initial value. private object _replacedTexture; /// /// internal DragonBones _dragonBones; private WorldClock _clock = null; // Initial value. /// /// internal Slot _parent; /// protected override void _OnClear() { if (this._clock != null) { // Remove clock first. this._clock.Remove(this); } foreach (var bone in this._bones) { bone.ReturnToPool(); } foreach (var slot in this._slots) { slot.ReturnToPool(); } foreach (var constraint in 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._slotsDirty = false; this._zOrderDirty = false; this._flipX = false; this._flipY = false; this._cacheFrameIndex = -1; this._bones.Clear(); this._slots.Clear(); this._constraints.Clear(); this._actions.Clear(); this._armatureData = null; // this._animation = null; // this._proxy = null; // this._display = null; this._replaceTextureAtlasData = null; this._replacedTexture = null; this._dragonBones = null; // this._clock = null; this._parent = null; } /// /// internal void _SortZOrder(short[] slotIndices, int offset) { var slotDatas = this._armatureData.sortedSlots; var isOriginal = slotIndices == null; if (this._zOrderDirty || !isOriginal) { for (int i = 0, l = slotDatas.Count; i < l; ++i) { var slotIndex = isOriginal ? i : slotIndices[offset + i]; if (slotIndex < 0 || slotIndex >= l) { continue; } var slotData = slotDatas[slotIndex]; var slot = this.GetSlot(slotData.name); if (slot != null) { slot._SetZorder(i); } } this._slotsDirty = true; this._zOrderDirty = !isOriginal; } } /// /// internal void _AddBone(Bone value) { if (!this._bones.Contains(value)) { this._bones.Add(value); } } /// /// internal void _AddSlot(Slot value) { if (!this._slots.Contains(value)) { this._slotsDirty = true; this._slots.Add(value); } } /// /// internal void _AddConstraint(Constraint value) { if (!this._constraints.Contains(value)) { this._constraints.Add(value); } } /// /// internal void _BufferAction(EventObject action, bool append) { if (!this._actions.Contains(action)) { if (append) { this._actions.Add(action); } else { this._actions.Insert(0, action); } } } /// /// - Dispose the armature. (Return to the object pool) /// /// /// TypeScript style, for reference only. ///
        ///     removeChild(armature.display);
        ///     armature.dispose();
        /// 
///
/// DragonBones 3.0 /// en_US /// /// - 释放骨架。 (回收到对象池) /// /// /// TypeScript 风格,仅供参考。 ///
        ///     removeChild(armature.display);
        ///     armature.dispose();
        /// 
///
/// DragonBones 3.0 /// zh_CN public void Dispose() { if (this._armatureData != null) { this._lockUpdate = true; if (this._dragonBones != null) { this._dragonBones.BufferObject(this); } } } /// /// internal void Init(ArmatureData armatureData, IArmatureProxy proxy, object display, DragonBones dragonBones) { if (this._armatureData != null) { return; } this._armatureData = armatureData; this._animation = BaseObject.BorrowObject(); this._proxy = proxy; this._display = display; this._dragonBones = dragonBones; this._proxy.DBInit(this); this._animation.Init(this); this._animation.animations = this._armatureData.animations; } /// public void AdvanceTime(float passedTime) { if (this._lockUpdate) { return; } if (this._armatureData == null) { Helper.Assert(false, "The armature has been disposed."); return; } else if (this._armatureData.parent == null) { Helper.Assert(false, "The armature data has been disposed.\nPlease make sure dispose armature before call factory.clear()."); return; } var prevCacheFrameIndex = this._cacheFrameIndex; // Update animation. this._animation.AdvanceTime(passedTime); if (this._slotsDirty) { this._slotsDirty = false; this._slots.Sort(Armature._OnSortSlots); } // Update bones and slots. if (this._cacheFrameIndex < 0 || this._cacheFrameIndex != prevCacheFrameIndex) { int i = 0, l = 0; for (i = 0, l = this._bones.Count; i < l; ++i) { this._bones[i].Update(this._cacheFrameIndex); } for (i = 0, l = this._slots.Count; i < l; ++i) { this._slots[i].Update(this._cacheFrameIndex); } } if (this._actions.Count > 0) { this._lockUpdate = true; foreach (var action in this._actions) { var actionData = action.actionData; if (actionData != null) { if (actionData.type == ActionType.Play) { if (action.slot != null) { var childArmature = action.slot.childArmature; if (childArmature != null) { childArmature.animation.FadeIn(actionData.name); } } else if (action.bone != null) { foreach (var slot in this.GetSlots()) { if (slot.parent == action.bone) { var childArmature = slot.childArmature; if (childArmature != null) { childArmature.animation.FadeIn(actionData.name); } } } } else { this._animation.FadeIn(actionData.name); } } } action.ReturnToPool(); } this._actions.Clear(); 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. /// /// - The bone name. (If not set, all bones will be update) /// - Whether to update the bone's slots. (Default: false) /// /// /// DragonBones 3.0 /// en_US /// /// - 强制特定骨骼或其拥有的插槽在下一帧更新变换或显示属性。 /// /// - 骨骼名称。 (如果未设置,将更新所有骨骼) /// - 是否更新骨骼的插槽。 (默认: false) /// /// /// DragonBones 3.0 /// zh_CN public void InvalidUpdate(string boneName = null, bool updateSlot = false) { if (!string.IsNullOrEmpty(boneName)) { Bone bone = this.GetBone(boneName); if (bone != null) { bone.InvalidUpdate(); if (updateSlot) { foreach (var slot in this._slots) { if (slot.parent == bone) { slot.InvalidUpdate(); } } } } } else { foreach (var bone in this._bones) { bone.InvalidUpdate(); } if (updateSlot) { foreach (var slot in 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. /// /// - The horizontal coordinate of the point. /// - The vertical coordinate of the point. /// DragonBones 5.0 /// en_US /// /// - 检查特定点是否在某个插槽的自定义边界框内。 /// 点的坐标系为骨架内坐标系。 /// 自定义边界框需要在 DragonBones Pro 中自定义。 /// /// - 点的水平坐标。 /// - 点的垂直坐标。 /// DragonBones 5.0 /// zh_CN public Slot ContainsPoint(float x, float y) { foreach (var slot in 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. /// /// - The horizontal coordinate of the beginning of the segment. /// - The vertical coordinate of the beginning of the segment. /// - The horizontal coordinate of the end point of the segment. /// - The vertical coordinate of the end point of the segment. /// - 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) /// - 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) /// - 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) /// The slot of the first custom bounding box where the segment intersects from the start point to the end point. /// DragonBones 5.0 /// en_US /// /// - 检查特定线段是否与骨架的某个插槽的自定义边界框相交。 /// 线段和交点的坐标系均为骨架内坐标系。 /// 自定义边界框需要在 DragonBones Pro 中自定义。 /// /// - 线段起点的水平坐标。 /// - 线段起点的垂直坐标。 /// - 线段终点的水平坐标。 /// - 线段终点的垂直坐标。 /// - 线段从起点到终点与边界框相交的第一个交点。 (如果未设置,则不计算交点) /// - 线段从终点到起点与边界框相交的第一个交点。 (如果未设置,则不计算交点) /// - 交点边界框切线的法线弧度。 [x: 第一个交点切线的法线弧度, y: 第二个交点切线的法线弧度] (如果未设置,则不计算法线) /// 线段从起点到终点相交的第一个自定义边界框的插槽。 /// DragonBones 5.0 /// zh_CN public Slot IntersectsSegment(float xA, float yA, float xB, float yB, Point intersectionPointA = null, Point intersectionPointB = null, Point normalRadians = null) { var isV = xA == xB; var dMin = 0.0f; var dMax = 0.0f; var intXA = 0.0f; var intYA = 0.0f; var intXB = 0.0f; var intYB = 0.0f; var intAN = 0.0f; var intBN = 0.0f; Slot intSlotA = null; Slot intSlotB = null; foreach (var slot in this._slots) { var intersectionCount = slot.IntersectsSegment(xA, yA, xB, yB, intersectionPointA, intersectionPointB, normalRadians); if (intersectionCount > 0) { if (intersectionPointA != null || intersectionPointB != null) { if (intersectionPointA != null) { var d = isV ? intersectionPointA.y - yA : intersectionPointA.x - xA; if (d < 0.0f) { d = -d; } if (intSlotA == null || d < dMin) { dMin = d; intXA = intersectionPointA.x; intYA = intersectionPointA.y; intSlotA = slot; if (normalRadians != null) { intAN = normalRadians.x; } } } if (intersectionPointB != null) { var d = intersectionPointB.x - xA; if (d < 0.0f) { 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. /// /// - The bone name. /// /// DragonBones 3.0 /// en_US /// /// - 获取特定的骨骼。 /// /// - 骨骼名称。 /// /// DragonBones 3.0 /// zh_CN public Bone GetBone(string name) { foreach (var bone in this._bones) { if (bone.name == name) { return bone; } } return null; } /// /// - Get a specific bone by the display. /// /// - The display object. /// /// DragonBones 3.0 /// en_US /// /// - 通过显示对象获取特定的骨骼。 /// /// - 显示对象。 /// /// DragonBones 3.0 /// zh_CN public Bone GetBoneByDisplay(object display) { var slot = this.GetSlotByDisplay(display); return slot != null ? slot.parent : null; } /// /// - Get a specific slot. /// /// - The slot name. /// /// DragonBones 3.0 /// en_US /// /// - 获取特定的插槽。 /// /// - 插槽名称。 /// /// DragonBones 3.0 /// zh_CN public Slot GetSlot(string name) { foreach (var slot in this._slots) { if (slot.name == name) { return slot; } } return null; } /// /// - Get a specific slot by the display. /// /// - The display object. /// /// DragonBones 3.0 /// en_US /// /// - 通过显示对象获取特定的插槽。 /// /// - 显示对象。 /// /// DragonBones 3.0 /// zh_CN public Slot GetSlotByDisplay(object display) { if (display != null) { foreach (var slot in this._slots) { if (slot.display == display) { return slot; } } } return null; } /// /// - Get all bones. /// /// /// DragonBones 3.0 /// en_US /// /// - 获取所有的骨骼。 /// /// /// DragonBones 3.0 /// zh_CN public List GetBones() { return this._bones; } /// /// - Get all slots. /// /// /// DragonBones 3.0 /// en_US /// /// - 获取所有的插槽。 /// /// /// DragonBones 3.0 /// zh_CN public List GetSlots() { return this._slots; } /// /// - Whether to flip the armature horizontally. /// /// DragonBones 5.5 /// en_US /// /// - 是否将骨架水平翻转。 /// /// DragonBones 5.5 /// zh_CN public bool flipX { get { return this._flipX; } set { if (this._flipX == value) { return; } this._flipX = value; this.InvalidUpdate(); } } /// /// - Whether to flip the armature vertically. /// /// DragonBones 5.5 /// en_US /// /// - 是否将骨架垂直翻转。 /// /// DragonBones 5.5 /// zh_CN public bool flipY { get { return this._flipY; } set { 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. /// /// /// TypeScript style, for reference only. ///
        ///     armature.cacheFrameRate = 24;
        /// 
///
/// /// /// DragonBones 4.5 /// en_US /// /// - 动画缓存帧率,当设置的值大于 0 的时,将会开启动画缓存。 /// 通过将动画数据缓存在内存中来提高运行性能,会有一定的内存开销。 /// 帧率不宜设置的过高,通常跟动画的帧率相当且低于程序运行的帧率。 /// 开启动画缓存后,某些功能将会失效,比如骨骼的 offset 属性等。 /// /// /// TypeScript 风格,仅供参考。 ///
        ///     armature.cacheFrameRate = 24;
        /// 
///
/// /// /// DragonBones 4.5 /// zh_CN public uint cacheFrameRate { get { return this._armatureData.cacheFrameRate; } set { if (this._armatureData.cacheFrameRate != value) { this._armatureData.CacheFrames(value); // Set child armature frameRate. foreach (var slot in this._slots) { var childArmature = slot.childArmature; if (childArmature != null) { childArmature.cacheFrameRate = value; } } } } } /// /// - The armature name. /// /// DragonBones 3.0 /// en_US /// /// - 骨架名称。 /// /// DragonBones 3.0 /// zh_CN public string name { get { return this._armatureData.name; } } /// /// - The armature data. /// /// /// DragonBones 4.5 /// en_US /// /// - 骨架数据。 /// /// /// DragonBones 4.5 /// zh_CN public ArmatureData armatureData { get { return this._armatureData; } } /// /// - The animation player. /// /// /// DragonBones 3.0 /// en_US /// /// - 动画播放器。 /// /// /// DragonBones 3.0 /// zh_CN public AnimationDB animation { get { return this._animation; } } /// public IArmatureProxy proxy { get { return this._proxy; } } /// /// - The EventDispatcher instance of the armature. /// /// DragonBones 4.5 /// en_US /// /// - 该骨架的 EventDispatcher 实例。 /// /// DragonBones 4.5 /// zh_CN public IEventDispatcher eventDispatcher { get { 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. /// /// DragonBones 3.0 /// en_US /// /// - 显示容器实例。 /// 插槽的显示对象都会以此显示容器为父级。 /// 根据渲染引擎的不同,类型会不同,通常是 DisplayObjectContainer 类型。 /// /// DragonBones 3.0 /// zh_CN public object display { get { return this._display; } } /// public object replacedTexture { get { return this._replacedTexture; } set { if (this._replacedTexture == value) { return; } if (this._replaceTextureAtlasData != null) { this._replaceTextureAtlasData.ReturnToPool(); this._replaceTextureAtlasData = null; } this._replacedTexture = value; foreach (var slot in this._slots) { slot.InvalidUpdate(); slot.Update(-1); } } } /// public WorldClock clock { get { return this._clock; } set { if (this._clock == value) { return; } if (this._clock != null) { this._clock.Remove(this); } this._clock = value; if (this._clock != null) { this._clock.Add(this); } // Update childArmature clock. foreach (var slot in this._slots) { var childArmature = slot.childArmature; if (childArmature != null) { childArmature.clock = this._clock; } } } } /// /// - Get the parent slot which the armature belongs to. /// /// /// DragonBones 4.5 /// en_US /// /// - 该骨架所属的父插槽。 /// /// /// DragonBones 4.5 /// zh_CN public Slot parent { get { return this._parent; } } } }