mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
Merge pull request #348 from Fenrisul/master
Spine Unity Runtime Update
This commit is contained in:
commit
47452a0c54
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
Binary file not shown.
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63212ccaf5776bd489cba58fb67a2233
|
||||
DefaultImporter:
|
||||
userData:
|
||||
BIN
spine-unity/Assets/Examples/Scenes/Mix and Match.unity
Normal file
BIN
spine-unity/Assets/Examples/Scenes/Mix and Match.unity
Normal file
Binary file not shown.
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5673b83016f67a4c99772dfb7b3c437
|
||||
DefaultImporter:
|
||||
userData:
|
||||
Binary file not shown.
@ -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
|
||||
|
||||
51
spine-unity/Assets/Examples/Scripts/Chimera.cs
Normal file
51
spine-unity/Assets/Examples/Scripts/Chimera.cs
Normal file
@ -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<SkeletonRenderer>().skeleton.FindSlot(targetSlot).Attachment = SpineAttachment.GetAttachment(attachmentPath, skeletonDataSource);
|
||||
}
|
||||
}
|
||||
8
spine-unity/Assets/Examples/Scripts/Chimera.cs.meta
Normal file
8
spine-unity/Assets/Examples/Scripts/Chimera.cs.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5053fe97a7657b5418b0c307b7338b0c
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
66
spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs
Normal file
66
spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs
Normal file
@ -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<SkeletonAnimation>();
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7dae3f4db9a24bf4abe2059526bfd689
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
85
spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs
Normal file
85
spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs
Normal file
@ -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<SkeletonAnimation>();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c826b50b0cfee343be3bdbbf59d0f7c
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@ -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))]
|
||||
|
||||
Binary file not shown.
Binary file not shown.
5
spine-unity/Assets/Examples/Spine/FootSoldier.meta
Normal file
5
spine-unity/Assets/Examples/Spine/FootSoldier.meta
Normal file
@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a709e690449c1b40b198bb86f707c41
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e90f1603e5c99c745a28d42e61afe5b2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@ -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
|
||||
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f0639ff8bc42314d8d62ee0f7ba541f
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
@ -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:
|
||||
Binary file not shown.
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c574489dd067c2b4cb4dc165a4c410cc
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
Binary file not shown.
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6e388faa521c96449984cfa4b60d74e
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
@ -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
|
||||
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ca7c05342912804eb0a2fd5bbe85b58
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
BIN
spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.png
Normal file
BIN
spine-unity/Assets/Examples/Spine/FootSoldier/FS_White.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
@ -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:
|
||||
Binary file not shown.
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3c7e74834cd8424f8735ba05e94a688
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
Binary file not shown.
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a3598dafa118754db95756064347da7
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
1754
spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier.json
Normal file
1754
spine-unity/Assets/Examples/Spine/FootSoldier/FootSoldier.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b075ad1d14f5db64b941d266e53de57d
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
Binary file not shown.
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e57cdb51287d3924ebb2ececf816733b
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
@ -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.
|
||||
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fe8b327d2ab4bd438a63ffec150a911
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5
spine-unity/Assets/Examples/Spine/Hero/license.txt
Normal file
5
spine-unity/Assets/Examples/Spine/Hero/license.txt
Normal file
@ -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.
|
||||
4
spine-unity/Assets/Examples/Spine/Hero/license.txt.meta
Normal file
4
spine-unity/Assets/Examples/Spine/Hero/license.txt.meta
Normal file
@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d9db1748e0cf9f408129df308299463
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
49
spine-unity/Assets/spine-unity/AtlasRegionAttacher.cs
Normal file
49
spine-unity/Assets/spine-unity/AtlasRegionAttacher.cs
Normal file
@ -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<SkeletonRenderer>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: afde57cc4fd39bb4dbd61b73403ae6a8
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
36
spine-unity/Assets/spine-unity/CustomSkin.cs
Normal file
36
spine-unity/Assets/spine-unity/CustomSkin.cs
Normal file
@ -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<SkeletonRenderer>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
8
spine-unity/Assets/spine-unity/CustomSkin.cs.meta
Normal file
8
spine-unity/Assets/spine-unity/CustomSkin.cs.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e55c8477eccddc4cb5c3551a3945ca7
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@ -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<AtlasRegion> regions = (List<AtlasRegion>)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();
|
||||
}
|
||||
|
||||
BIN
spine-unity/Assets/spine-unity/Editor/GUI/icon-slotRoot.png
Normal file
BIN
spine-unity/Assets/spine-unity/Editor/GUI/icon-slotRoot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 649 B |
@ -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:
|
||||
@ -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: []
|
||||
|
||||
BIN
spine-unity/Assets/spine-unity/Editor/GUI/icon-weights.png
Normal file
BIN
spine-unity/Assets/spine-unity/Editor/GUI/icon-weights.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 698 B |
@ -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:
|
||||
@ -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<string> warnings = new List<string>();
|
||||
|
||||
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<Attachment> slotAttachments = new List<Attachment>();
|
||||
List<string> slotAttachmentNames = new List<string>();
|
||||
List<string> defaultSkinAttachmentNames = new List<string>();
|
||||
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<Atlas> atlasList = new List<Atlas>();
|
||||
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<Spine.Event> m_animEvents = new List<Spine.Event>();
|
||||
List<float> m_animEventFrames = new List<float>();
|
||||
|
||||
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<SkeletonAnimation>();
|
||||
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<SkeletonAnimation>();
|
||||
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);
|
||||
|
||||
@ -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 () {
|
||||
|
||||
585
spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs
Normal file
585
spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs
Normal file
@ -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<SkeletonRenderer>() != null) {
|
||||
var skeletonRenderer = component.GetComponent<SkeletonRenderer>();
|
||||
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<SkeletonRenderer>() != null) {
|
||||
var skeletonRenderer = component.GetComponent<SkeletonRenderer>();
|
||||
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<AtlasRegion> regions = (List<AtlasRegion>)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<SkeletonRenderer>() != null) {
|
||||
var skeletonRenderer = component.GetComponent<SkeletonRenderer>();
|
||||
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<SkeletonRenderer>() != null) {
|
||||
skeletonRenderer = component.GetComponent<SkeletonRenderer>();
|
||||
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<Skin> validSkins = new List<Skin>();
|
||||
|
||||
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<string> attachmentNames = new List<string>();
|
||||
List<string> placeholderNames = new List<string>();
|
||||
|
||||
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<SkeletonRenderer>() != null) {
|
||||
var skeletonRenderer = component.GetComponent<SkeletonRenderer>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2de282d583d4a641bf1c349f0a3eef9
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@ -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<TextAsset> spineJsonList = new List<TextAsset>();
|
||||
|
||||
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<string>(imported);
|
||||
List<string> atlasPaths = new List<string>();
|
||||
List<string> imagePaths = new List<string>();
|
||||
List<string> skeletonPaths = new List<string>();
|
||||
|
||||
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<AtlasAsset> atlases = new List<AtlasAsset>();
|
||||
|
||||
//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<AtlasAsset> MultiAtlasDialog(List<string> requiredPaths, string initialDirectory, string header = "") {
|
||||
|
||||
List<AtlasAsset> atlasAssets = new List<AtlasAsset>();
|
||||
|
||||
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<string> missingRegions = new List<string>(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<string> GetRequiredAtlasRegions(string jsonPath) {
|
||||
List<string> requiredPaths = new List<string>();
|
||||
|
||||
TextAsset spineJson = (TextAsset)AssetDatabase.LoadAssetAtPath(jsonPath, typeof(TextAsset));
|
||||
|
||||
StringReader reader = new StringReader(spineJson.text);
|
||||
var root = Json.Deserialize(reader) as Dictionary<string, object>;
|
||||
|
||||
foreach (KeyValuePair<string, object> entry in (Dictionary<string, object>)root["skins"]) {
|
||||
foreach (KeyValuePair<string, object> slotEntry in (Dictionary<string, object>)entry.Value) {
|
||||
|
||||
foreach (KeyValuePair<string, object> attachmentEntry in ((Dictionary<string, object>)slotEntry.Value)) {
|
||||
var data = ((Dictionary<string, object>)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<string> requiredPaths, List<AtlasAsset> 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<AtlasAsset> FindAtlasesAtPath(string path) {
|
||||
List<AtlasAsset> arr = new List<AtlasAsset>();
|
||||
|
||||
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<string> pageFiles = new List<string>();
|
||||
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<SkeletonDataAsset>();
|
||||
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<SkeletonAnimation>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -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<Slot> submeshSeparatorSlots = new List<Slot>();
|
||||
|
||||
|
||||
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<Material> submeshMaterials = new List<Material>();
|
||||
private readonly List<Submesh> submeshes = new List<Submesh>();
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
|
||||
public class SkeletonUtilityKinematicShadow : MonoBehaviour {
|
||||
public bool hideShadow = true;
|
||||
public Transform parent;
|
||||
Dictionary<Transform, Transform> 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<Transform, Transform>();
|
||||
|
||||
|
||||
198
spine-unity/Assets/spine-unity/SpineAttributes.cs
Normal file
198
spine-unity/Assets/spine-unity/SpineAttributes.cs
Normal file
@ -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 = "";
|
||||
|
||||
/// <summary>
|
||||
/// Smart popup menu for Spine Slots
|
||||
/// </summary>
|
||||
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
|
||||
/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
|
||||
/// </param>
|
||||
public SpineSlot(string startsWith = "", string dataField = "") {
|
||||
this.startsWith = startsWith;
|
||||
this.dataField = dataField;
|
||||
}
|
||||
}
|
||||
|
||||
public class SpineSkin : PropertyAttribute {
|
||||
public string startsWith = "";
|
||||
public string dataField = "";
|
||||
|
||||
/// <summary>
|
||||
/// Smart popup menu for Spine Skins
|
||||
/// </summary>
|
||||
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
|
||||
/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
|
||||
/// </param>
|
||||
public SpineSkin(string startsWith = "", string dataField = "") {
|
||||
this.startsWith = startsWith;
|
||||
this.dataField = dataField;
|
||||
}
|
||||
}
|
||||
public class SpineAnimation : PropertyAttribute {
|
||||
public string startsWith = "";
|
||||
public string dataField = "";
|
||||
|
||||
/// <summary>
|
||||
/// Smart popup menu for Spine Animations
|
||||
/// </summary>
|
||||
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
|
||||
/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
|
||||
/// </param>
|
||||
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() {
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smart popup menu for Spine Attachments
|
||||
/// </summary>
|
||||
/// <param name="currentSkinOnly">Filters popup results to only include the current Skin. Only valid when a SkeletonRenderer is the data source.</param>
|
||||
/// <param name="returnAttachmentPath">Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName"</param>
|
||||
/// <param name="placeholdersOnly">Filters popup results to exclude attachments that are not children of Skin Placeholders</param>
|
||||
/// <param name="slotField">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</param>
|
||||
/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
|
||||
/// </param>
|
||||
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 = "";
|
||||
|
||||
/// <summary>
|
||||
/// Smart popup menu for Spine Bones
|
||||
/// </summary>
|
||||
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
|
||||
/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
|
||||
/// </param>
|
||||
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.
|
||||
}
|
||||
8
spine-unity/Assets/spine-unity/SpineAttributes.cs.meta
Normal file
8
spine-unity/Assets/spine-unity/SpineAttributes.cs.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce216f51ebc1d3f40929f4e58d1c65e5
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
Loading…
x
Reference in New Issue
Block a user