diff --git a/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs b/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs index 37fdbb9fd..4cd6d02af 100644 --- a/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs +++ b/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs @@ -32,15 +32,15 @@ using System; namespace Spine { public class AtlasAttachmentLoader : AttachmentLoader { - private Atlas atlas; + private Atlas[] atlasArray; - public AtlasAttachmentLoader (Atlas atlas) { - if (atlas == null) throw new ArgumentNullException("atlas cannot be null."); - this.atlas = atlas; + public AtlasAttachmentLoader (params Atlas[] atlasArray) { + if (atlasArray == null) throw new ArgumentNullException("atlas array cannot be null."); + this.atlasArray = atlasArray; } public RegionAttachment NewRegionAttachment (Skin skin, String name, String path) { - AtlasRegion region = atlas.FindRegion(path); + AtlasRegion region = FindRegion(path); if (region == null) throw new Exception("Region not found in atlas: " + path + " (region attachment: " + name + ")"); RegionAttachment attachment = new RegionAttachment(name); attachment.RendererObject = region; @@ -55,7 +55,7 @@ namespace Spine { } public MeshAttachment NewMeshAttachment (Skin skin, String name, String path) { - AtlasRegion region = atlas.FindRegion(path); + AtlasRegion region = FindRegion(path); if (region == null) throw new Exception("Region not found in atlas: " + path + " (mesh attachment: " + name + ")"); MeshAttachment attachment = new MeshAttachment(name); attachment.RendererObject = region; @@ -74,7 +74,7 @@ namespace Spine { } public SkinnedMeshAttachment NewSkinnedMeshAttachment (Skin skin, String name, String path) { - AtlasRegion region = atlas.FindRegion(path); + AtlasRegion region = FindRegion(path); if (region == null) throw new Exception("Region not found in atlas: " + path + " (skinned mesh attachment: " + name + ")"); SkinnedMeshAttachment attachment = new SkinnedMeshAttachment(name); attachment.RendererObject = region; @@ -95,5 +95,17 @@ namespace Spine { public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) { return new BoundingBoxAttachment(name); } + + public AtlasRegion FindRegion(string name) { + AtlasRegion region; + + for (int i = 0; i < atlasArray.Length; i++) { + region = atlasArray[i].FindRegion(name); + if (region != null) + return region; + } + + return null; + } } } diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs index 77b3d7350..ca9bb6153 100644 --- a/spine-csharp/src/SkeletonJson.cs +++ b/spine-csharp/src/SkeletonJson.cs @@ -42,8 +42,8 @@ namespace Spine { private AttachmentLoader attachmentLoader; public float Scale { get; set; } - public SkeletonJson (Atlas atlas) - : this(new AtlasAttachmentLoader(atlas)) { + public SkeletonJson (params Atlas[] atlasArray) + : this(new AtlasAttachmentLoader(atlasArray)) { } public SkeletonJson (AttachmentLoader attachmentLoader) { diff --git a/spine-unity/Assets/Examples/Scenes/Attributes and AtlasRegions.unity b/spine-unity/Assets/Examples/Scenes/Attributes and AtlasRegions.unity new file mode 100644 index 000000000..cc0f39f32 Binary files /dev/null and b/spine-unity/Assets/Examples/Scenes/Attributes and AtlasRegions.unity differ diff --git a/spine-unity/Assets/Examples/Scenes/Attributes and AtlasRegions.unity.meta b/spine-unity/Assets/Examples/Scenes/Attributes and AtlasRegions.unity.meta new file mode 100644 index 000000000..edbdffa80 --- /dev/null +++ b/spine-unity/Assets/Examples/Scenes/Attributes and AtlasRegions.unity.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 63212ccaf5776bd489cba58fb67a2233 +DefaultImporter: + userData: diff --git a/spine-unity/Assets/Examples/Scenes/Mix and Match.unity b/spine-unity/Assets/Examples/Scenes/Mix and Match.unity new file mode 100644 index 000000000..775cb6bf7 Binary files /dev/null and b/spine-unity/Assets/Examples/Scenes/Mix and Match.unity differ diff --git a/spine-unity/Assets/Examples/Scenes/Mix and Match.unity.meta b/spine-unity/Assets/Examples/Scenes/Mix and Match.unity.meta new file mode 100644 index 000000000..a220c5fd4 --- /dev/null +++ b/spine-unity/Assets/Examples/Scenes/Mix and Match.unity.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: c5673b83016f67a4c99772dfb7b3c437 +DefaultImporter: + userData: diff --git a/spine-unity/Assets/Examples/Scenes/Spineboy Movement.unity b/spine-unity/Assets/Examples/Scenes/Spineboy Movement.unity index c625ed8a6..bea46f183 100644 Binary files a/spine-unity/Assets/Examples/Scenes/Spineboy Movement.unity and b/spine-unity/Assets/Examples/Scenes/Spineboy Movement.unity differ diff --git a/spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs b/spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs index 1eb015e1e..761ac31af 100644 --- a/spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs +++ b/spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs @@ -71,11 +71,17 @@ public class BasicPlatformerController : MonoBehaviour { #if UNITY_4_5 [Header("Animation")] #endif + [SpineAnimation(dataField: "skeletonAnimation")] public string walkName = "Walk"; + [SpineAnimation(dataField: "skeletonAnimation")] public string runName = "Run"; + [SpineAnimation(dataField: "skeletonAnimation")] public string idleName = "Idle"; + [SpineAnimation(dataField: "skeletonAnimation")] public string jumpName = "Jump"; + [SpineAnimation(dataField: "skeletonAnimation")] public string fallName = "Fall"; + [SpineAnimation(dataField: "skeletonAnimation")] public string crouchName = "Crouch"; #if UNITY_4_5 diff --git a/spine-unity/Assets/Examples/Scripts/Chimera.cs b/spine-unity/Assets/Examples/Scripts/Chimera.cs new file mode 100644 index 000000000..27f831f86 --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/Chimera.cs @@ -0,0 +1,51 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +/***************************************************************************** + * Basic Platformer Controller created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ +using UnityEngine; +using System.Collections; + +public class Chimera : MonoBehaviour { + + public SkeletonDataAsset skeletonDataSource; + + [SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skeletonDataSource")] + public string attachmentPath; + + [SpineSlot] + public string targetSlot; + + void Start() { + GetComponent().skeleton.FindSlot(targetSlot).Attachment = SpineAttachment.GetAttachment(attachmentPath, skeletonDataSource); + } +} diff --git a/spine-unity/Assets/Examples/Scripts/Chimera.cs.meta b/spine-unity/Assets/Examples/Scripts/Chimera.cs.meta new file mode 100644 index 000000000..ae5a4019f --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/Chimera.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5053fe97a7657b5418b0c307b7338b0c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs b/spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs new file mode 100644 index 000000000..c638f696e --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs @@ -0,0 +1,66 @@ +using UnityEngine; +using System.Collections; + +public class DynamicSpineBone : MonoBehaviour { + + public Transform speedReference; + + [SpineBone] + public string boneName; + + [Range(-90, 90)] + public float minRotation = -45; + [Range(-90, 90)] + public float maxRotation = 45; + + [Range(-2000, 2000)] + public float rotationFactor = 300; + + [Range(5, 30)] + public float returnSpeed = 10; + + [Range(100, 1000)] + public float boneSpeed = 300; + + public float returnThreshhold = 0.01f; + + public bool useAcceleration; + + + SkeletonAnimation skeletonAnimation; + float goalRotation; + Spine.Bone bone; + Vector3 velocity; + Vector3 acceleration; + Vector3 lastPosition; + + void Start() { + if (speedReference == null) + speedReference = transform; + + skeletonAnimation = GetComponent(); + bone = SpineBone.GetBone(boneName, skeletonAnimation); + skeletonAnimation.UpdateLocal += UpdateLocal; + lastPosition = speedReference.position; + } + + void FixedUpdate() { + acceleration = (speedReference.position - lastPosition) - velocity; + velocity = speedReference.position - lastPosition; + lastPosition = speedReference.position; + } + + void UpdateLocal(SkeletonAnimation animation) { + Vector3 vec = useAcceleration ? acceleration : velocity; + + if (Mathf.Abs(vec.x) < returnThreshhold) + goalRotation = Mathf.Lerp(goalRotation, 0, returnSpeed * Time.deltaTime); + else + goalRotation += vec.x * rotationFactor * Time.deltaTime * (bone.WorldFlipX ? -1 : 1); + + goalRotation = Mathf.Clamp(goalRotation, minRotation, maxRotation); + + bone.Rotation = Mathf.Lerp(bone.Rotation, bone.Rotation + goalRotation, boneSpeed * Time.deltaTime); + + } +} diff --git a/spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs.meta b/spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs.meta new file mode 100644 index 000000000..6aac53582 --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7dae3f4db9a24bf4abe2059526bfd689 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs b/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs new file mode 100644 index 000000000..655445869 --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs @@ -0,0 +1,85 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +/***************************************************************************** + * FootSoldierExample created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ +using UnityEngine; +using System.Collections; + +public class FootSoldierExample : MonoBehaviour { + [SpineAnimation("Idle")] + public string idleAnimation; + + [SpineAnimation] + public string attackAnimation; + + [SpineSlot] + public string eyesSlot; + + [SpineAttachment(currentSkinOnly: true, slotField: "eyesSlot")] + public string eyesOpenAttachment; + + [SpineAttachment(currentSkinOnly: true, slotField: "eyesSlot")] + public string blinkAttachment; + + [Range(0, 0.2f)] + public float blinkDuration = 0.05f; + + private SkeletonAnimation skeletonAnimation; + + void Awake() { + skeletonAnimation = GetComponent(); + } + + void Start() { + skeletonAnimation.state.SetAnimation(0, idleAnimation, true); + StartCoroutine("Blink"); + } + + void Update() { + if (Input.GetKey(KeyCode.Space)) { + if (skeletonAnimation.state.GetCurrent(0).Animation.Name != attackAnimation) { + skeletonAnimation.state.SetAnimation(0, attackAnimation, false); + skeletonAnimation.state.AddAnimation(0, idleAnimation, true, 0); + } + } + } + + IEnumerator Blink() { + while (true) { + yield return new WaitForSeconds(Random.Range(0.25f, 3f)); + skeletonAnimation.skeleton.SetAttachment(eyesSlot, blinkAttachment); + yield return new WaitForSeconds(blinkDuration); + skeletonAnimation.skeleton.SetAttachment(eyesSlot, eyesOpenAttachment); + } + } +} diff --git a/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs.meta b/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs.meta new file mode 100644 index 000000000..c82e3bd15 --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3c826b50b0cfee343be3bdbbf59d0f7c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/spine-unity/Assets/Examples/Scripts/SpineboyController.cs b/spine-unity/Assets/Examples/Scripts/SpineboyController.cs index 4cebc7de3..064152df4 100644 --- a/spine-unity/Assets/Examples/Scripts/SpineboyController.cs +++ b/spine-unity/Assets/Examples/Scripts/SpineboyController.cs @@ -1,4 +1,38 @@ -using UnityEngine; +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +/***************************************************************************** + * SpineboyController created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ +using UnityEngine; using System.Collections; [RequireComponent(typeof(SkeletonAnimation), typeof(Rigidbody2D))] diff --git a/spine-unity/Assets/Examples/Spine/Dragon/dragon_SkeletonData.asset b/spine-unity/Assets/Examples/Spine/Dragon/dragon_SkeletonData.asset index 8134c651e..8f43b99b9 100644 Binary files a/spine-unity/Assets/Examples/Spine/Dragon/dragon_SkeletonData.asset and b/spine-unity/Assets/Examples/Spine/Dragon/dragon_SkeletonData.asset differ diff --git a/spine-unity/Assets/Examples/Spine/Eyes/eyes_SkeletonData.asset b/spine-unity/Assets/Examples/Spine/Eyes/eyes_SkeletonData.asset index a87067b98..77a24614b 100644 Binary files a/spine-unity/Assets/Examples/Spine/Eyes/eyes_SkeletonData.asset and b/spine-unity/Assets/Examples/Spine/Eyes/eyes_SkeletonData.asset differ diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier.meta b/spine-unity/Assets/Examples/Spine/FootSoldier.meta new file mode 100644 index 000000000..be62dadf6 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 7a709e690449c1b40b198bb86f707c41 +folderAsset: yes +DefaultImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment.meta b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment.meta new file mode 100644 index 000000000..bb211ad21 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: e90f1603e5c99c745a28d42e61afe5b2 +folderAsset: yes +DefaultImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment.atlas.txt b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment.atlas.txt new file mode 100644 index 000000000..1d3beb679 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment.atlas.txt @@ -0,0 +1,34 @@ + +Equipment.png +size: 512,128 +format: RGBA8888 +filter: Linear,Linear +repeat: none +Equipment/shield1 + rotate: true + xy: 220, 33 + size: 71, 118 + orig: 256, 256 + offset: 92, 69 + index: -1 +Equipment/shield2 + rotate: true + xy: 340, 22 + size: 82, 111 + orig: 256, 256 + offset: 87, 72 + index: -1 +Equipment/sword1 + rotate: false + xy: 2, 2 + size: 161, 31 + orig: 512, 256 + offset: 217, 112 + index: -1 +Equipment/sword4 + rotate: false + xy: 2, 35 + size: 216, 69 + orig: 512, 256 + offset: 200, 94 + index: -1 diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment.atlas.txt.meta b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment.atlas.txt.meta new file mode 100644 index 000000000..0b8cff140 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment.atlas.txt.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 4f0639ff8bc42314d8d62ee0f7ba541f +TextScriptImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment.png b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment.png new file mode 100644 index 000000000..f564999af Binary files /dev/null and b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment.png differ diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment.png.meta b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment.png.meta new file mode 100644 index 000000000..1f7655474 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment.png.meta @@ -0,0 +1,47 @@ +fileFormatVersion: 2 +guid: ddb89f63d0296cf4f8572b0448bb6b30 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 0 + textureType: -1 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment_Atlas.asset b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment_Atlas.asset new file mode 100644 index 000000000..365c78c1f Binary files /dev/null and b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment_Atlas.asset differ diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment_Atlas.asset.meta b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment_Atlas.asset.meta new file mode 100644 index 000000000..4b102f19e --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment_Atlas.asset.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: c574489dd067c2b4cb4dc165a4c410cc +NativeFormatImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment_Material.mat b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment_Material.mat new file mode 100644 index 000000000..b3e55407f Binary files /dev/null and b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment_Material.mat differ diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment_Material.mat.meta b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment_Material.mat.meta new file mode 100644 index 000000000..c9a613c0d --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/Equipment/Equipment_Material.mat.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: e6e388faa521c96449984cfa4b60d74e +NativeFormatImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.atlas.txt b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.atlas.txt new file mode 100644 index 000000000..888211fc8 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.atlas.txt @@ -0,0 +1,90 @@ + +FS_White.png +size: 256,256 +format: RGBA8888 +filter: Linear,Linear +repeat: none +White/arm + rotate: false + xy: 143, 156 + size: 111, 98 + orig: 111, 98 + offset: 0, 0 + index: -1 +White/arm 2 + rotate: false + xy: 95, 36 + size: 46, 79 + orig: 46, 79 + offset: 0, 0 + index: -1 +White/body + rotate: false + xy: 2, 12 + size: 91, 103 + orig: 91, 103 + offset: 0, 0 + index: -1 +White/eyes + rotate: true + xy: 195, 87 + size: 67, 31 + orig: 67, 31 + offset: 0, 0 + index: -1 +White/eyes blink + rotate: true + xy: 228, 87 + size: 67, 22 + orig: 67, 22 + offset: 0, 0 + index: -1 +White/feet + rotate: false + xy: 95, 2 + size: 50, 32 + orig: 50, 32 + offset: 0, 0 + index: -1 +White/feet 2 + rotate: false + xy: 193, 58 + size: 55, 27 + orig: 55, 27 + offset: 0, 0 + index: -1 +White/hand + rotate: false + xy: 147, 9 + size: 32, 28 + orig: 32, 28 + offset: 0, 0 + index: -1 +White/head 1 + rotate: false + xy: 2, 117 + size: 139, 137 + orig: 139, 137 + offset: 0, 0 + index: -1 +White/leg + rotate: false + xy: 143, 39 + size: 48, 55 + orig: 48, 55 + offset: 0, 0 + index: -1 +White/leg 2 + rotate: false + xy: 143, 96 + size: 50, 58 + orig: 50, 58 + offset: 0, 0 + index: -1 +White/mouth + rotate: false + xy: 193, 35 + size: 28, 21 + orig: 28, 21 + offset: 0, 0 + index: -1 diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.atlas.txt.meta b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.atlas.txt.meta new file mode 100644 index 000000000..0f2b01ac2 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.atlas.txt.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 5ca7c05342912804eb0a2fd5bbe85b58 +TextScriptImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.png b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.png new file mode 100644 index 000000000..d79ebb167 Binary files /dev/null and b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.png differ diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.png.meta b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.png.meta new file mode 100644 index 000000000..460f78c1f --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.png.meta @@ -0,0 +1,47 @@ +fileFormatVersion: 2 +guid: 57b57f94df266f94ea0981915a4472e1 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 0 + textureType: -1 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White_Atlas.asset b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White_Atlas.asset new file mode 100644 index 000000000..d39d422a0 Binary files /dev/null and b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White_Atlas.asset differ diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White_Atlas.asset.meta b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White_Atlas.asset.meta new file mode 100644 index 000000000..9eba9339a --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White_Atlas.asset.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: e3c7e74834cd8424f8735ba05e94a688 +NativeFormatImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White_Material.mat b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White_Material.mat new file mode 100644 index 000000000..facc2bd81 Binary files /dev/null and b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White_Material.mat differ diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White_Material.mat.meta b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White_Material.mat.meta new file mode 100644 index 000000000..146d15988 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/FS_White_Material.mat.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 5a3598dafa118754db95756064347da7 +NativeFormatImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier.json b/spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier.json new file mode 100644 index 000000000..75e7be0bb --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier.json @@ -0,0 +1,1754 @@ +{ +"skeleton": { "hash": "uZfvh80BNvvngM3EGMfAkHebg00", "spine": "2.1.08", "width": 147.68, "height": 268.92, "images": "./images/" }, +"bones": [ + { "name": "Root" }, + { "name": "Hip", "parent": "Root", "x": -0.93, "y": 73.4 }, + { "name": "Body", "parent": "Hip", "length": 60.98, "x": 2.46, "y": -7.69, "rotation": 89.52 }, + { "name": "Leg", "parent": "Hip", "length": 31.39, "x": -20.32, "y": -13.85, "rotation": -105.82 }, + { "name": "Leg2", "parent": "Hip", "length": 31.09, "x": 22.48, "y": -12.01, "rotation": -74.17 }, + { "name": "Arm", "parent": "Body", "length": 51.63, "x": 49.91, "y": 37.35, "rotation": 166.67 }, + { "name": "Arm2", "parent": "Body", "length": 52.61, "x": 53.81, "y": -28.52, "rotation": -157.17 }, + { "name": "Feet", "parent": "Leg", "length": 15.4, "x": 39.56, "y": 1.59, "rotation": 14.56 }, + { "name": "Feet2", "parent": "Leg2", "length": 12.32, "x": 41.33, "y": 0.12, "rotation": -17.19 }, + { "name": "Head", "parent": "Body", "length": 65.29, "x": 73.6, "y": 1.09, "rotation": -88.23 }, + { "name": "Shield", "parent": "Arm", "x": 45.01, "y": -2.1, "rotation": 123.56 }, + { "name": "Weapon", "parent": "Arm2", "length": 137.65, "x": 48.2, "y": 12.78, "rotation": 92.5 } +], +"slots": [ + { "name": "Arm2", "bone": "Arm2", "attachment": "Arm2" }, + { "name": "Weapon", "bone": "Weapon" }, + { "name": "Hand", "bone": "Arm2", "attachment": "Hand" }, + { "name": "Leg2", "bone": "Leg2", "attachment": "Leg2" }, + { "name": "Feet2", "bone": "Feet2", "attachment": "Feet2" }, + { "name": "Leg", "bone": "Leg", "attachment": "Leg" }, + { "name": "Feet", "bone": "Feet", "attachment": "Feet" }, + { "name": "Body", "bone": "Body", "attachment": "body" }, + { "name": "Arm", "bone": "Arm", "attachment": "Arm" }, + { "name": "Head", "bone": "Head", "attachment": "Head" }, + { "name": "Eyes", "bone": "Head", "attachment": "Open" }, + { "name": "Shield", "bone": "Shield" }, + { "name": "Mouth", "bone": "Head", "attachment": "Closed" } +], +"skins": { + "default": {}, + "White": { + "Arm": { + "Arm": { "name": "arm", "path": "White/arm", "x": 21.18, "y": 21.04, "rotation": 109.17, "width": 111, "height": 98 } + }, + "Arm2": { + "Arm2": { "name": "arm 2", "path": "White/arm 2", "x": 23.03, "y": -1.29, "rotation": 78.03, "width": 46, "height": 79 } + }, + "Body": { + "body": { "path": "White/body", "x": 23.73, "y": 7.21, "rotation": -89.52, "width": 91, "height": 103 } + }, + "Eyes": { + "Blink": { "path": "White/eyes blink", "x": 10.41, "y": 31.16, "rotation": -1.13, "width": 67, "height": 22 }, + "Open": { "path": "White/eyes", "x": 11.07, "y": 26.12, "rotation": -4.55, "width": 67, "height": 31 } + }, + "Feet": { + "Feet": { "name": "feet", "path": "White/feet", "x": 6.08, "y": -1.36, "rotation": 91.26, "width": 50, "height": 32 } + }, + "Feet2": { + "Feet2": { "name": "feet 2", "path": "White/feet 2", "x": 4.26, "y": -4.34, "rotation": 91.37, "width": 55, "height": 27 } + }, + "Hand": { + "Hand": { "name": "hand", "path": "White/hand", "x": 49.06, "y": 2.06, "rotation": 67.64, "width": 32, "height": 28 } + }, + "Head": { + "Head": { "name": "head 1", "path": "White/head 1", "x": -3.5, "y": 58.44, "rotation": -2.76, "width": 139, "height": 137 } + }, + "Leg": { + "Leg": { "name": "leg", "path": "White/leg", "x": 16.86, "y": -4.3, "rotation": 104.82, "width": 48, "height": 55 } + }, + "Leg2": { + "Leg2": { "name": "leg 2", "path": "White/leg 2", "x": 16.44, "y": -2.11, "rotation": 74.17, "width": 50, "height": 58 } + }, + "Mouth": { + "Closed": { "path": "White/mouth", "x": 10.96, "y": 3.69, "rotation": -1.29, "width": 28, "height": 21 } + } + } +}, +"events": { + "Hit": {} +}, +"animations": { + "Attack": { + "bones": { + "Arm": { + "rotate": [ + { + "time": 0, + "angle": -10.02, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.3333, "angle": -50.06 }, + { "time": 0.4, "angle": -15.89 }, + { "time": 0.4666, "angle": -29.45, "curve": "stepped" }, + { + "time": 0.6, + "angle": -29.45, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8333, "angle": -10.02 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.3333, "x": 2.53, "y": -1.01 }, + { "time": 0.4666, "x": -5.05, "y": -2.57, "curve": "stepped" }, + { + "time": 0.6, + "x": -5.05, + "y": -2.57, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8333, "x": 0, "y": 0 } + ] + }, + "Root": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Hip": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.3333, "angle": 0, "curve": "stepped" }, + { "time": 0.8333, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.3333, "x": 0, "y": 0 }, + { "time": 0.4666, "x": 9.25, "y": -5.94, "curve": "stepped" }, + { + "time": 0.6, + "x": 9.25, + "y": -5.94, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8333, "x": 0, "y": 0 } + ] + }, + "Body": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.3333, "angle": 8.25 }, + { "time": 0.4666, "angle": -15.92, "curve": "stepped" }, + { + "time": 0.6, + "angle": -15.92, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8333, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.3333, "x": -1.07, "y": 3.56 }, + { "time": 0.8333, "x": 0, "y": 0 } + ] + }, + "Leg": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.3333, "angle": 0 }, + { "time": 0.4666, "angle": -18.61, "curve": "stepped" }, + { + "time": 0.6, + "angle": -18.61, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8333, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.3333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8333, "x": 0, "y": 0 } + ] + }, + "Leg2": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.3333, "angle": 15.02 }, + { "time": 0.4666, "angle": 4.71, "curve": "stepped" }, + { + "time": 0.6, + "angle": 4.71, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8333, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.3333, "x": -2.61, "y": 4.37 }, + { "time": 0.4666, "x": 5.74, "y": 4.37, "curve": "stepped" }, + { + "time": 0.6, + "x": 5.74, + "y": 4.37, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8333, "x": 0, "y": 0 } + ] + }, + "Arm2": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.3333, "angle": 148.07 }, + { "time": 0.4666, "angle": 335.98, "curve": "stepped" }, + { + "time": 0.6, + "angle": 335.98, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8333, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.3333, "x": 4.5, "y": -0.72 }, + { "time": 0.4666, "x": -1.04, "y": -2.35, "curve": "stepped" }, + { + "time": 0.6, + "x": -1.04, + "y": -2.35, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8333, "x": 0, "y": 0 } + ] + }, + "Feet": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.3333, "angle": 0 }, + { "time": 0.4666, "angle": 18.36, "curve": "stepped" }, + { + "time": 0.6, + "angle": 18.36, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8333, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.3333, "x": 0, "y": 0 }, + { "time": 0.4666, "x": -1.43, "y": 2.2, "curve": "stepped" }, + { + "time": 0.6, + "x": -1.43, + "y": 2.2, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8333, "x": 0, "y": 0 } + ] + }, + "Feet2": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.3333, "angle": 0 }, + { "time": 0.4666, "angle": -2.99, "curve": "stepped" }, + { + "time": 0.6, + "angle": -2.99, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8333, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.3333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.8333, "x": 0, "y": 0 } + ] + }, + "Head": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.3333, "angle": 19.17 }, + { "time": 0.4666, "angle": -2.33 }, + { "time": 0.8333, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.3333, "x": -1.36, "y": -0.46 }, + { "time": 0.8333, "x": 0, "y": 0 } + ] + }, + "Weapon": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + } + }, + "events": [ + { "time": 0.4333, "name": "Hit" } + ] + }, + "DeathBackward": { + "bones": { + "Root": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Hip": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.3333, "angle": 87.75 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.2, "x": -50.02, "y": -6.59 }, + { "time": 0.3333, "x": -83.36, "y": -37.67 } + ] + }, + "Body": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.3333, "x": 5.06, "y": -0.04 } + ] + }, + "Leg": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.3333, "angle": 33.36 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Leg2": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.3333, "angle": -48.11 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Arm": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.3333, "angle": -346.22 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.3333, "x": -4.06, "y": -12.11 } + ] + }, + "Arm2": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.3333, "angle": 278.31 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Feet": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.3333, "angle": -46.46 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Feet2": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Head": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.3333, "angle": 17.02 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.3333, "x": -3.67, "y": -21.86 } + ] + }, + "Weapon": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.3333, "angle": -38.52 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + } + } + }, + "DeathForward": { + "bones": { + "Root": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Hip": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.758, 0.67 ] + }, + { "time": 0.3333, "angle": -87.3 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.243, 0, 0.638, 0.47 ] + }, + { + "time": 0.2, + "x": 36.63, + "y": 0.08, + "curve": [ 0.386, 0.35, 0.748, 0.73 ] + }, + { "time": 0.3333, "x": 74.83, "y": -43.12 } + ] + }, + "Body": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Leg": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.758, 0.67 ] + }, + { "time": 0.3333, "angle": 52.39 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.758, 0.67 ] + }, + { "time": 0.3333, "x": -4.47, "y": 3.29 } + ] + }, + "Leg2": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.758, 0.67 ] + }, + { "time": 0.3333, "angle": -34.64 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Arm": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.758, 0.67 ] + }, + { "time": 0.3333, "angle": -306.5 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.3333, "x": -2.42, "y": -20.41 } + ] + }, + "Arm2": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.758, 0.67 ] + }, + { "time": 0.3333, "angle": 160 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.3333, "x": -0.79, "y": 16.15 } + ] + }, + "Feet": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.758, 0.67 ] + }, + { "time": 0.3333, "angle": -19.93 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.758, 0.67 ] + }, + { "time": 0.3333, "x": -0.19, "y": -2.03 } + ] + }, + "Feet2": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.758, 0.67 ] + }, + { "time": 0.3333, "angle": 30.89 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Head": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.758, 0.67 ] + }, + { "time": 0.3333, "angle": 47.13 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Weapon": { + "rotate": [ + { + "time": 0, + "angle": 0, + "curve": [ 0.25, 0, 0.758, 0.67 ] + }, + { "time": 0.3333, "angle": 43.72 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + } + } + }, + "Idle": { + "bones": { + "Body": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.5, + "x": 0, + "y": -3.51, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 1, "x": 0, "y": 0 } + ] + }, + "Head": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": -1.03, + "y": 0, + "curve": [ 0.212, 0.29, 0.75, 1 ] + }, + { + "time": 0.1333, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6333, + "x": -1.75, + "y": -0.01, + "curve": [ 0.25, 0, 0.755, 0.68 ] + }, + { "time": 1, "x": -1.03, "y": 0 } + ] + }, + "Arm": { + "rotate": [ + { + "time": 0, + "angle": -10.63, + "curve": [ 0.337, 0.34, 0.757, 1 ] + }, + { + "time": 0.3666, + "angle": -13.14, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8666, "angle": -10 }, + { "time": 1, "angle": -10.63 } + ], + "translate": [ + { + "time": 0, + "x": -0.44, + "y": 0, + "curve": [ 0.337, 0.34, 0.757, 1 ] + }, + { + "time": 0.3666, + "x": -2.17, + "y": -0.01, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8666, "x": 0, "y": 0 }, + { "time": 1, "x": -0.44, "y": 0 } + ] + }, + "Arm2": { + "rotate": [ + { + "time": 0, + "angle": -0.47, + "curve": [ 0.337, 0.34, 0.757, 1 ] + }, + { + "time": 0.3666, + "angle": -2.34, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8666, "angle": 0 }, + { "time": 1, "angle": -0.47 } + ], + "translate": [ + { + "time": 0, + "x": -0.46, + "y": 0, + "curve": [ 0.337, 0.34, 0.757, 1 ] + }, + { + "time": 0.3666, + "x": -2.29, + "y": -0.01, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.8666, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 1, "x": -0.46, "y": 0 } + ] + }, + "Leg": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.1, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": -0.26, + "curve": [ 0.374, 0.61, 0.715, 1 ] + }, + { "time": 0.1, "x": 0, "y": 0 }, + { + "time": 0.6, + "x": 0, + "y": -2.03, + "curve": [ 0.242, 0, 0.679, 0.71 ] + }, + { "time": 1, "x": 0, "y": -0.26 } + ] + }, + "Feet": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.1, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": -0.25, + "y": -0.07, + "curve": [ 0.374, 0.61, 0.715, 1 ] + }, + { + "time": 0.1, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6, + "x": -1.95, + "y": -0.55, + "curve": [ 0.242, 0, 0.679, 0.71 ] + }, + { "time": 1, "x": -0.25, "y": -0.07 } + ] + }, + "Leg2": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.1, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": -0.33, + "curve": [ 0.374, 0.61, 0.715, 1 ] + }, + { "time": 0.1, "x": 0, "y": 0 }, + { + "time": 0.6, + "x": 0, + "y": -2.54, + "curve": [ 0.242, 0, 0.679, 0.71 ] + }, + { "time": 1, "x": 0, "y": -0.33 } + ] + }, + "Feet2": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.1, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": -0.31, + "y": 0.09, + "curve": [ 0.374, 0.61, 0.715, 1 ] + }, + { + "time": 0.1, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6, + "x": -2.44, + "y": 0.69, + "curve": [ 0.242, 0, 0.679, 0.71 ] + }, + { "time": 1, "x": -0.31, "y": 0.09 } + ] + }, + "Root": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Hip": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Weapon": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + } + } + }, + "Idle2": { + "bones": { + "Body": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.5, + "x": 0, + "y": -3.51, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 1, "x": 0, "y": 0 } + ] + }, + "Head": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.1666, "angle": 22.27, "curve": "stepped" }, + { "time": 0.7333, "angle": 22.27 }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": -1.03, + "y": 0, + "curve": [ 0.212, 0.29, 0.75, 1 ] + }, + { + "time": 0.1333, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.4, + "x": -1.75, + "y": -0.01, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 1, "x": -1.03, "y": 0 } + ] + }, + "Arm": { + "rotate": [ + { + "time": 0, + "angle": -10.63, + "curve": [ 0.337, 0.34, 0.757, 1 ] + }, + { + "time": 0.3666, + "angle": -13.14, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8666, "angle": -10 }, + { "time": 1, "angle": -10.63 } + ], + "translate": [ + { + "time": 0, + "x": -0.44, + "y": 0, + "curve": [ 0.337, 0.34, 0.757, 1 ] + }, + { + "time": 0.3666, + "x": -2.17, + "y": -0.01, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8666, "x": 0, "y": 0 }, + { "time": 1, "x": -0.44, "y": 0 } + ] + }, + "Arm2": { + "rotate": [ + { + "time": 0, + "angle": -0.47, + "curve": [ 0.315, 0.27, 0.661, 0.65 ] + }, + { "time": 0.1666, "angle": 79.53, "curve": "stepped" }, + { + "time": 0.7333, + "angle": 79.53, + "curve": [ 0.337, 0.34, 0.757, 1 ] + }, + { "time": 0.9666, "angle": -0.47, "curve": "stepped" }, + { "time": 1, "angle": -0.47 } + ], + "translate": [ + { "time": 0, "x": -0.46, "y": 0, "curve": "stepped" }, + { "time": 1, "x": -0.46, "y": 0 } + ] + }, + "Leg": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.1, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": -0.26, + "curve": [ 0.374, 0.61, 0.715, 1 ] + }, + { "time": 0.1, "x": 0, "y": 0 }, + { + "time": 0.6, + "x": 0, + "y": -2.03, + "curve": [ 0.242, 0, 0.679, 0.71 ] + }, + { "time": 1, "x": 0, "y": -0.26 } + ] + }, + "Feet": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.1, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": -0.25, + "y": -0.07, + "curve": [ 0.374, 0.61, 0.715, 1 ] + }, + { + "time": 0.1, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6, + "x": -1.95, + "y": -0.55, + "curve": [ 0.242, 0, 0.679, 0.71 ] + }, + { "time": 1, "x": -0.25, "y": -0.07 } + ] + }, + "Leg2": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.1, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": -0.33, + "curve": [ 0.374, 0.61, 0.715, 1 ] + }, + { "time": 0.1, "x": 0, "y": 0 }, + { + "time": 0.6, + "x": 0, + "y": -2.54, + "curve": [ 0.242, 0, 0.679, 0.71 ] + }, + { "time": 1, "x": 0, "y": -0.33 } + ] + }, + "Feet2": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.1, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": -0.31, + "y": 0.09, + "curve": [ 0.374, 0.61, 0.715, 1 ] + }, + { + "time": 0.1, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6, + "x": -2.44, + "y": 0.69, + "curve": [ 0.242, 0, 0.679, 0.71 ] + }, + { "time": 1, "x": -0.31, "y": 0.09 } + ] + }, + "Root": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Hip": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Weapon": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.1666, "angle": -15.07, "curve": "stepped" }, + { "time": 0.7333, "angle": -15.07 }, + { "time": 0.9666, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + } + } + }, + "Idle3": { + "bones": { + "Body": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.5, + "x": 0, + "y": -3.51, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 1, "x": 0, "y": 0 } + ] + }, + "Head": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": -1.03, + "y": 0, + "curve": [ 0.212, 0.29, 0.75, 1 ] + }, + { + "time": 0.1333, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6333, + "x": -1.75, + "y": -0.01, + "curve": [ 0.25, 0, 0.755, 0.68 ] + }, + { "time": 1, "x": -1.03, "y": 0 } + ] + }, + "Arm": { + "rotate": [ + { + "time": 0, + "angle": -10.63, + "curve": [ 0.337, 0.34, 0.757, 1 ] + }, + { + "time": 0.3666, + "angle": -13.14, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8666, "angle": -10 }, + { "time": 1, "angle": -10.63 } + ], + "translate": [ + { + "time": 0, + "x": -0.44, + "y": 0, + "curve": [ 0.337, 0.34, 0.757, 1 ] + }, + { + "time": 0.3666, + "x": -2.17, + "y": -0.01, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.8666, "x": 0, "y": 0 }, + { "time": 1, "x": -0.44, "y": 0 } + ] + }, + "Arm2": { + "rotate": [ + { + "time": 0, + "angle": -0.47, + "curve": [ 0.322, 0.29, 0.658, 0.63 ] + }, + { + "time": 0.1666, + "angle": 322.37, + "curve": [ 0.339, 0.35, 0.673, 0.68 ] + }, + { + "time": 0.2666, + "angle": 335.29, + "curve": [ 0.336, 0.34, 0.67, 0.67 ] + }, + { + "time": 0.3666, + "angle": 322.1, + "curve": [ 0.381, 0.58, 0.729, 1 ] + }, + { + "time": 0.4666, + "angle": 331.22, + "curve": [ 0.333, 0.33, 0.667, 0.66 ] + }, + { + "time": 0.5666, + "angle": 322.25, + "curve": [ 0.33, 0.32, 0.664, 0.66 ] + }, + { + "time": 0.6666, + "angle": 322.37, + "curve": [ 0.386, 0, 0.755, 1 ] + }, + { "time": 1, "angle": -0.47 } + ], + "translate": [ + { + "time": 0, + "x": -0.46, + "y": 0, + "curve": [ 0.322, 0.29, 0.658, 0.63 ] + }, + { + "time": 0.1666, + "x": -12.22, + "y": -0.1, + "curve": [ 0.332, 0.33, 0.682, 0.71 ] + }, + { + "time": 0.4333, + "x": -10.84, + "y": -0.08, + "curve": [ 0.381, 0.58, 0.729, 1 ] + }, + { + "time": 0.6666, + "x": -12.22, + "y": -0.1, + "curve": [ 0.361, 0.43, 0.755, 1 ] + }, + { "time": 1, "x": -0.46, "y": 0 } + ] + }, + "Leg": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.1, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": -0.26, + "curve": [ 0.374, 0.61, 0.715, 1 ] + }, + { "time": 0.1, "x": 0, "y": 0 }, + { + "time": 0.6, + "x": 0, + "y": -2.03, + "curve": [ 0.242, 0, 0.679, 0.71 ] + }, + { "time": 1, "x": 0, "y": -0.26 } + ] + }, + "Feet": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.1, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": -0.25, + "y": -0.07, + "curve": [ 0.374, 0.61, 0.715, 1 ] + }, + { + "time": 0.1, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6, + "x": -1.95, + "y": -0.55, + "curve": [ 0.242, 0, 0.679, 0.71 ] + }, + { "time": 1, "x": -0.25, "y": -0.07 } + ] + }, + "Leg2": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.1, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": -0.33, + "curve": [ 0.374, 0.61, 0.715, 1 ] + }, + { "time": 0.1, "x": 0, "y": 0 }, + { + "time": 0.6, + "x": 0, + "y": -2.54, + "curve": [ 0.242, 0, 0.679, 0.71 ] + }, + { "time": 1, "x": 0, "y": -0.33 } + ] + }, + "Feet2": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.1, "angle": 0, "curve": "stepped" }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { + "time": 0, + "x": -0.31, + "y": 0.09, + "curve": [ 0.374, 0.61, 0.715, 1 ] + }, + { + "time": 0.1, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.6, + "x": -2.44, + "y": 0.69, + "curve": [ 0.242, 0, 0.679, 0.71 ] + }, + { "time": 1, "x": -0.31, "y": 0.09 } + ] + }, + "Root": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Hip": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Weapon": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.1666, "angle": -7.41, "curve": "stepped" }, + { "time": 0.6666, "angle": -7.41 }, + { "time": 1, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 1, "x": 0, "y": 0 } + ] + } + } + }, + "Move": { + "bones": { + "Arm": { + "rotate": [ + { + "time": 0, + "angle": -345.33, + "curve": [ 0.235, 0.29, 0.75, 1 ] + }, + { + "time": 0.2, + "angle": -8.71, + "curve": [ 0.25, 0, 0.592, 1 ] + }, + { + "time": 0.5333, + "angle": -338.66, + "curve": [ 0.25, 0, 0.715, 0.63 ] + }, + { "time": 0.6666, "angle": -345.33 } + ], + "translate": [ + { + "time": 0, + "x": -2.81, + "y": -0.06, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.2, + "x": -0.69, + "y": -0.92, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.3333, + "x": -2.81, + "y": -0.06, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.5333, + "x": 0.36, + "y": 0.38, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.6666, "x": -2.81, "y": -0.06 } + ] + }, + "Leg": { + "rotate": [ + { + "time": 0, + "angle": 26.56, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.2, + "angle": 52.94, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.5333, + "angle": -1.83, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.6666, "angle": 26.56 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.2, + "x": 19.08, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.6666, "x": 0, "y": 0 } + ] + }, + "Leg2": { + "rotate": [ + { + "time": 0, + "angle": -33.11, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.2, + "angle": -49.48, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.5333, + "angle": -1.66, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.6666, "angle": -33.11 } + ], + "translate": [ + { + "time": 0, + "x": 0, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.2, + "x": -22.21, + "y": 0, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.4, + "x": -11.24, + "y": 6.26, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.6666, "x": 0, "y": 0 } + ] + }, + "Feet": { + "rotate": [ + { + "time": 0, + "angle": -23.86, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.2, + "angle": -51.25, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.3333, + "angle": -29.88, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.4333, + "angle": -14.06, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.5333, + "angle": -18.03, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.6666, "angle": -23.48 } + ], + "translate": [ + { + "time": 0, + "x": -0.84, + "y": -4.45, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.2, "x": -1.03, "y": -4.7, "curve": "stepped" }, + { + "time": 0.5333, + "x": -1.03, + "y": -4.7, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.6666, "x": -0.84, "y": -4.45 } + ] + }, + "Feet2": { + "rotate": [ + { + "time": 0, + "angle": 33.53, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.2, + "angle": 9.39, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.5333, + "angle": 14.85, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.6666, "angle": 33.53 } + ], + "translate": [ + { + "time": 0, + "x": -0.98, + "y": 3.98, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.2, + "x": 0.28, + "y": -0.42, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.5333, + "x": 0.31, + "y": 2.54, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { "time": 0.6666, "x": -0.98, "y": 3.98 } + ] + }, + "Hip": { + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.2, "x": 0, "y": 3.84 }, + { "time": 0.3333, "x": 0, "y": 0 }, + { "time": 0.5333, "x": 0, "y": 3.84 }, + { "time": 0.6666, "x": 0, "y": 0 } + ] + }, + "Head": { + "rotate": [ + { "time": 0, "angle": 6.58 } + ], + "translate": [ + { "time": 0, "x": 0.95, "y": 0 }, + { "time": 0.0666, "x": 0, "y": 0 }, + { "time": 0.2666, "x": 1.91, "y": 0.01 }, + { "time": 0.4, "x": 0, "y": 0 }, + { "time": 0.6, "x": 1.91, "y": 0.01 }, + { "time": 0.6666, "x": 0.95, "y": 0 } + ] + }, + "Arm2": { + "rotate": [ + { + "time": 0, + "angle": -5.89, + "curve": [ 0.244, 0.31, 0.75, 1 ] + }, + { + "time": 0.2, + "angle": 4.17, + "curve": [ 0.25, 0, 0.75, 1 ] + }, + { + "time": 0.5333, + "angle": -10.86, + "curve": [ 0.25, 0, 0.781, 0.72 ] + }, + { "time": 0.6666, "angle": -5.89 } + ] + }, + "Body": { + "rotate": [ + { "time": 0, "angle": -5.26 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 2.51 }, + { "time": 0.0666, "x": 0, "y": 0 }, + { "time": 0.2666, "x": 0, "y": 3.02 }, + { "time": 0.4, "x": 0, "y": 0 }, + { "time": 0.6, "x": 0, "y": 5.03 }, + { "time": 0.6666, "x": 0, "y": 2.51 } + ] + } + } + }, + "Parried": { + "bones": { + "Arm": { + "rotate": [ + { "time": 0, "angle": -22.67 }, + { "time": 0.1, "angle": -46.87 }, + { "time": 0.2333, "angle": -5.32 }, + { "time": 0.4, "angle": -10.02 } + ], + "translate": [ + { "time": 0, "x": -3.15, "y": -2.18 }, + { "time": 0.4, "x": 0, "y": 0 } + ] + }, + "Root": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + }, + "Hip": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.4, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 6.94, "y": -4.46 }, + { "time": 0.4, "x": 0, "y": 0 } + ] + }, + "Body": { + "rotate": [ + { "time": 0, "angle": -9.88 }, + { "time": 0.1, "angle": 7.82 }, + { "time": 0.4, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": -0.86, "y": 2.85 }, + { "time": 0.1, "x": -3.19, "y": 2.13 }, + { "time": 0.4, "x": 0, "y": 0 } + ] + }, + "Leg": { + "rotate": [ + { "time": 0, "angle": -13.95 }, + { "time": 0.4, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.4, "x": 0, "y": 0 } + ] + }, + "Leg2": { + "rotate": [ + { "time": 0, "angle": 7.29 }, + { "time": 0.1, "angle": 19.32 }, + { "time": 0.4, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 3.65, "y": 4.37 }, + { "time": 0.1, "x": -3.88, "y": 5.46 }, + { "time": 0.4, "x": 0, "y": 0 } + ] + }, + "Arm2": { + "rotate": [ + { "time": 0, "angle": 19 }, + { "time": 0.1, "angle": 88.31 }, + { "time": 0.4, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0.34, "y": -1.94 }, + { "time": 0.1, "x": -1.61, "y": 3.91 }, + { "time": 0.4, "x": 0, "y": 0 } + ] + }, + "Feet": { + "rotate": [ + { "time": 0, "angle": 13.77 }, + { "time": 0.4, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": -1.07, "y": 1.65 }, + { "time": 0.4, "x": 0, "y": 0 } + ] + }, + "Feet2": { + "rotate": [ + { "time": 0, "angle": -2.24 }, + { "time": 0.1, "angle": 3.22 }, + { "time": 0.4, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { "time": 0.1, "x": -1.56, "y": 1.1 }, + { "time": 0.4, "x": 0, "y": 0 } + ] + }, + "Head": { + "rotate": [ + { "time": 0, "angle": 3.04 }, + { "time": 0.1, "angle": 11.84 }, + { "time": 0.4, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": -1.08, "y": -0.37 }, + { "time": 0.4, "x": 0, "y": 0 } + ] + }, + "Weapon": { + "rotate": [ + { "time": 0, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 } + ] + } + } + } +} +} \ No newline at end of file diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier.json.meta b/spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier.json.meta new file mode 100644 index 000000000..208bf85c4 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier.json.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: b075ad1d14f5db64b941d266e53de57d +TextScriptImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier_SkeletonData.asset b/spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier_SkeletonData.asset new file mode 100644 index 000000000..2328d3451 Binary files /dev/null and b/spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier_SkeletonData.asset differ diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier_SkeletonData.asset.meta b/spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier_SkeletonData.asset.meta new file mode 100644 index 000000000..64306fba7 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier_SkeletonData.asset.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: e57cdb51287d3924ebb2ececf816733b +NativeFormatImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/license.txt b/spine-unity/Assets/Examples/Spine/FootSoldier/license.txt new file mode 100644 index 000000000..d7d03d722 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/license.txt @@ -0,0 +1,5 @@ +Copyright (c) 2014, XDTech + +The project file and images in this "FootSoldier" project are provided for +demonstration purposes only and may not be redistributed for any reason nor +used as the basis for derivative work. \ No newline at end of file diff --git a/spine-unity/Assets/Examples/Spine/FootSoldier/license.txt.meta b/spine-unity/Assets/Examples/Spine/FootSoldier/license.txt.meta new file mode 100644 index 000000000..aa07eb919 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/FootSoldier/license.txt.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 6fe8b327d2ab4bd438a63ffec150a911 +TextScriptImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/Goblins/goblins-mesh_Material.mat b/spine-unity/Assets/Examples/Spine/Goblins/goblins-mesh_Material.mat index 9a65bc515..e98d762ed 100644 Binary files a/spine-unity/Assets/Examples/Spine/Goblins/goblins-mesh_Material.mat and b/spine-unity/Assets/Examples/Spine/Goblins/goblins-mesh_Material.mat differ diff --git a/spine-unity/Assets/Examples/Spine/Goblins/goblins-mesh_SkeletonData.asset b/spine-unity/Assets/Examples/Spine/Goblins/goblins-mesh_SkeletonData.asset index 72b2e4c4f..e443663b9 100644 Binary files a/spine-unity/Assets/Examples/Spine/Goblins/goblins-mesh_SkeletonData.asset and b/spine-unity/Assets/Examples/Spine/Goblins/goblins-mesh_SkeletonData.asset differ diff --git a/spine-unity/Assets/Examples/Spine/Hero.prefab b/spine-unity/Assets/Examples/Spine/Hero.prefab index c0bcadfa9..99198b958 100644 Binary files a/spine-unity/Assets/Examples/Spine/Hero.prefab and b/spine-unity/Assets/Examples/Spine/Hero.prefab differ diff --git a/spine-unity/Assets/Examples/Spine/Hero/Hero_SkeletonData.asset b/spine-unity/Assets/Examples/Spine/Hero/Hero_SkeletonData.asset index 30bc75a14..a58b793c1 100644 Binary files a/spine-unity/Assets/Examples/Spine/Hero/Hero_SkeletonData.asset and b/spine-unity/Assets/Examples/Spine/Hero/Hero_SkeletonData.asset differ diff --git a/spine-unity/Assets/Examples/Spine/Hero/license.txt b/spine-unity/Assets/Examples/Spine/Hero/license.txt new file mode 100644 index 000000000..86fb7fa08 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/Hero/license.txt @@ -0,0 +1,5 @@ +Copyright (c) 2014, XDTech + +The project file and images in this "Hero" project are provided for +demonstration purposes only and may not be redistributed for any reason nor +used as the basis for derivative work. \ No newline at end of file diff --git a/spine-unity/Assets/Examples/Spine/Hero/license.txt.meta b/spine-unity/Assets/Examples/Spine/Hero/license.txt.meta new file mode 100644 index 000000000..7f70f6011 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/Hero/license.txt.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 0d9db1748e0cf9f408129df308299463 +TextScriptImporter: + userData: diff --git a/spine-unity/Assets/Examples/Spine/Raptor/raptor_Material.mat b/spine-unity/Assets/Examples/Spine/Raptor/raptor_Material.mat index 658e3cae1..bba7fb875 100644 Binary files a/spine-unity/Assets/Examples/Spine/Raptor/raptor_Material.mat and b/spine-unity/Assets/Examples/Spine/Raptor/raptor_Material.mat differ diff --git a/spine-unity/Assets/Examples/Spine/Raptor/raptor_SkeletonData.asset b/spine-unity/Assets/Examples/Spine/Raptor/raptor_SkeletonData.asset index d6633bb0e..64a8d0ed4 100644 Binary files a/spine-unity/Assets/Examples/Spine/Raptor/raptor_SkeletonData.asset and b/spine-unity/Assets/Examples/Spine/Raptor/raptor_SkeletonData.asset differ diff --git a/spine-unity/Assets/Examples/Spine/Spineboy/spineboy_Atlas.asset b/spine-unity/Assets/Examples/Spine/Spineboy/spineboy_Atlas.asset index ae8491bec..7a117dfbe 100644 Binary files a/spine-unity/Assets/Examples/Spine/Spineboy/spineboy_Atlas.asset and b/spine-unity/Assets/Examples/Spine/Spineboy/spineboy_Atlas.asset differ diff --git a/spine-unity/Assets/Examples/Spine/Spineboy/spineboy_SkeletonData.asset b/spine-unity/Assets/Examples/Spine/Spineboy/spineboy_SkeletonData.asset index 33476c81b..8c6f6de39 100644 Binary files a/spine-unity/Assets/Examples/Spine/Spineboy/spineboy_SkeletonData.asset and b/spine-unity/Assets/Examples/Spine/Spineboy/spineboy_SkeletonData.asset differ diff --git a/spine-unity/Assets/Examples/Spine/dragon.prefab b/spine-unity/Assets/Examples/Spine/dragon.prefab index 7fd541d13..d11770576 100644 Binary files a/spine-unity/Assets/Examples/Spine/dragon.prefab and b/spine-unity/Assets/Examples/Spine/dragon.prefab differ diff --git a/spine-unity/Assets/spine-unity/AtlasRegionAttacher.cs b/spine-unity/Assets/spine-unity/AtlasRegionAttacher.cs new file mode 100644 index 000000000..193ab754d --- /dev/null +++ b/spine-unity/Assets/spine-unity/AtlasRegionAttacher.cs @@ -0,0 +1,49 @@ +using UnityEngine; +using System.Collections; +using Spine; + + +public class AtlasRegionAttacher : MonoBehaviour { + + [System.Serializable] + public class SlotRegionPair { + [SpineSlot] + public string slot; + + [SpineAtlasRegion] + public string region; + } + + public AtlasAsset atlasAsset; + public SlotRegionPair[] attachments; + + [HideInInspector] + public SkeletonRenderer skeletonRenderer; + + + Atlas atlas; + + void Start() { + atlas = atlasAsset.GetAtlas(); + this.skeletonRenderer = GetComponent(); + + AtlasAttachmentLoader loader = new AtlasAttachmentLoader(atlas); + + float scaleMultiplier = skeletonRenderer.skeletonDataAsset.scale; + + var enumerator = attachments.GetEnumerator(); + while (enumerator.MoveNext()) { + var entry = (SlotRegionPair)enumerator.Current; + var regionAttachment = loader.NewRegionAttachment(null, entry.region, entry.region); + regionAttachment.Width = regionAttachment.RegionOriginalWidth * scaleMultiplier; + regionAttachment.Height = regionAttachment.RegionOriginalHeight * scaleMultiplier; + + regionAttachment.SetColor(new Color(1, 1, 1, 1)); + regionAttachment.UpdateOffset(); + + var slot = this.skeletonRenderer.skeleton.FindSlot(entry.slot); + slot.Attachment = regionAttachment; + } + } + +} diff --git a/spine-unity/Assets/spine-unity/AtlasRegionAttacher.cs.meta b/spine-unity/Assets/spine-unity/AtlasRegionAttacher.cs.meta new file mode 100644 index 000000000..bbf8780fa --- /dev/null +++ b/spine-unity/Assets/spine-unity/AtlasRegionAttacher.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: afde57cc4fd39bb4dbd61b73403ae6a8 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/spine-unity/Assets/spine-unity/CustomSkin.cs b/spine-unity/Assets/spine-unity/CustomSkin.cs new file mode 100644 index 000000000..5b98ad66c --- /dev/null +++ b/spine-unity/Assets/spine-unity/CustomSkin.cs @@ -0,0 +1,36 @@ +using UnityEngine; +using System.Collections; +using Spine; + +public class CustomSkin : MonoBehaviour { + + + [System.Serializable] + public class SkinPair { + [SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skinSource")] + public string sourceAttachment; + [SpineSlot] + public string targetSlot; + [SpineAttachment(currentSkinOnly: true, placeholdersOnly: true)] + public string targetAttachment; + } + + public SkeletonDataAsset skinSource; + public SkinPair[] skinning; + public Skin customSkin; + + SkeletonRenderer skeletonRenderer; + void Start() { + skeletonRenderer = GetComponent(); + Skeleton skeleton = skeletonRenderer.skeleton; + + customSkin = new Skin("CustomSkin"); + + foreach (var pair in skinning) { + var attachment = SpineAttachment.GetAttachment(pair.sourceAttachment, skinSource); + customSkin.AddAttachment(skeleton.FindSlotIndex(pair.targetSlot), pair.targetAttachment, attachment); + } + + skeleton.SetSkin(customSkin); + } +} diff --git a/spine-unity/Assets/spine-unity/CustomSkin.cs.meta b/spine-unity/Assets/spine-unity/CustomSkin.cs.meta new file mode 100644 index 000000000..3a7ba7a9d --- /dev/null +++ b/spine-unity/Assets/spine-unity/CustomSkin.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6e55c8477eccddc4cb5c3551a3945ca7 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs b/spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs index 136b7aae8..72b0a68d0 100644 --- a/spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs @@ -28,14 +28,21 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; using UnityEditor; using UnityEngine; +using Spine; + [CustomEditor(typeof(AtlasAsset))] public class AtlasAssetInspector : Editor { private SerializedProperty atlasFile, materials; void OnEnable () { + SpineEditorUtilities.ConfirmInitialization(); atlasFile = serializedObject.FindProperty("atlasFile"); materials = serializedObject.FindProperty("materials"); } @@ -44,11 +51,43 @@ public class AtlasAssetInspector : Editor { serializedObject.Update(); AtlasAsset asset = (AtlasAsset)target; + EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(atlasFile); EditorGUILayout.PropertyField(materials, true); + if (EditorGUI.EndChangeCheck()) + serializedObject.ApplyModifiedProperties(); + + if (materials.arraySize == 0) { + EditorGUILayout.LabelField(new GUIContent("Error: Missing materials", SpineEditorUtilities.Icons.warning)); + return; + } + + for (int i = 0; i < materials.arraySize; i++) { + SerializedProperty prop = materials.GetArrayElementAtIndex(i); + Material mat = (Material)prop.objectReferenceValue; + if (mat == null) { + EditorGUILayout.LabelField(new GUIContent("Error: Materials cannot be null", SpineEditorUtilities.Icons.warning)); + return; + } + } + + + + if (atlasFile.objectReferenceValue != null) { + Atlas atlas = asset.GetAtlas(); + FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); + List regions = (List)field.GetValue(atlas); + EditorGUILayout.LabelField("Regions"); + EditorGUI.indentLevel++; + for (int i = 0; i < regions.Count; i++) { + EditorGUILayout.LabelField(regions[i].name); + } + EditorGUI.indentLevel--; + } + if (serializedObject.ApplyModifiedProperties() || - (Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed") + (UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed") ) { asset.Reset(); } diff --git a/spine-unity/Assets/spine-unity/Editor/GUI/icon-slotRoot.png b/spine-unity/Assets/spine-unity/Editor/GUI/icon-slotRoot.png new file mode 100644 index 000000000..dfca413ac Binary files /dev/null and b/spine-unity/Assets/spine-unity/Editor/GUI/icon-slotRoot.png differ diff --git a/spine-unity/Assets/spine-unity/Editor/GUI/icon-slotRoot.png.meta b/spine-unity/Assets/spine-unity/Editor/GUI/icon-slotRoot.png.meta new file mode 100644 index 000000000..d85022986 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Editor/GUI/icon-slotRoot.png.meta @@ -0,0 +1,47 @@ +fileFormatVersion: 2 +guid: 4a1646cf39026224c85ecba92d7d6948 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: diff --git a/spine-unity/Assets/spine-unity/Editor/GUI/icon-subMeshRenderer.png.meta b/spine-unity/Assets/spine-unity/Editor/GUI/icon-subMeshRenderer.png.meta index 35500c50d..cbccad91c 100644 --- a/spine-unity/Assets/spine-unity/Editor/GUI/icon-subMeshRenderer.png.meta +++ b/spine-unity/Assets/spine-unity/Editor/GUI/icon-subMeshRenderer.png.meta @@ -5,8 +5,8 @@ TextureImporter: serializedVersion: 2 mipmaps: mipMapMode: 0 - enableMipMap: 1 - linearTexture: 0 + enableMipMap: 0 + linearTexture: 1 correctGamma: 0 fadeOut: 0 borderMipMap: 0 @@ -25,10 +25,10 @@ TextureImporter: maxTextureSize: 1024 textureSettings: filterMode: -1 - aniso: -1 + aniso: 1 mipBias: -1 - wrapMode: -1 - nPOTScale: 1 + wrapMode: 1 + nPOTScale: 0 lightmap: 0 compressionQuality: 50 spriteMode: 0 @@ -38,8 +38,8 @@ TextureImporter: spritePivot: {x: .5, y: .5} spriteBorder: {x: 0, y: 0, z: 0, w: 0} spritePixelsToUnits: 100 - alphaIsTransparency: 0 - textureType: -1 + alphaIsTransparency: 1 + textureType: 2 buildTargetSettings: [] spriteSheet: sprites: [] diff --git a/spine-unity/Assets/spine-unity/Editor/GUI/icon-weights.png b/spine-unity/Assets/spine-unity/Editor/GUI/icon-weights.png new file mode 100644 index 000000000..fa8f1906b Binary files /dev/null and b/spine-unity/Assets/spine-unity/Editor/GUI/icon-weights.png differ diff --git a/spine-unity/Assets/spine-unity/Editor/GUI/icon-weights.png.meta b/spine-unity/Assets/spine-unity/Editor/GUI/icon-weights.png.meta new file mode 100644 index 000000000..067337d4f --- /dev/null +++ b/spine-unity/Assets/spine-unity/Editor/GUI/icon-weights.png.meta @@ -0,0 +1,47 @@ +fileFormatVersion: 2 +guid: 0b1bcb09fa228d049ba3c9ea6a57e1ee +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs index df0061291..a47765d54 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs @@ -44,42 +44,48 @@ using Spine; [CustomEditor(typeof(SkeletonDataAsset))] public class SkeletonDataAssetInspector : Editor { - private SerializedProperty atlasAsset, skeletonJSON, scale, fromAnimation, toAnimation, duration, defaultMix; - private bool showAnimationStateData = true; - - #if UNITY_4_3 - private bool m_showAnimationList = true; - #else - private AnimBool m_showAnimationList = new AnimBool(true); - #endif - + static bool showAnimationStateData = true; + static bool showAnimationList = true; + static bool showSlotList = false; + static bool showAttachments = false; + + private SerializedProperty atlasAssets, skeletonJSON, scale, fromAnimation, toAnimation, duration, defaultMix; + private bool m_initialized = false; private SkeletonDataAsset m_skeletonDataAsset; + private SkeletonData m_skeletonData; private string m_skeletonDataAssetGUID; - - void OnEnable () { - try { - atlasAsset = serializedObject.FindProperty("atlasAsset"); + List warnings = new List(); + + void OnEnable() { + + SpineEditorUtilities.ConfirmInitialization(); + + try { + atlasAssets = serializedObject.FindProperty("atlasAssets"); skeletonJSON = serializedObject.FindProperty("skeletonJSON"); scale = serializedObject.FindProperty("scale"); fromAnimation = serializedObject.FindProperty("fromAnimation"); toAnimation = serializedObject.FindProperty("toAnimation"); duration = serializedObject.FindProperty("duration"); defaultMix = serializedObject.FindProperty("defaultMix"); - + m_skeletonDataAsset = (SkeletonDataAsset)target; m_skeletonDataAssetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_skeletonDataAsset)); - - EditorApplication.update += Update; + EditorApplication.update += Update; } catch { } + + m_skeletonData = m_skeletonDataAsset.GetSkeletonData(true); + + RepopulateWarnings(); } - - void OnDestroy () { + + void OnDestroy() { m_initialized = false; EditorApplication.update -= Update; this.DestroyPreviewInstances(); @@ -88,156 +94,341 @@ public class SkeletonDataAssetInspector : Editor { this.m_previewUtility = null; } } - - override public void OnInspectorGUI () { + + override public void OnInspectorGUI() { serializedObject.Update(); SkeletonDataAsset asset = (SkeletonDataAsset)target; - + EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(atlasAsset); + EditorGUILayout.PropertyField(atlasAssets, true); EditorGUILayout.PropertyField(skeletonJSON); EditorGUILayout.PropertyField(scale); if (EditorGUI.EndChangeCheck()) { - if (m_previewUtility != null) { - m_previewUtility.Cleanup(); - m_previewUtility = null; + if (serializedObject.ApplyModifiedProperties()) { + + if (m_previewUtility != null) { + m_previewUtility.Cleanup(); + m_previewUtility = null; + } + + RepopulateWarnings(); + OnEnable(); + return; } + } + - SkeletonData skeletonData = asset.GetSkeletonData(asset.atlasAsset == null || asset.skeletonJSON == null); - if (skeletonData != null) { - showAnimationStateData = EditorGUILayout.Foldout(showAnimationStateData, "Animation State Data"); - if (showAnimationStateData) { - EditorGUILayout.PropertyField(defaultMix); - - // Animation names - String[] animations = new String[skeletonData.Animations.Count]; - for (int i = 0; i < animations.Length; i++) - animations[i] = skeletonData.Animations[i].Name; - - for (int i = 0; i < fromAnimation.arraySize; i++) { - SerializedProperty from = fromAnimation.GetArrayElementAtIndex(i); - SerializedProperty to = toAnimation.GetArrayElementAtIndex(i); - SerializedProperty durationProp = duration.GetArrayElementAtIndex(i); - EditorGUILayout.BeginHorizontal(); - from.stringValue = animations[EditorGUILayout.Popup(Math.Max(Array.IndexOf(animations, from.stringValue), 0), animations)]; - to.stringValue = animations[EditorGUILayout.Popup(Math.Max(Array.IndexOf(animations, to.stringValue), 0), animations)]; - durationProp.floatValue = EditorGUILayout.FloatField(durationProp.floatValue); - if (GUILayout.Button("Delete")) { - duration.DeleteArrayElementAtIndex(i); - toAnimation.DeleteArrayElementAtIndex(i); - fromAnimation.DeleteArrayElementAtIndex(i); - } - EditorGUILayout.EndHorizontal(); - } - EditorGUILayout.BeginHorizontal(); - EditorGUILayout.Space(); - if (GUILayout.Button("Add Mix")) { - duration.arraySize++; - toAnimation.arraySize++; - fromAnimation.arraySize++; - } - EditorGUILayout.Space(); - EditorGUILayout.EndHorizontal(); - } + if (m_skeletonData != null) { + + DrawAnimationStateInfo(); + DrawAnimationList(); + DrawSlotList(); - if (GUILayout.Button(new GUIContent("Setup Pose", SpineEditorUtilities.Icons.skeleton), GUILayout.Width(105), GUILayout.Height(18))) { - StopAnimation(); - m_skeletonAnimation.skeleton.SetToSetupPose(); - m_requireRefresh = true; - } - - #if UNITY_4_3 - m_showAnimationList = EditorGUILayout.Foldout(m_showAnimationList, new GUIContent("Animations", SpineEditorUtilities.Icons.animationRoot)); - if(m_showAnimationList){ - #else - m_showAnimationList.target = EditorGUILayout.Foldout(m_showAnimationList.target, new GUIContent("Animations", SpineEditorUtilities.Icons.animationRoot)); - if (EditorGUILayout.BeginFadeGroup(m_showAnimationList.faded)) { - #endif - - - - - EditorGUILayout.LabelField("Name", "Duration"); - foreach (Spine.Animation a in skeletonData.Animations) { - GUILayout.BeginHorizontal(); - - if (m_skeletonAnimation != null && m_skeletonAnimation.state != null) { - if (m_skeletonAnimation.state.GetCurrent(0) != null && m_skeletonAnimation.state.GetCurrent(0).Animation == a) { - GUI.contentColor = Color.black; - if (GUILayout.Button("\u25BA", GUILayout.Width(24))) { - StopAnimation(); - } - GUI.contentColor = Color.white; - } else { - if (GUILayout.Button("\u25BA", GUILayout.Width(24))) { - PlayAnimation(a.Name, true); - } - } - } else { - GUILayout.Label("?", GUILayout.Width(24)); - } - EditorGUILayout.LabelField(new GUIContent(a.Name, SpineEditorUtilities.Icons.animation), new GUIContent(a.Duration.ToString("f3") + "s" + ("(" + (Mathf.RoundToInt(a.Duration * 30)) + ")").PadLeft(12, ' '))); - GUILayout.EndHorizontal(); - } - } - #if !UNITY_4_3 - EditorGUILayout.EndFadeGroup(); - #endif + } else { + + DrawReimportButton(); + //Show Warnings + foreach (var str in warnings) + EditorGUILayout.LabelField(new GUIContent(str, SpineEditorUtilities.Icons.warning)); } - + if (!Application.isPlaying) { if (serializedObject.ApplyModifiedProperties() || (UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed") - ) { + ) { asset.Reset(); } } } + + void DrawReimportButton() { + EditorGUI.BeginDisabledGroup(skeletonJSON.objectReferenceValue == null); + if (GUILayout.Button(new GUIContent("Attempt Reimport", SpineEditorUtilities.Icons.warning))) { + SpineEditorUtilities.ImportSpineContent(new string[] { AssetDatabase.GetAssetPath(skeletonJSON.objectReferenceValue) }, true); + + if (m_previewUtility != null) { + m_previewUtility.Cleanup(); + m_previewUtility = null; + } + + RepopulateWarnings(); + OnEnable(); + return; + + } + EditorGUI.EndDisabledGroup(); + } + + void DrawAnimationStateInfo() { + showAnimationStateData = EditorGUILayout.Foldout(showAnimationStateData, "Animation State Data"); + if (!showAnimationStateData) + return; + + EditorGUILayout.PropertyField(defaultMix); + + // Animation names + String[] animations = new String[m_skeletonData.Animations.Count]; + for (int i = 0; i < animations.Length; i++) + animations[i] = m_skeletonData.Animations[i].Name; + + for (int i = 0; i < fromAnimation.arraySize; i++) { + SerializedProperty from = fromAnimation.GetArrayElementAtIndex(i); + SerializedProperty to = toAnimation.GetArrayElementAtIndex(i); + SerializedProperty durationProp = duration.GetArrayElementAtIndex(i); + EditorGUILayout.BeginHorizontal(); + from.stringValue = animations[EditorGUILayout.Popup(Math.Max(Array.IndexOf(animations, from.stringValue), 0), animations)]; + to.stringValue = animations[EditorGUILayout.Popup(Math.Max(Array.IndexOf(animations, to.stringValue), 0), animations)]; + durationProp.floatValue = EditorGUILayout.FloatField(durationProp.floatValue); + if (GUILayout.Button("Delete")) { + duration.DeleteArrayElementAtIndex(i); + toAnimation.DeleteArrayElementAtIndex(i); + fromAnimation.DeleteArrayElementAtIndex(i); + } + EditorGUILayout.EndHorizontal(); + } + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.Space(); + if (GUILayout.Button("Add Mix")) { + duration.arraySize++; + toAnimation.arraySize++; + fromAnimation.arraySize++; + } + EditorGUILayout.Space(); + EditorGUILayout.EndHorizontal(); + } + void DrawAnimationList() { + showAnimationList = EditorGUILayout.Foldout(showAnimationList, new GUIContent("Animations", SpineEditorUtilities.Icons.animationRoot)); + if (!showAnimationList) + return; + + if (GUILayout.Button(new GUIContent("Setup Pose", SpineEditorUtilities.Icons.skeleton), GUILayout.Width(105), GUILayout.Height(18))) { + StopAnimation(); + m_skeletonAnimation.skeleton.SetToSetupPose(); + m_requireRefresh = true; + } + + EditorGUILayout.LabelField("Name", "Duration"); + foreach (Spine.Animation a in m_skeletonData.Animations) { + GUILayout.BeginHorizontal(); + + if (m_skeletonAnimation != null && m_skeletonAnimation.state != null) { + if (m_skeletonAnimation.state.GetCurrent(0) != null && m_skeletonAnimation.state.GetCurrent(0).Animation == a) { + GUI.contentColor = Color.black; + if (GUILayout.Button("\u25BA", GUILayout.Width(24))) { + StopAnimation(); + } + GUI.contentColor = Color.white; + } else { + if (GUILayout.Button("\u25BA", GUILayout.Width(24))) { + PlayAnimation(a.Name, true); + } + } + } else { + GUILayout.Label("?", GUILayout.Width(24)); + } + EditorGUILayout.LabelField(new GUIContent(a.Name, SpineEditorUtilities.Icons.animation), new GUIContent(a.Duration.ToString("f3") + "s" + ("(" + (Mathf.RoundToInt(a.Duration * 30)) + ")").PadLeft(12, ' '))); + GUILayout.EndHorizontal(); + } + } + + + void DrawSlotList() { + showSlotList = EditorGUILayout.Foldout(showSlotList, new GUIContent("Slots", SpineEditorUtilities.Icons.slotRoot)); + + if (!showSlotList) + return; + + if (m_skeletonAnimation == null || m_skeletonAnimation.skeleton == null) + return; + + EditorGUI.indentLevel++; + try { + showAttachments = EditorGUILayout.ToggleLeft("Show Attachments", showAttachments); + } catch { + return; + } + + + List slotAttachments = new List(); + List slotAttachmentNames = new List(); + List defaultSkinAttachmentNames = new List(); + var defaultSkin = m_skeletonData.Skins[0]; + Skin skin = m_skeletonAnimation.skeleton.Skin; + if (skin == null) { + skin = defaultSkin; + } + + for (int i = m_skeletonAnimation.skeleton.Slots.Count-1; i >= 0; i--) { + Slot slot = m_skeletonAnimation.skeleton.Slots[i]; + EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, SpineEditorUtilities.Icons.slot)); + if (showAttachments) { + + + EditorGUI.indentLevel++; + slotAttachments.Clear(); + slotAttachmentNames.Clear(); + defaultSkinAttachmentNames.Clear(); + + skin.FindNamesForSlot(i, slotAttachmentNames); + skin.FindAttachmentsForSlot(i, slotAttachments); + + + if (skin != defaultSkin) { + defaultSkin.FindNamesForSlot(i, defaultSkinAttachmentNames); + defaultSkin.FindNamesForSlot(i, slotAttachmentNames); + defaultSkin.FindAttachmentsForSlot(i, slotAttachments); + } else { + defaultSkin.FindNamesForSlot(i, defaultSkinAttachmentNames); + } + + + + for (int a = 0; a < slotAttachments.Count; a++) { + Attachment attachment = slotAttachments[a]; + string name = slotAttachmentNames[a]; + + Texture2D icon = null; + var type = attachment.GetType(); + + if (type == typeof(RegionAttachment)) + icon = SpineEditorUtilities.Icons.image; + else if (type == typeof(MeshAttachment)) + icon = SpineEditorUtilities.Icons.mesh; + else if (type == typeof(BoundingBoxAttachment)) + icon = SpineEditorUtilities.Icons.boundingBox; + else if (type == typeof(SkinnedMeshAttachment)) + icon = SpineEditorUtilities.Icons.weights; + else + icon = SpineEditorUtilities.Icons.warning; + + //TODO: Waterboard Nate + //if (name != attachment.Name) + //icon = SpineEditorUtilities.Icons.skinPlaceholder; + + bool initialState = slot.Attachment == attachment; + + bool toggled = EditorGUILayout.ToggleLeft(new GUIContent(name, icon), slot.Attachment == attachment); + + if (!defaultSkinAttachmentNames.Contains(name)) { + Rect skinPlaceHolderIconRect = GUILayoutUtility.GetLastRect(); + skinPlaceHolderIconRect.width = SpineEditorUtilities.Icons.skinPlaceholder.width; + skinPlaceHolderIconRect.height = SpineEditorUtilities.Icons.skinPlaceholder.height; + GUI.DrawTexture(skinPlaceHolderIconRect, SpineEditorUtilities.Icons.skinPlaceholder); + } + + + if (toggled != initialState) { + if (toggled) { + slot.Attachment = attachment; + } else { + slot.Attachment = null; + } + m_requireRefresh = true; + } + } + + + EditorGUI.indentLevel--; + } + } + + EditorGUI.indentLevel--; + } + + + void RepopulateWarnings() { + warnings.Clear(); + + if (skeletonJSON.objectReferenceValue == null) + warnings.Add("Missing Skeleton JSON"); + else { + + if (SpineEditorUtilities.IsSpineJSON((TextAsset)skeletonJSON.objectReferenceValue) == false) { + warnings.Add("Skeleton JSON is not a Valid JSON file"); + } else { + bool detectedNullAtlasEntry = false; + List atlasList = new List(); + for (int i = 0; i < atlasAssets.arraySize; i++) { + if (atlasAssets.GetArrayElementAtIndex(i).objectReferenceValue == null) { + detectedNullAtlasEntry = true; + break; + } else { + atlasList.Add(((AtlasAsset)atlasAssets.GetArrayElementAtIndex(i).objectReferenceValue).GetAtlas()); + } + } + + if (detectedNullAtlasEntry) + warnings.Add("AtlasAsset elements cannot be Null"); + else { + //get requirements + var missingPaths = SpineEditorUtilities.GetRequiredAtlasRegions(AssetDatabase.GetAssetPath((TextAsset)skeletonJSON.objectReferenceValue)); + + foreach (var atlas in atlasList) { + for (int i = 0; i < missingPaths.Count; i++) { + if (atlas.FindRegion(missingPaths[i]) != null) { + missingPaths.RemoveAt(i); + i--; + } + } + } + + foreach (var str in missingPaths) + warnings.Add("Missing Region: '" + str + "'"); + + + + } + } + + + } + } + //preview window stuff private PreviewRenderUtility m_previewUtility; private GameObject m_previewInstance; private Vector2 previewDir; private SkeletonAnimation m_skeletonAnimation; - private SkeletonData m_skeletonData; + //private SkeletonData m_skeletonData; private static int sliderHash = "Slider".GetHashCode(); private float m_lastTime; private bool m_playing; private bool m_requireRefresh; private Color m_originColor = new Color(0.3f, 0.3f, 0.3f, 1); - - private void StopAnimation () { + + private void StopAnimation() { m_skeletonAnimation.state.ClearTrack(0); m_playing = false; } - + List m_animEvents = new List(); List m_animEventFrames = new List(); - private void PlayAnimation (string animName, bool loop) { + private void PlayAnimation(string animName, bool loop) { m_animEvents.Clear(); m_animEventFrames.Clear(); - + m_skeletonAnimation.state.SetAnimation(0, animName, loop); - + Spine.Animation a = m_skeletonAnimation.state.GetCurrent(0).Animation; foreach (Timeline t in a.Timelines) { if (t.GetType() == typeof(EventTimeline)) { EventTimeline et = (EventTimeline)t; - + for (int i = 0; i < et.Events.Length; i++) { m_animEvents.Add(et.Events[i]); m_animEventFrames.Add(et.Frames[i]); } - + } } - + m_playing = true; } - - private void InitPreview () { + + private void InitPreview() { if (this.m_previewUtility == null) { this.m_lastTime = Time.realtimeSinceStartup; this.m_previewUtility = new PreviewRenderUtility(true); @@ -247,48 +438,59 @@ public class SkeletonDataAssetInspector : Editor { this.CreatePreviewInstances(); } } - - private void CreatePreviewInstances () { + + private void CreatePreviewInstances() { this.DestroyPreviewInstances(); if (this.m_previewInstance == null) { - string skinName = EditorPrefs.GetString(m_skeletonDataAssetGUID + "_lastSkin", ""); - - m_previewInstance = SpineEditorUtilities.SpawnAnimatedSkeleton((SkeletonDataAsset)target, skinName).gameObject; - m_previewInstance.hideFlags = HideFlags.HideAndDontSave; - m_previewInstance.layer = 0x1f; - - - m_skeletonAnimation = m_previewInstance.GetComponent(); - m_skeletonAnimation.initialSkinName = skinName; - m_skeletonAnimation.LateUpdate(); - - m_skeletonData = m_skeletonAnimation.skeletonDataAsset.GetSkeletonData(true); - - m_previewInstance.renderer.enabled = false; - - m_initialized = true; - AdjustCameraGoals(true); + try { + string skinName = EditorPrefs.GetString(m_skeletonDataAssetGUID + "_lastSkin", ""); + + m_previewInstance = SpineEditorUtilities.SpawnAnimatedSkeleton((SkeletonDataAsset)target, skinName).gameObject; + m_previewInstance.hideFlags = HideFlags.HideAndDontSave; + m_previewInstance.layer = 0x1f; + + + m_skeletonAnimation = m_previewInstance.GetComponent(); + m_skeletonAnimation.initialSkinName = skinName; + m_skeletonAnimation.LateUpdate(); + + m_skeletonData = m_skeletonAnimation.skeletonDataAsset.GetSkeletonData(true); + + m_previewInstance.renderer.enabled = false; + + m_initialized = true; + AdjustCameraGoals(true); + } catch { + + } } } - - private void DestroyPreviewInstances () { + + private void DestroyPreviewInstances() { if (this.m_previewInstance != null) { DestroyImmediate(this.m_previewInstance); m_previewInstance = null; } m_initialized = false; } - - public override bool HasPreviewGUI () { + + public override bool HasPreviewGUI() { //TODO: validate json data + + for (int i = 0; i < atlasAssets.arraySize; i++) { + var prop = atlasAssets.GetArrayElementAtIndex(i); + if (prop.objectReferenceValue == null) + return false; + } + return skeletonJSON.objectReferenceValue != null; } - + Texture m_previewTex = new Texture(); - public override void OnInteractivePreviewGUI (Rect r, GUIStyle background) { + public override void OnInteractivePreviewGUI(Rect r, GUIStyle background) { this.InitPreview(); - + if (UnityEngine.Event.current.type == EventType.Repaint) { if (m_requireRefresh) { this.m_previewUtility.BeginPreview(r, background); @@ -299,50 +501,54 @@ public class SkeletonDataAssetInspector : Editor { if (this.m_previewTex != null) GUI.DrawTexture(r, m_previewTex, ScaleMode.StretchToFill, false); } - + DrawSkinToolbar(r); NormalizedTimeBar(r); //TODO: implement panning // this.previewDir = Drag2D(this.previewDir, r); MouseScroll(r); } - + float m_orthoGoal = 1; Vector3 m_posGoal = new Vector3(0, 0, -10); double m_adjustFrameEndTime = 0; - private void AdjustCameraGoals (bool calculateMixTime) { + private void AdjustCameraGoals(bool calculateMixTime) { + if (this.m_previewInstance == null) + return; + if (calculateMixTime) { if (m_skeletonAnimation.state.GetCurrent(0) != null) { m_adjustFrameEndTime = EditorApplication.timeSinceStartup + m_skeletonAnimation.state.GetCurrent(0).Mix; } } - - + + GameObject go = this.m_previewInstance; + Bounds bounds = go.renderer.bounds; m_orthoGoal = bounds.size.y; - + m_posGoal = bounds.center + new Vector3(0, 0, -10); } - - private void AdjustCameraGoals () { + + private void AdjustCameraGoals() { AdjustCameraGoals(false); } - - private void AdjustCamera () { + + private void AdjustCamera() { if (m_previewUtility == null) return; - - + + if (EditorApplication.timeSinceStartup < m_adjustFrameEndTime) { AdjustCameraGoals(); } - + float orthoSet = Mathf.Lerp(this.m_previewUtility.m_Camera.orthographicSize, m_orthoGoal, 0.1f); - + this.m_previewUtility.m_Camera.orthographicSize = orthoSet; - + float dist = Vector3.Distance(m_previewUtility.m_Camera.transform.position, m_posGoal); if (dist > 60f * ((SkeletonDataAsset)target).scale) { Vector3 pos = Vector3.Lerp(this.m_previewUtility.m_Camera.transform.position, m_posGoal, 0.1f); @@ -352,138 +558,138 @@ public class SkeletonDataAssetInspector : Editor { m_requireRefresh = true; } } - - private void DoRenderPreview (bool drawHandles) { + + private void DoRenderPreview(bool drawHandles) { GameObject go = this.m_previewInstance; - - if (m_requireRefresh) { + + if (m_requireRefresh && go != null) { go.renderer.enabled = true; - + if (EditorApplication.isPlaying) { //do nothing } else { m_skeletonAnimation.Update((Time.realtimeSinceStartup - m_lastTime)); } - + m_lastTime = Time.realtimeSinceStartup; - + if (!EditorApplication.isPlaying) m_skeletonAnimation.LateUpdate(); - + if (drawHandles) { Handles.SetCamera(m_previewUtility.m_Camera); Handles.color = m_originColor; - + Handles.DrawLine(new Vector3(-1000 * m_skeletonDataAsset.scale, 0, 0), new Vector3(1000 * m_skeletonDataAsset.scale, 0, 0)); Handles.DrawLine(new Vector3(0, 1000 * m_skeletonDataAsset.scale, 0), new Vector3(0, -1000 * m_skeletonDataAsset.scale, 0)); } - + this.m_previewUtility.m_Camera.Render(); go.renderer.enabled = false; } - - + + } - - void Update () { + + void Update() { AdjustCamera(); - + if (m_playing) { m_requireRefresh = true; Repaint(); } else if (m_requireRefresh) { - Repaint(); - } else { - #if !UNITY_4_3 - if (m_showAnimationList.isAnimating) - Repaint(); - #endif - } + Repaint(); + } else { + //only needed if using smooth menus + } } - - void DrawSkinToolbar (Rect r) { + + void DrawSkinToolbar(Rect r) { if (m_skeletonAnimation == null) return; - + if (m_skeletonAnimation.skeleton != null) { string label = (m_skeletonAnimation.skeleton != null && m_skeletonAnimation.skeleton.Skin != null) ? m_skeletonAnimation.skeleton.Skin.Name : "default"; - + Rect popRect = new Rect(r); popRect.y += 32; popRect.x += 4; popRect.height = 24; popRect.width = 40; EditorGUI.DropShadowLabel(popRect, new GUIContent("Skin", SpineEditorUtilities.Icons.skinsRoot)); - + popRect.y += 11; popRect.width = 150; popRect.x += 44; - + if (GUI.Button(popRect, label, EditorStyles.popup)) { SelectSkinContext(); } } } - - void SelectSkinContext () { + + void SelectSkinContext() { GenericMenu menu = new GenericMenu(); - + foreach (Skin s in m_skeletonData.Skins) { menu.AddItem(new GUIContent(s.Name), this.m_skeletonAnimation.skeleton.Skin == s, SetSkin, (object)s); } - + menu.ShowAsContext(); } - - void SetSkin (object o) { + + void SetSkin(object o) { Skin skin = (Skin)o; - + m_skeletonAnimation.initialSkinName = skin.Name; m_skeletonAnimation.Reset(); m_requireRefresh = true; - + EditorPrefs.SetString(m_skeletonDataAssetGUID + "_lastSkin", skin.Name); } - - void NormalizedTimeBar (Rect r) { + + void NormalizedTimeBar(Rect r) { + if (m_skeletonAnimation == null) + return; + Rect barRect = new Rect(r); barRect.height = 32; barRect.x += 4; barRect.width -= 4; - + GUI.Box(barRect, ""); - + Rect lineRect = new Rect(barRect); float width = lineRect.width; TrackEntry t = m_skeletonAnimation.state.GetCurrent(0); - + if (t != null) { int loopCount = (int)(t.Time / t.EndTime); float currentTime = t.Time - (t.EndTime * loopCount); - + float normalizedTime = currentTime / t.Animation.Duration; - + lineRect.x = barRect.x + (width * normalizedTime) - 0.5f; lineRect.width = 2; - + GUI.color = Color.red; GUI.DrawTexture(lineRect, EditorGUIUtility.whiteTexture); GUI.color = Color.white; - + for (int i = 0; i < m_animEvents.Count; i++) { //TODO: Tooltip //Spine.Event spev = animEvents[i]; - + float fr = m_animEventFrames[i]; - + Rect evRect = new Rect(barRect); evRect.x = Mathf.Clamp(((fr / t.Animation.Duration) * width) - (SpineEditorUtilities.Icons._event.width / 2), barRect.x, float.MaxValue); evRect.width = SpineEditorUtilities.Icons._event.width; evRect.height = SpineEditorUtilities.Icons._event.height; evRect.y += SpineEditorUtilities.Icons._event.height; GUI.DrawTexture(evRect, SpineEditorUtilities.Icons._event); - - + + //TODO: Tooltip /* UnityEngine.Event ev = UnityEngine.Event.current; @@ -500,24 +706,24 @@ public class SkeletonDataAssetInspector : Editor { } } } - - void MouseScroll (Rect position) { + + void MouseScroll(Rect position) { UnityEngine.Event current = UnityEngine.Event.current; int controlID = GUIUtility.GetControlID(sliderHash, FocusType.Passive); - + switch (current.GetTypeForControl(controlID)) { - case EventType.ScrollWheel: - if (position.Contains(current.mousePosition)) { - - m_orthoGoal += current.delta.y * ((SkeletonDataAsset)target).scale * 10; - GUIUtility.hotControl = controlID; - current.Use(); - } - break; + case EventType.ScrollWheel: + if (position.Contains(current.mousePosition)) { + + m_orthoGoal += current.delta.y * ((SkeletonDataAsset)target).scale * 10; + GUIUtility.hotControl = controlID; + current.Use(); + } + break; } - + } - + //TODO: Implement preview panning /* static Vector2 Drag2D(Vector2 scrollPosition, Rect position) @@ -559,45 +765,45 @@ public class SkeletonDataAssetInspector : Editor { return scrollPosition; } */ - - public override GUIContent GetPreviewTitle () { + + public override GUIContent GetPreviewTitle() { return new GUIContent("Preview"); } - - public override void OnPreviewSettings () { + + public override void OnPreviewSettings() { if (!m_initialized) { GUILayout.HorizontalSlider(0, 0, 2, GUILayout.MaxWidth(64)); } else { float speed = GUILayout.HorizontalSlider(m_skeletonAnimation.timeScale, 0, 2, GUILayout.MaxWidth(64)); - + //snap to nearest 0.25 float y = speed / 0.25f; int q = Mathf.RoundToInt(y); speed = q * 0.25f; - + m_skeletonAnimation.timeScale = speed; } } - + //TODO: Fix first-import error //TODO: Update preview without thumbnail - public override Texture2D RenderStaticPreview (string assetPath, UnityEngine.Object[] subAssets, int width, int height) { + public override Texture2D RenderStaticPreview(string assetPath, UnityEngine.Object[] subAssets, int width, int height) { Texture2D tex = new Texture2D(width, height, TextureFormat.ARGB32, false); - + this.InitPreview(); - + if (this.m_previewUtility.m_Camera == null) return null; - + m_requireRefresh = true; this.DoRenderPreview(false); AdjustCameraGoals(false); - + this.m_previewUtility.m_Camera.orthographicSize = m_orthoGoal / 2; this.m_previewUtility.m_Camera.transform.position = m_posGoal; this.m_previewUtility.BeginStaticPreview(new Rect(0, 0, width, height)); this.DoRenderPreview(false); - + //TODO: Figure out why this is throwing errors on first attempt // if(m_previewUtility != null){ // Handles.SetCamera(this.m_previewUtility.m_Camera); diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs index 3c918ac89..5dbff2514 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs @@ -33,15 +33,17 @@ using UnityEngine; [CustomEditor(typeof(SkeletonRenderer))] public class SkeletonRendererInspector : Editor { - protected SerializedProperty skeletonDataAsset, initialSkinName, normals, tangents, meshes, immutableTriangles; + protected SerializedProperty skeletonDataAsset, initialSkinName, normals, tangents, meshes, immutableTriangles, submeshSeparators; protected virtual void OnEnable () { + SpineEditorUtilities.ConfirmInitialization(); skeletonDataAsset = serializedObject.FindProperty("skeletonDataAsset"); initialSkinName = serializedObject.FindProperty("initialSkinName"); normals = serializedObject.FindProperty("calculateNormals"); tangents = serializedObject.FindProperty("calculateTangents"); meshes = serializedObject.FindProperty("renderMeshes"); immutableTriangles = serializedObject.FindProperty("immutableTriangles"); + submeshSeparators = serializedObject.FindProperty("submeshSeparators"); } protected virtual void gui () { @@ -52,8 +54,11 @@ public class SkeletonRendererInspector : Editor { float reloadWidth = GUI.skin.label.CalcSize(new GUIContent("Reload")).x + 20; if (GUILayout.Button("Reload", GUILayout.Width(reloadWidth))) { if (component.skeletonDataAsset != null) { - if (component.skeletonDataAsset.atlasAsset != null) - component.skeletonDataAsset.atlasAsset.Reset(); + foreach (AtlasAsset aa in component.skeletonDataAsset.atlasAssets) { + if (aa != null) + aa.Reset(); + } + component.skeletonDataAsset.Reset(); } component.Reset(); @@ -92,6 +97,7 @@ public class SkeletonRendererInspector : Editor { new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility")); EditorGUILayout.PropertyField(normals); EditorGUILayout.PropertyField(tangents); + EditorGUILayout.PropertyField(submeshSeparators, true); } override public void OnInspectorGUI () { diff --git a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs new file mode 100644 index 000000000..9d9e3b3d2 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs @@ -0,0 +1,585 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +/***************************************************************************** + * Spine Attribute Drawers created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ +using UnityEngine; +using UnityEditor; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Linq; +using System.Reflection; +using Spine; + + +public struct SpineDrawerValuePair { + public string str; + public SerializedProperty property; + + public SpineDrawerValuePair(string val, SerializedProperty property) { + this.str = val; + this.property = property; + } +} + +[CustomPropertyDrawer(typeof(SpineSlot))] +public class SpineSlotDrawer : PropertyDrawer { + SkeletonDataAsset skeletonDataAsset; + + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + if (property.propertyType != SerializedPropertyType.String) { + EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); + return; + } + + SpineSlot attrib = (SpineSlot)attribute; + + var dataProperty = property.serializedObject.FindProperty(attrib.dataField); + + if (dataProperty != null) { + if (dataProperty.objectReferenceValue is SkeletonDataAsset) { + skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; + } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { + var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; + if (renderer != null) + skeletonDataAsset = renderer.skeletonDataAsset; + } else { + EditorGUI.LabelField(position, "ERROR:", "Invalid reference type"); + return; + } + + } else if (property.serializedObject.targetObject is Component) { + var component = (Component)property.serializedObject.targetObject; + if (component.GetComponent() != null) { + var skeletonRenderer = component.GetComponent(); + skeletonDataAsset = skeletonRenderer.skeletonDataAsset; + } + } + + if (skeletonDataAsset == null) { + EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset"); + return; + } + + position = EditorGUI.PrefixLabel(position, label); + + if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { + Selector(property); + } + + } + + void Selector(SerializedProperty property) { + SpineSlot attrib = (SpineSlot)attribute; + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + if (data == null) + return; + + GenericMenu menu = new GenericMenu(); + + menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); + menu.AddSeparator(""); + + for (int i = 0; i < data.Slots.Count; i++) { + string name = data.Slots[i].Name; + if (name.StartsWith(attrib.startsWith)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + menu.ShowAsContext(); + } + + void HandleSelect(object val) { + var pair = (SpineDrawerValuePair)val; + pair.property.stringValue = pair.str; + pair.property.serializedObject.ApplyModifiedProperties(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + return 18; + } +} + +[CustomPropertyDrawer(typeof(SpineSkin))] +public class SpineSkinDrawer : PropertyDrawer { + SkeletonDataAsset skeletonDataAsset; + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + if (property.propertyType != SerializedPropertyType.String) { + EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); + return; + } + + SpineSkin attrib = (SpineSkin)attribute; + + var dataProperty = property.serializedObject.FindProperty(attrib.dataField); + + if (dataProperty != null) { + if (dataProperty.objectReferenceValue is SkeletonDataAsset) { + skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; + } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { + var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; + if (renderer != null) + skeletonDataAsset = renderer.skeletonDataAsset; + } else { + EditorGUI.LabelField(position, "ERROR:", "Invalid reference type"); + return; + } + + } else if (property.serializedObject.targetObject is Component) { + var component = (Component)property.serializedObject.targetObject; + if (component.GetComponent() != null) { + var skeletonRenderer = component.GetComponent(); + skeletonDataAsset = skeletonRenderer.skeletonDataAsset; + } + } + + if (skeletonDataAsset == null) { + EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset"); + return; + } + + position = EditorGUI.PrefixLabel(position, label); + + if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { + Selector(property); + } + + } + + void Selector(SerializedProperty property) { + SpineSkin attrib = (SpineSkin)attribute; + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + if (data == null) + return; + + GenericMenu menu = new GenericMenu(); + + menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); + menu.AddSeparator(""); + + for (int i = 0; i < data.Skins.Count; i++) { + string name = data.Skins[i].Name; + if (name.StartsWith(attrib.startsWith)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + menu.ShowAsContext(); + } + + void HandleSelect(object val) { + var pair = (SpineDrawerValuePair)val; + pair.property.stringValue = pair.str; + pair.property.serializedObject.ApplyModifiedProperties(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + return 18; + } +} + +[CustomPropertyDrawer(typeof(SpineAtlasRegion))] +public class SpineAtlasRegionDrawer : PropertyDrawer { + Component component; + SerializedProperty atlasProp; + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + if (property.propertyType != SerializedPropertyType.String) { + EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); + return; + } + + component = (Component)property.serializedObject.targetObject; + + if (component != null) + atlasProp = property.serializedObject.FindProperty("atlasAsset"); + else + atlasProp = null; + + + if (atlasProp == null) { + EditorGUI.LabelField(position, "ERROR:", "Must have AtlasAsset variable!"); + return; + } else if (atlasProp.objectReferenceValue == null) { + EditorGUI.LabelField(position, "ERROR:", "Atlas variable must not be null!"); + return; + } else if (atlasProp.objectReferenceValue.GetType() != typeof(AtlasAsset)) { + EditorGUI.LabelField(position, "ERROR:", "Atlas variable must be of type AtlasAsset!"); + } + + position = EditorGUI.PrefixLabel(position, label); + + if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { + Selector(property); + } + + } + + void Selector(SerializedProperty property) { + GenericMenu menu = new GenericMenu(); + AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue; + Atlas atlas = atlasAsset.GetAtlas(); + FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); + List regions = (List)field.GetValue(atlas); + + for (int i = 0; i < regions.Count; i++) { + string name = regions[i].name; + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + + menu.ShowAsContext(); + } + + void HandleSelect(object val) { + var pair = (SpineDrawerValuePair)val; + pair.property.stringValue = pair.str; + pair.property.serializedObject.ApplyModifiedProperties(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + return 18; + } +} + +[CustomPropertyDrawer(typeof(SpineAnimation))] +public class SpineAnimationDrawer : PropertyDrawer { + SkeletonDataAsset skeletonDataAsset; + + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + + + if (property.propertyType != SerializedPropertyType.String) { + EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); + return; + } + + SpineAnimation attrib = (SpineAnimation)attribute; + + var dataProperty = property.serializedObject.FindProperty(attrib.dataField); + + if (dataProperty != null) { + if (dataProperty.objectReferenceValue is SkeletonDataAsset) { + skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; + } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { + var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; + if (renderer != null) + skeletonDataAsset = renderer.skeletonDataAsset; + } else { + EditorGUI.LabelField(position, "ERROR:", "Invalid reference type"); + return; + } + } else if (property.serializedObject.targetObject is Component) { + var component = (Component)property.serializedObject.targetObject; + if (component.GetComponent() != null) { + var skeletonRenderer = component.GetComponent(); + skeletonDataAsset = skeletonRenderer.skeletonDataAsset; + } + } + + if (skeletonDataAsset == null) { + EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset"); + return; + } + + position = EditorGUI.PrefixLabel(position, label); + + if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { + Selector(property); + } + + } + + void Selector(SerializedProperty property) { + + SpineAnimation attrib = (SpineAnimation)attribute; + + GenericMenu menu = new GenericMenu(); + + var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations; + for (int i = 0; i < animations.Count; i++) { + string name = animations[i].Name; + if (name.StartsWith(attrib.startsWith)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + menu.ShowAsContext(); + } + + void HandleSelect(object val) { + var pair = (SpineDrawerValuePair)val; + pair.property.stringValue = pair.str; + pair.property.serializedObject.ApplyModifiedProperties(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + return 18; + } +} + +[CustomPropertyDrawer(typeof(SpineAttachment))] +public class SpineAttachmentDrawer : PropertyDrawer { + + SkeletonDataAsset skeletonDataAsset; + SkeletonRenderer skeletonRenderer; + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + + if (property.propertyType != SerializedPropertyType.String) { + EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); + return; + } + + SpineAttachment attrib = (SpineAttachment)attribute; + + var dataProperty = property.serializedObject.FindProperty(attrib.dataField); + + if (dataProperty != null) { + if (dataProperty.objectReferenceValue is SkeletonDataAsset) { + skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; + } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { + var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; + if (renderer != null) + skeletonDataAsset = renderer.skeletonDataAsset; + else { + EditorGUI.LabelField(position, "ERROR:", "No SkeletonRenderer"); + } + } else { + EditorGUI.LabelField(position, "ERROR:", "Invalid reference type"); + return; + } + + } else if (property.serializedObject.targetObject is Component) { + var component = (Component)property.serializedObject.targetObject; + if (component.GetComponent() != null) { + skeletonRenderer = component.GetComponent(); + skeletonDataAsset = skeletonRenderer.skeletonDataAsset; + } + } + + if (skeletonDataAsset == null && skeletonRenderer == null) { + EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset or SkeletonRenderer"); + return; + } + + position = EditorGUI.PrefixLabel(position, label); + + if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { + Selector(property); + } + + } + + void Selector(SerializedProperty property) { + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + + if (data == null) + return; + + SpineAttachment attrib = (SpineAttachment)attribute; + + List validSkins = new List(); + + if (skeletonRenderer != null && attrib.currentSkinOnly) { + if (skeletonRenderer.skeleton.Skin != null) { + validSkins.Add(skeletonRenderer.skeleton.Skin); + } else { + validSkins.Add(data.Skins[0]); + } + } else { + foreach (Skin skin in data.Skins) { + if (skin != null) + validSkins.Add(skin); + } + } + + GenericMenu menu = new GenericMenu(); + List attachmentNames = new List(); + List placeholderNames = new List(); + + string prefix = ""; + + if (skeletonRenderer != null && attrib.currentSkinOnly) + menu.AddDisabledItem(new GUIContent(skeletonRenderer.gameObject.name + " (SkeletonRenderer)")); + else + menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); + menu.AddSeparator(""); + + menu.AddItem(new GUIContent("Null"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property)); + menu.AddSeparator(""); + + Skin defaultSkin = data.Skins[0]; + + SerializedProperty slotProperty = property.serializedObject.FindProperty(attrib.slotField); + string slotMatch = ""; + if (slotProperty != null) { + if (slotProperty.propertyType == SerializedPropertyType.String) { + slotMatch = slotProperty.stringValue.ToLower(); + } + } + + foreach (Skin skin in validSkins) { + string skinPrefix = skin.Name + "/"; + + if (validSkins.Count > 1) + prefix = skinPrefix; + + for (int i = 0; i < data.Slots.Count; i++) { + if (slotMatch.Length > 0 && data.Slots[i].Name.ToLower().Contains(slotMatch) == false) + continue; + + attachmentNames.Clear(); + placeholderNames.Clear(); + + skin.FindNamesForSlot(i, attachmentNames); + if (skin != defaultSkin) { + defaultSkin.FindNamesForSlot(i, attachmentNames); + skin.FindNamesForSlot(i, placeholderNames); + } + + + for (int a = 0; a < attachmentNames.Count; a++) { + + string attachmentPath = attachmentNames[a]; + string menuPath = prefix + data.Slots[i].Name + "/" + attachmentPath; + string name = attachmentNames[a]; + + if (attrib.returnAttachmentPath) + name = skin.Name + "/" + data.Slots[i].Name + "/" + attachmentPath; + + if (attrib.placeholdersOnly && placeholderNames.Contains(attachmentPath) == false) { + menu.AddDisabledItem(new GUIContent(menuPath)); + } else { + menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + + } + } + } + + + menu.ShowAsContext(); + } + + void HandleSelect(object val) { + var pair = (SpineDrawerValuePair)val; + pair.property.stringValue = pair.str; + pair.property.serializedObject.ApplyModifiedProperties(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + return 18; + } +} + +[CustomPropertyDrawer(typeof(SpineBone))] +public class SpineBoneDrawer : PropertyDrawer { + SkeletonDataAsset skeletonDataAsset; + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + if (property.propertyType != SerializedPropertyType.String) { + EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); + return; + } + + SpineBone attrib = (SpineBone)attribute; + + var dataProperty = property.serializedObject.FindProperty(attrib.dataField); + + if (dataProperty != null) { + if (dataProperty.objectReferenceValue is SkeletonDataAsset) { + skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; + } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { + var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; + if (renderer != null) + skeletonDataAsset = renderer.skeletonDataAsset; + } else { + EditorGUI.LabelField(position, "ERROR:", "Invalid reference type"); + return; + } + + } else if (property.serializedObject.targetObject is Component) { + var component = (Component)property.serializedObject.targetObject; + if (component.GetComponent() != null) { + var skeletonRenderer = component.GetComponent(); + skeletonDataAsset = skeletonRenderer.skeletonDataAsset; + } + } + + if (skeletonDataAsset == null) { + EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset"); + return; + } + + position = EditorGUI.PrefixLabel(position, label); + + if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { + Selector(property); + } + + } + + void Selector(SerializedProperty property) { + SpineBone attrib = (SpineBone)attribute; + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + if (data == null) + return; + + GenericMenu menu = new GenericMenu(); + + menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); + menu.AddSeparator(""); + + for (int i = 0; i < data.Bones.Count; i++) { + string name = data.Bones[i].Name; + if (name.StartsWith(attrib.startsWith)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + menu.ShowAsContext(); + } + + void HandleSelect(object val) { + var pair = (SpineDrawerValuePair)val; + pair.property.stringValue = pair.str; + pair.property.serializedObject.ApplyModifiedProperties(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + return 18; + } +} diff --git a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs.meta b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs.meta new file mode 100644 index 000000000..69a87c913 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f2de282d583d4a641bf1c349f0a3eef9 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs index 3af7d3add..5624ad02d 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs @@ -39,6 +39,8 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; +using System.Linq; +using System.Reflection; using Spine; [InitializeOnLoad] @@ -51,10 +53,12 @@ public class SpineEditorUtilities : AssetPostprocessor { public static Texture2D poseBones; public static Texture2D boneNib; public static Texture2D slot; + public static Texture2D slotRoot; public static Texture2D skinPlaceholder; public static Texture2D image; public static Texture2D boundingBox; public static Texture2D mesh; + public static Texture2D weights; public static Texture2D skin; public static Texture2D skinsRoot; public static Texture2D animation; @@ -78,7 +82,7 @@ public class SpineEditorUtilities : AssetPostprocessor { new Vector3(0.1f, 0.1f, 0) }; _boneMesh.uv = new Vector2[4]; - _boneMesh.triangles = new int[6]{0,1,2,2,3,0}; + _boneMesh.triangles = new int[6] { 0, 1, 2, 2, 3, 0 }; _boneMesh.RecalculateBounds(); _boneMesh.RecalculateNormals(); } @@ -108,17 +112,19 @@ public class SpineEditorUtilities : AssetPostprocessor { internal static Material _boneMaterial; - public static void Initialize () { + public static void Initialize() { skeleton = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skeleton.png"); nullBone = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-null.png"); bone = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-bone.png"); poseBones = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-poseBones.png"); boneNib = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boneNib.png"); slot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slot.png"); + slotRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slotRoot.png"); skinPlaceholder = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png"); image = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-image.png"); boundingBox = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boundingBox.png"); mesh = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-mesh.png"); + weights = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-weights.png"); skin = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png"); skinsRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinsRoot.png"); animation = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-animation.png"); @@ -140,12 +146,17 @@ public class SpineEditorUtilities : AssetPostprocessor { public static float defaultScale = 0.01f; public static float defaultMix = 0.2f; public static string defaultShader = "Spine/Skeleton"; + public static bool initialized; + + static SpineEditorUtilities() { + Initialize(); + } - static SpineEditorUtilities () { + static void Initialize(){ DirectoryInfo rootDir = new DirectoryInfo(Application.dataPath); FileInfo[] files = rootDir.GetFiles("SpineEditorUtilities.cs", SearchOption.AllDirectories); editorPath = Path.GetDirectoryName(files[0].FullName.Replace("\\", "/").Replace(Application.dataPath, "Assets")); - editorGUIPath = editorPath + "/GUI"; + editorGUIPath = editorPath + "/GUI"; Icons.Initialize(); @@ -156,9 +167,15 @@ public class SpineEditorUtilities : AssetPostprocessor { EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI; HierarchyWindowChanged(); + initialized = true; + } + + public static void ConfirmInitialization(){ + if(!initialized || Icons.skeleton == null) + Initialize(); } - static void HierarchyWindowChanged () { + static void HierarchyWindowChanged() { skeletonRendererTable.Clear(); skeletonUtilityBoneTable.Clear(); @@ -172,122 +189,353 @@ public class SpineEditorUtilities : AssetPostprocessor { skeletonUtilityBoneTable.Add(b.gameObject.GetInstanceID(), b); } - static void HierarchyWindowItemOnGUI (int instanceId, Rect selectionRect) { + static void HierarchyWindowItemOnGUI(int instanceId, Rect selectionRect) { if (skeletonRendererTable.ContainsKey(instanceId)) { - Rect r = new Rect(selectionRect); + Rect r = new Rect(selectionRect); r.x = r.width - 15; r.width = 15; GUI.Label(r, Icons.spine); } else if (skeletonUtilityBoneTable.ContainsKey(instanceId)) { - Rect r = new Rect(selectionRect); - r.x -= 26; + Rect r = new Rect(selectionRect); + r.x -= 26; - if (skeletonUtilityBoneTable[instanceId] != null) { - if (skeletonUtilityBoneTable[instanceId].transform.childCount == 0) - r.x += 13; - - r.y += 2; + if (skeletonUtilityBoneTable[instanceId] != null) { + if (skeletonUtilityBoneTable[instanceId].transform.childCount == 0) + r.x += 13; - r.width = 13; - r.height = 13; + r.y += 2; - if (skeletonUtilityBoneTable[instanceId].mode == SkeletonUtilityBone.Mode.Follow) { - GUI.DrawTexture(r, Icons.bone); - } else { - GUI.DrawTexture(r, Icons.poseBones); - } + r.width = 13; + r.height = 13; + + if (skeletonUtilityBoneTable[instanceId].mode == SkeletonUtilityBone.Mode.Follow) { + GUI.DrawTexture(r, Icons.bone); + } else { + GUI.DrawTexture(r, Icons.poseBones); } - } - } - - [MenuItem("Assets/Spine/Ingest")] - static void IngestSpineProjectFromSelection () { - TextAsset spineJson = null; - TextAsset atlasText = null; - - List spineJsonList = new List(); - - foreach (UnityEngine.Object o in Selection.objects) { - if (o.GetType() != typeof(TextAsset)) - continue; - - string fileName = Path.GetFileName(AssetDatabase.GetAssetPath(o)); - - if (fileName.EndsWith(".json")) - spineJson = (TextAsset)o; - else if (fileName.EndsWith(".atlas.txt")) - atlasText = (TextAsset)o; - } - - if (spineJson == null) { - EditorUtility.DisplayDialog("Error!", "Spine JSON file not found in selection!", "OK"); - return; - } - - string primaryName = Path.GetFileNameWithoutExtension(spineJson.name); - string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson)); - - if (atlasText == null) { - string atlasPath = assetPath + "/" + primaryName + ".atlas.txt"; - atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(TextAsset)); } - AtlasAsset atlasAsset = IngestSpineAtlas(atlasText); - - IngestSpineProject(spineJson, atlasAsset); } - static void OnPostprocessAllAssets (string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths) { - //debug -// return; + static void OnPostprocessAllAssets(string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths) { + ImportSpineContent(imported, false); + } + public static void ImportSpineContent(string[] imported, bool reimport = false) { - AtlasAsset sharedAtlas = null; - - System.Array.Sort(imported); + List atlasPaths = new List(); + List imagePaths = new List(); + List skeletonPaths = new List(); foreach (string str in imported) { - if (Path.GetExtension(str).ToLower() == ".json") { - TextAsset spineJson = (TextAsset)AssetDatabase.LoadAssetAtPath(str, typeof(TextAsset)); - if (IsSpineJSON(spineJson)) { - - if (sharedAtlas != null) { - string spinePath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson)); - string atlasPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(sharedAtlas)); - if (spinePath != atlasPath) - sharedAtlas = null; + string extension = Path.GetExtension(str).ToLower(); + switch (extension) { + case ".txt": + if (str.EndsWith(".atlas.txt")) { + atlasPaths.Add(str); } - - SkeletonDataAsset data = AutoIngestSpineProject(spineJson, sharedAtlas); - if (data == null) - continue; - - sharedAtlas = data.atlasAsset; - - - string dir = Path.GetDirectoryName(Path.GetDirectoryName(AssetDatabase.GetAssetPath(data))); - string prefabPath = Path.Combine(dir, data.skeletonJSON.name + ".prefab").Replace("\\", "/"); - - if (File.Exists(prefabPath) == false) { - SkeletonAnimation anim = SpawnAnimatedSkeleton(data); - PrefabUtility.CreatePrefab(prefabPath, anim.gameObject, ReplacePrefabOptions.ReplaceNameBased); - if (EditorApplication.isPlaying) - GameObject.Destroy(anim.gameObject); - else - GameObject.DestroyImmediate(anim.gameObject); - } else { - + break; + case ".png": + case ".jpg": + imagePaths.Add(str); + break; + case ".json": + TextAsset spineJson = (TextAsset)AssetDatabase.LoadAssetAtPath(str, typeof(TextAsset)); + if (IsSpineJSON(spineJson)) { + skeletonPaths.Add(str); } + break; + } + } + List atlases = new List(); + + //import atlases first + foreach (string ap in atlasPaths) { + if (!reimport && CheckForValidAtlas(ap)) + continue; + + TextAsset atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(ap, typeof(TextAsset)); + AtlasAsset atlas = IngestSpineAtlas(atlasText); + atlases.Add(atlas); + } + + //import skeletons and match them with atlases + bool abortSkeletonImport = false; + foreach (string sp in skeletonPaths) { + if (!reimport && CheckForValidSkeletonData(sp)) { + Debug.Log("Automatically skipping: " + sp); + continue; + } + + + string dir = Path.GetDirectoryName(sp); + + var localAtlases = FindAtlasesAtPath(dir); + var requiredPaths = GetRequiredAtlasRegions(sp); + var atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases); + + if (atlasMatch != null) { + IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch); + } else { + bool resolved = false; + while (!resolved) { + int result = EditorUtility.DisplayDialogComplex("Skeleton JSON Import Error!", "Could not find matching AtlasAsset for " + Path.GetFileNameWithoutExtension(sp), "Select", "Skip", "Abort"); + switch (result) { + case -1: + Debug.Log("Select Atlas"); + AtlasAsset selectedAtlas = GetAtlasDialog(Path.GetDirectoryName(sp)); + if (selectedAtlas != null) { + localAtlases.Clear(); + localAtlases.Add(selectedAtlas); + atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases); + if (atlasMatch != null) { + resolved = true; + IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch); + } + } + + break; + case 0: + var atlasList = MultiAtlasDialog(requiredPaths, Path.GetDirectoryName(sp), Path.GetFileNameWithoutExtension(sp)); + + if (atlasList != null) + IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasList.ToArray()); + + resolved = true; + break; + + case 1: + Debug.Log("Skipped importing: " + Path.GetFileName(sp)); + resolved = true; + break; + + + case 2: + //abort + abortSkeletonImport = true; + resolved = true; + break; + } + } + } + + if (abortSkeletonImport) + break; + } + + //TODO: any post processing of images + } + + static bool CheckForValidSkeletonData(string skeletonJSONPath) { + + string dir = Path.GetDirectoryName(skeletonJSONPath); + TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonJSONPath, typeof(TextAsset)); + DirectoryInfo dirInfo = new DirectoryInfo(dir); + + FileInfo[] files = dirInfo.GetFiles("*.asset"); + + foreach (var f in files) { + string localPath = dir + "/" + f.Name; + var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object)); + if (obj is SkeletonDataAsset) { + var skeletonDataAsset = (SkeletonDataAsset)obj; + if (skeletonDataAsset.skeletonJSON == textAsset) + return true; + } + } + + return false; + } + + static bool CheckForValidAtlas(string atlasPath) { + + string dir = Path.GetDirectoryName(atlasPath); + TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(TextAsset)); + DirectoryInfo dirInfo = new DirectoryInfo(dir); + + FileInfo[] files = dirInfo.GetFiles("*.asset"); + + foreach (var f in files) { + string localPath = dir + "/" + f.Name; + var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object)); + if (obj is AtlasAsset) { + var atlasAsset = (AtlasAsset)obj; + if (atlasAsset.atlasFile == textAsset) + return true; + } + } + + return false; + } + + static List MultiAtlasDialog(List requiredPaths, string initialDirectory, string header = "") { + + List atlasAssets = new List(); + + bool resolved = false; + string lastAtlasPath = initialDirectory; + while (!resolved) { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(header); + sb.AppendLine("Atlases:"); + if (atlasAssets.Count == 0) { + sb.AppendLine("\t--none--"); + } + for (int i = 0; i < atlasAssets.Count; i++) { + sb.AppendLine("\t" + atlasAssets[i].name); + } + + sb.AppendLine(); + sb.AppendLine("Missing Regions:"); + + List missingRegions = new List(requiredPaths); + + foreach (var atlasAsset in atlasAssets) { + var atlas = atlasAsset.GetAtlas(); + for (int i = 0; i < missingRegions.Count; i++) { + if (atlas.FindRegion(missingRegions[i]) != null) { + missingRegions.RemoveAt(i); + i--; + } + } + } + + if (missingRegions.Count == 0) { + break; + } + + for (int i = 0; i < missingRegions.Count; i++) { + sb.AppendLine("\t" + missingRegions[i]); + } + + int result = EditorUtility.DisplayDialogComplex("Atlas Selection", sb.ToString(), "Select", "Finish", "Abort"); + + switch(result){ + case 0: + AtlasAsset selectedAtlasAsset = GetAtlasDialog(lastAtlasPath); + if (selectedAtlasAsset != null) { + var atlas = selectedAtlasAsset.GetAtlas(); + bool hasValidRegion = false; + foreach (string str in missingRegions) { + if (atlas.FindRegion(str) != null) { + hasValidRegion = true; + break; + } + } + + atlasAssets.Add(selectedAtlasAsset); + } + break; + + case 1: + resolved = true; + break; + + case 2: + atlasAssets = null; + resolved = true; + break; + } + + + } + + + return atlasAssets; + } + + static AtlasAsset GetAtlasDialog(string dirPath) { + string path = EditorUtility.OpenFilePanel("Select AtlasAsset...", dirPath, "asset"); + if (path == "") + return null; + + int subLen = Application.dataPath.Length - 6; + string assetRelativePath = path.Substring(subLen, path.Length - subLen).Replace("\\", "/"); + + Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset)); + + if (obj == null || obj.GetType() != typeof(AtlasAsset)) + return null; + + return (AtlasAsset)obj; + } + + public static List GetRequiredAtlasRegions(string jsonPath) { + List requiredPaths = new List(); + + TextAsset spineJson = (TextAsset)AssetDatabase.LoadAssetAtPath(jsonPath, typeof(TextAsset)); + + StringReader reader = new StringReader(spineJson.text); + var root = Json.Deserialize(reader) as Dictionary; + + foreach (KeyValuePair entry in (Dictionary)root["skins"]) { + foreach (KeyValuePair slotEntry in (Dictionary)entry.Value) { + + foreach (KeyValuePair attachmentEntry in ((Dictionary)slotEntry.Value)) { + var data = ((Dictionary)attachmentEntry.Value); + if (data.ContainsKey("path")) + requiredPaths.Add((string)data["path"]); + else if (data.ContainsKey("name")) + requiredPaths.Add((string)data["name"]); + else + requiredPaths.Add(attachmentEntry.Key); + //requiredPaths.Add((string)sdf["path"]); } } } + + return requiredPaths; + } + static AtlasAsset GetMatchingAtlas(List requiredPaths, List atlasAssets) { + AtlasAsset atlasAssetMatch = null; + + foreach (AtlasAsset a in atlasAssets) { + Atlas atlas = a.GetAtlas(); + bool failed = false; + foreach (string regionPath in requiredPaths) { + if (atlas.FindRegion(regionPath) == null) { + failed = true; + break; + } + } + + if (!failed) { + atlasAssetMatch = a; + break; + } + + } + + return atlasAssetMatch; } - static bool IsSpineJSON (TextAsset asset) { + static List FindAtlasesAtPath(string path) { + List arr = new List(); + + DirectoryInfo dir = new DirectoryInfo(path); + FileInfo[] assetInfoArr = dir.GetFiles("*.asset"); + + int subLen = Application.dataPath.Length - 6; + + foreach (var f in assetInfoArr) { + string assetRelativePath = f.FullName.Substring(subLen, f.FullName.Length - subLen).Replace("\\", "/"); + + Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset)); + if (obj != null) { + arr.Add(obj as AtlasAsset); + } + + } + + + return arr; + } + + public static bool IsSpineJSON(TextAsset asset) { object obj = Json.Deserialize(new StringReader(asset.text)); if (obj == null) { Debug.LogError("Is not valid JSON"); @@ -307,51 +555,7 @@ public class SpineEditorUtilities : AssetPostprocessor { return true; } - static SkeletonDataAsset AutoIngestSpineProject (TextAsset spineJson, Object atlasSource = null) { - TextAsset atlasText = null; - AtlasAsset atlasAsset = null; - - if (atlasSource != null) { - if (atlasSource.GetType() == typeof(TextAsset)) { - atlasText = (TextAsset)atlasSource; - } else if (atlasSource.GetType() == typeof(AtlasAsset)) { - atlasAsset = (AtlasAsset)atlasSource; - } - } - - if (atlasText == null && atlasAsset == null) { - string primaryName = Path.GetFileNameWithoutExtension(spineJson.name); - string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson)); - - if (atlasText == null) { - string atlasPath = assetPath + "/" + primaryName + ".atlas.txt"; - atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(TextAsset)); - - if (atlasText == null) { - //can't find atlas, likely because using a shared atlas - bool abort = !EditorUtility.DisplayDialog("Atlas not Found", "Expecting " + spineJson.name + ".atlas\n" + "Press OK to select Atlas", "OK", "Abort"); - if (abort) { - //do nothing, let it error later - } else { - string path = EditorUtility.OpenFilePanel("Find Atlas source...", Path.GetDirectoryName(Application.dataPath) + "/" + assetPath, "txt"); - if (path != "") { - path = path.Replace("\\", "/"); - path = path.Replace(Application.dataPath.Replace("\\", "/"), "Assets"); - atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset)); - } - } - - } - } - } - - if (atlasAsset == null) - atlasAsset = IngestSpineAtlas(atlasText); - - return IngestSpineProject(spineJson, atlasAsset); - } - - static AtlasAsset IngestSpineAtlas (TextAsset atlasText) { + static AtlasAsset IngestSpineAtlas(TextAsset atlasText) { if (atlasText == null) { Debug.LogWarning("Atlas source cannot be null!"); return null; @@ -376,17 +580,17 @@ public class SpineEditorUtilities : AssetPostprocessor { string[] atlasLines = atlasStr.Split('\n'); List pageFiles = new List(); - for (int i = 0; i < atlasLines.Length-1; i++) { + for (int i = 0; i < atlasLines.Length - 1; i++) { if (atlasLines[i].Length == 0) pageFiles.Add(atlasLines[i + 1]); } - + atlasAsset.materials = new Material[pageFiles.Count]; - + for (int i = 0; i < pageFiles.Count; i++) { string texturePath = assetPath + "/" + pageFiles[i]; Texture2D texture = (Texture2D)AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D)); - + TextureImporter texImporter = (TextureImporter)TextureImporter.GetAtPath(texturePath); texImporter.textureFormat = TextureImporterFormat.AutomaticTruecolor; texImporter.mipmapEnabled = false; @@ -396,13 +600,13 @@ public class SpineEditorUtilities : AssetPostprocessor { EditorUtility.SetDirty(texImporter); AssetDatabase.ImportAsset(texturePath); AssetDatabase.SaveAssets(); - + string pageName = Path.GetFileNameWithoutExtension(pageFiles[i]); - + //because this looks silly if (pageName == primaryName && pageFiles.Count == 1) pageName = "Material"; - + string materialPath = assetPath + "/" + primaryName + "_" + pageName + ".mat"; Material mat = (Material)AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material)); @@ -410,12 +614,12 @@ public class SpineEditorUtilities : AssetPostprocessor { mat = new Material(Shader.Find(defaultShader)); AssetDatabase.CreateAsset(mat, materialPath); } - + mat.mainTexture = texture; EditorUtility.SetDirty(mat); AssetDatabase.SaveAssets(); - + atlasAsset.materials[i] = mat; } @@ -429,40 +633,41 @@ public class SpineEditorUtilities : AssetPostprocessor { return (AtlasAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(AtlasAsset)); } - static SkeletonDataAsset IngestSpineProject (TextAsset spineJson, AtlasAsset atlasAsset = null) { + static SkeletonDataAsset IngestSpineProject(TextAsset spineJson, params AtlasAsset[] atlasAssets) { string primaryName = Path.GetFileNameWithoutExtension(spineJson.name); string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson)); string filePath = assetPath + "/" + primaryName + "_SkeletonData.asset"; - if (spineJson != null && atlasAsset != null) { + if (spineJson != null && atlasAssets != null) { SkeletonDataAsset skelDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset)); if (skelDataAsset == null) { skelDataAsset = SkeletonDataAsset.CreateInstance(); - skelDataAsset.atlasAsset = atlasAsset; + skelDataAsset.atlasAssets = atlasAssets; skelDataAsset.skeletonJSON = spineJson; skelDataAsset.fromAnimation = new string[0]; skelDataAsset.toAnimation = new string[0]; skelDataAsset.duration = new float[0]; skelDataAsset.defaultMix = defaultMix; skelDataAsset.scale = defaultScale; - + AssetDatabase.CreateAsset(skelDataAsset, filePath); AssetDatabase.SaveAssets(); } else { + skelDataAsset.atlasAssets = atlasAssets; skelDataAsset.Reset(); skelDataAsset.GetSkeletonData(true); } return skelDataAsset; } else { - EditorUtility.DisplayDialog("Error!", "Must specify both Spine JSON and Atlas TextAsset", "OK"); + EditorUtility.DisplayDialog("Error!", "Must specify both Spine JSON and AtlasAsset array", "OK"); return null; } } [MenuItem("Assets/Spine/Spawn")] - static void SpawnAnimatedSkeleton () { + static void SpawnAnimatedSkeleton() { Object[] arr = Selection.objects; foreach (Object o in arr) { string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); @@ -474,59 +679,66 @@ public class SpineEditorUtilities : AssetPostprocessor { } [MenuItem("Assets/Spine/Spawn", true)] - static bool ValidateSpawnAnimatedSkeleton () { + static bool ValidateSpawnAnimatedSkeleton() { Object[] arr = Selection.objects; - + if (arr.Length == 0) return false; - + foreach (Object o in arr) { if (o.GetType() != typeof(SkeletonDataAsset)) return false; } - + return true; } - public static SkeletonAnimation SpawnAnimatedSkeleton (SkeletonDataAsset skeletonDataAsset, string skinName) { + public static SkeletonAnimation SpawnAnimatedSkeleton(SkeletonDataAsset skeletonDataAsset, string skinName) { return SpawnAnimatedSkeleton(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName)); } - public static SkeletonAnimation SpawnAnimatedSkeleton (SkeletonDataAsset skeletonDataAsset, Skin skin = null) { + public static SkeletonAnimation SpawnAnimatedSkeleton(SkeletonDataAsset skeletonDataAsset, Skin skin = null) { GameObject go = new GameObject(skeletonDataAsset.name.Replace("_SkeletonData", ""), typeof(MeshFilter), typeof(MeshRenderer), typeof(SkeletonAnimation)); SkeletonAnimation anim = go.GetComponent(); anim.skeletonDataAsset = skeletonDataAsset; bool requiresNormals = false; - foreach (Material m in anim.skeletonDataAsset.atlasAsset.materials) { - if (m.shader.name.Contains("Lit")) { - requiresNormals = true; - break; + foreach (AtlasAsset atlasAsset in anim.skeletonDataAsset.atlasAssets) { + foreach (Material m in atlasAsset.materials) { + if (m.shader.name.Contains("Lit")) { + requiresNormals = true; + break; + } } } + + anim.calculateNormals = requiresNormals; SkeletonData data = skeletonDataAsset.GetSkeletonData(true); if (data == null) { - string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAsset); - skeletonDataAsset.atlasAsset = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset)); + for(int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++){ + string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]); + skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset)); + } + data = skeletonDataAsset.GetSkeletonData(true); } if (skin == null) skin = data.DefaultSkin; - + if (skin == null) skin = data.Skins[0]; anim.Reset(); - + anim.skeleton.SetSkin(skin); anim.initialSkinName = skin.Name; - + anim.skeleton.Update(1); anim.state.Update(1); anim.state.Apply(anim.skeleton); @@ -534,4 +746,4 @@ public class SpineEditorUtilities : AssetPostprocessor { return anim; } -} +} \ No newline at end of file diff --git a/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs b/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs index 3e6242dcb..06afb8275 100644 --- a/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs +++ b/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs @@ -35,7 +35,7 @@ using UnityEngine; using Spine; public class SkeletonDataAsset : ScriptableObject { - public AtlasAsset atlasAsset; + public AtlasAsset[] atlasAssets; public TextAsset skeletonJSON; public float scale = 1; public String[] fromAnimation; @@ -45,13 +45,14 @@ public class SkeletonDataAsset : ScriptableObject { private SkeletonData skeletonData; private AnimationStateData stateData; - public void Reset () { + public void Reset() { skeletonData = null; stateData = null; } - public SkeletonData GetSkeletonData (bool quiet) { - if (atlasAsset == null) { + public SkeletonData GetSkeletonData(bool quiet) { + if (atlasAssets == null) { + atlasAssets = new AtlasAsset[0]; if (!quiet) Debug.LogError("Atlas not set for SkeletonData asset: " + name, this); Reset(); @@ -65,16 +66,33 @@ public class SkeletonDataAsset : ScriptableObject { return null; } - Atlas atlas = atlasAsset.GetAtlas(); - if (atlas == null) { + + + if (atlasAssets.Length == 0) { Reset(); return null; } + Atlas[] atlasArr = new Atlas[atlasAssets.Length]; + for (int i = 0; i < atlasAssets.Length; i++) { + if (atlasAssets[i] == null) { + Reset(); + return null; + } + atlasArr[i] = atlasAssets[i].GetAtlas(); + if (atlasArr[i] == null) { + Reset(); + return null; + } + } + + + + if (skeletonData != null) return skeletonData; - SkeletonJson json = new SkeletonJson(atlas); + SkeletonJson json = new SkeletonJson(atlasArr); json.Scale = scale; try { skeletonData = json.ReadSkeletonData(new StringReader(skeletonJSON.text)); @@ -95,7 +113,7 @@ public class SkeletonDataAsset : ScriptableObject { return skeletonData; } - public AnimationStateData GetAnimationStateData () { + public AnimationStateData GetAnimationStateData() { if (stateData != null) return stateData; GetSkeletonData(false); diff --git a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs index a483e47e6..e1dad6e58 100644 --- a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs +++ b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs @@ -38,71 +38,71 @@ using System.Collections; using Spine; public static class SkeletonExtensions { - - public static void SetColor (this Slot slot, Color color) { + + public static void SetColor(this Slot slot, Color color) { slot.A = color.a; slot.R = color.r; slot.G = color.g; slot.B = color.b; } - public static void SetColor (this Slot slot, Color32 color) { + public static void SetColor(this Slot slot, Color32 color) { slot.A = color.a / 255f; slot.R = color.r / 255f; slot.G = color.g / 255f; slot.B = color.b / 255f; } - public static void SetColor (this RegionAttachment attachment, Color color) { + public static void SetColor(this RegionAttachment attachment, Color color) { attachment.A = color.a; attachment.R = color.r; attachment.G = color.g; attachment.B = color.b; } - public static void SetColor (this RegionAttachment attachment, Color32 color) { + public static void SetColor(this RegionAttachment attachment, Color32 color) { attachment.A = color.a / 255f; attachment.R = color.r / 255f; attachment.G = color.g / 255f; attachment.B = color.b / 255f; } - public static void SetColor (this MeshAttachment attachment, Color color) { + public static void SetColor(this MeshAttachment attachment, Color color) { attachment.A = color.a; attachment.R = color.r; attachment.G = color.g; attachment.B = color.b; } - public static void SetColor (this MeshAttachment attachment, Color32 color) { + public static void SetColor(this MeshAttachment attachment, Color32 color) { attachment.A = color.a / 255f; attachment.R = color.r / 255f; attachment.G = color.g / 255f; attachment.B = color.b / 255f; } - public static void SetColor (this SkinnedMeshAttachment attachment, Color color) { + public static void SetColor(this SkinnedMeshAttachment attachment, Color color) { attachment.A = color.a; attachment.R = color.r; attachment.G = color.g; attachment.B = color.b; } - public static void SetColor (this SkinnedMeshAttachment attachment, Color32 color) { + public static void SetColor(this SkinnedMeshAttachment attachment, Color32 color) { attachment.A = color.a / 255f; attachment.R = color.r / 255f; attachment.G = color.g / 255f; attachment.B = color.b / 255f; } - public static void SetPosition (this Bone bone, Vector2 position) { + public static void SetPosition(this Bone bone, Vector2 position) { bone.X = position.x; bone.Y = position.y; } - public static void SetPosition (this Bone bone, Vector3 position) { + public static void SetPosition(this Bone bone, Vector3 position) { bone.X = position.x; bone.Y = position.y; } -} +} \ No newline at end of file diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index 30c39da73..07cabff32 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -51,6 +51,14 @@ public class SkeletonRenderer : MonoBehaviour { public float zSpacing; public bool renderMeshes = true, immutableTriangles; public bool logErrors = false; + + [SpineSlot] + public string[] submeshSeparators = new string[0]; + + [HideInInspector] + public List submeshSeparatorSlots = new List(); + + private MeshFilter meshFilter; private Mesh mesh1, mesh2; private bool useMesh1; @@ -62,6 +70,7 @@ public class SkeletonRenderer : MonoBehaviour { private Material[] sharedMaterials = new Material[0]; private readonly List submeshMaterials = new List(); private readonly List submeshes = new List(); + public virtual void Reset () { if (meshFilter != null) @@ -99,6 +108,12 @@ public class SkeletonRenderer : MonoBehaviour { skeleton = new Skeleton(skeletonData); if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default") skeleton.SetSkin(initialSkinName); + + submeshSeparatorSlots.Clear(); + for (int i = 0; i < submeshSeparators.Length; i++) { + submeshSeparatorSlots.Add(skeleton.FindSlot(submeshSeparators[i])); + } + if (OnReset != null) OnReset(this); } @@ -156,7 +171,8 @@ public class SkeletonRenderer : MonoBehaviour { // Populate submesh when material changes. Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject; - if ((lastMaterial != material && lastMaterial != null) || slot.Data.name[0] == '*') { + + if ((lastMaterial != material && lastMaterial != null) || submeshSeparatorSlots.Contains(slot)) { AddSubmesh(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false); submeshTriangleCount = 0; submeshFirstVertex = vertexCount; diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityKinematicShadow.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityKinematicShadow.cs index c088353f2..86f96e942 100644 --- a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityKinematicShadow.cs +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityKinematicShadow.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; public class SkeletonUtilityKinematicShadow : MonoBehaviour { public bool hideShadow = true; + public Transform parent; Dictionary shadowTable; GameObject shadowRoot; @@ -12,7 +13,10 @@ public class SkeletonUtilityKinematicShadow : MonoBehaviour { if (hideShadow) shadowRoot.hideFlags = HideFlags.HideInHierarchy; - shadowRoot.transform.parent = transform.root; + if(parent == null) + shadowRoot.transform.parent = transform.root; + else + shadowRoot.transform.parent = parent; shadowTable = new Dictionary(); diff --git a/spine-unity/Assets/spine-unity/SpineAttributes.cs b/spine-unity/Assets/spine-unity/SpineAttributes.cs new file mode 100644 index 000000000..1bf15bca2 --- /dev/null +++ b/spine-unity/Assets/spine-unity/SpineAttributes.cs @@ -0,0 +1,198 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +/***************************************************************************** + * Spine Attributes created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ +using UnityEngine; +using System.Collections; + +public class SpineSlot : PropertyAttribute { + public string startsWith = ""; + public string dataField = ""; + + /// + /// Smart popup menu for Spine Slots + /// + /// Filters popup results to elements that begin with supplied string. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineSlot(string startsWith = "", string dataField = "") { + this.startsWith = startsWith; + this.dataField = dataField; + } +} + +public class SpineSkin : PropertyAttribute { + public string startsWith = ""; + public string dataField = ""; + + /// + /// Smart popup menu for Spine Skins + /// + /// Filters popup results to elements that begin with supplied string. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineSkin(string startsWith = "", string dataField = "") { + this.startsWith = startsWith; + this.dataField = dataField; + } +} +public class SpineAnimation : PropertyAttribute { + public string startsWith = ""; + public string dataField = ""; + + /// + /// Smart popup menu for Spine Animations + /// + /// Filters popup results to elements that begin with supplied string. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineAnimation(string startsWith = "", string dataField = "") { + this.startsWith = startsWith; + this.dataField = dataField; + } +} + +public class SpineAttachment : PropertyAttribute { + public bool returnAttachmentPath = false; + public bool currentSkinOnly = false; + public bool placeholdersOnly = false; + public string dataField = ""; + public string slotField = ""; + + + public SpineAttachment() { + + } + + /// + /// Smart popup menu for Spine Attachments + /// + /// Filters popup results to only include the current Skin. Only valid when a SkeletonRenderer is the data source. + /// Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName" + /// Filters popup results to exclude attachments that are not children of Skin Placeholders + /// If specified, a locally scoped field with the name supplied by in slotField will be used to limit the popup results to children of a named slot + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineAttachment(bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "") { + this.currentSkinOnly = currentSkinOnly; + this.returnAttachmentPath = returnAttachmentPath; + this.placeholdersOnly = placeholdersOnly; + this.slotField = slotField; + this.dataField = dataField; + } + + public static Hierarchy GetHierarchy(string fullPath) { + return new Hierarchy(fullPath); + } + + public static Spine.Attachment GetAttachment(string attachmentPath, Spine.SkeletonData skeletonData) { + var hierarchy = SpineAttachment.GetHierarchy(attachmentPath); + if (hierarchy.name == "") + return null; + + return skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name); + } + + public static Spine.Attachment GetAttachment(string attachmentPath, SkeletonDataAsset skeletonDataAsset) { + return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true)); + } + + public struct Hierarchy { + public string skin; + public string slot; + public string name; + + public Hierarchy(string fullPath) { + string[] chunks = fullPath.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries); + if (chunks.Length == 0) { + skin = ""; + slot = ""; + name = ""; + return; + } + else if (chunks.Length < 2) { + throw new System.Exception("Cannot generate Attachment Hierarchy from string! Not enough components! [" + fullPath + "]"); + } + skin = chunks[0]; + slot = chunks[1]; + name = ""; + for (int i = 2; i < chunks.Length; i++) { + name += chunks[i]; + } + } + } +} + +public class SpineBone : PropertyAttribute { + public string startsWith = ""; + public string dataField = ""; + + /// + /// Smart popup menu for Spine Bones + /// + /// Filters popup results to elements that begin with supplied string. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineBone(string startsWith = "", string dataField = "") { + this.startsWith = startsWith; + this.dataField = dataField; + } + + public static Spine.Bone GetBone(string boneName, SkeletonRenderer renderer) { + if (renderer.skeleton == null) + return null; + + return renderer.skeleton.FindBone(boneName); + } + + public static Spine.BoneData GetBoneData(string boneName, SkeletonDataAsset skeletonDataAsset) { + var data = skeletonDataAsset.GetSkeletonData(true); + + return data.FindBone(boneName); + } +} + +public class SpineAtlasRegion : PropertyAttribute { + //TODO: Standardize with Skeleton attributes + //NOTE: For now, relies on locally scoped field named "atlasAsset" for source. +} \ No newline at end of file diff --git a/spine-unity/Assets/spine-unity/SpineAttributes.cs.meta b/spine-unity/Assets/spine-unity/SpineAttributes.cs.meta new file mode 100644 index 000000000..fe4c14b95 --- /dev/null +++ b/spine-unity/Assets/spine-unity/SpineAttributes.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ce216f51ebc1d3f40929f4e58d1c65e5 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: