mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-11 09:38:44 +08:00
All scripts now in Spine.Unity and Spine.Unity.Modules namespace
This commit is contained in:
parent
c0eb1a5237
commit
cc95db8e6c
@ -34,148 +34,150 @@ using System.IO;
|
||||
using UnityEngine;
|
||||
using Spine;
|
||||
|
||||
/// <summary>Loads and stores a Spine atlas and list of materials.</summary>
|
||||
public class AtlasAsset : ScriptableObject {
|
||||
public TextAsset atlasFile;
|
||||
public Material[] materials;
|
||||
private Atlas atlas;
|
||||
namespace Spine.Unity {
|
||||
/// <summary>Loads and stores a Spine atlas and list of materials.</summary>
|
||||
public class AtlasAsset : ScriptableObject {
|
||||
public TextAsset atlasFile;
|
||||
public Material[] materials;
|
||||
private Atlas atlas;
|
||||
|
||||
public void Reset () {
|
||||
atlas = null;
|
||||
}
|
||||
|
||||
/// <returns>The atlas or null if it could not be loaded.</returns>
|
||||
public Atlas GetAtlas () {
|
||||
if (atlasFile == null) {
|
||||
Debug.LogError("Atlas file not set for atlas asset: " + name, this);
|
||||
Reset();
|
||||
return null;
|
||||
public void Reset () {
|
||||
atlas = null;
|
||||
}
|
||||
|
||||
if (materials == null || materials.Length == 0) {
|
||||
Debug.LogError("Materials not set for atlas asset: " + name, this);
|
||||
Reset();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (atlas != null)
|
||||
return atlas;
|
||||
|
||||
try {
|
||||
atlas = new Atlas(new StringReader(atlasFile.text), "", new MaterialsTextureLoader(this));
|
||||
atlas.FlipV();
|
||||
return atlas;
|
||||
} catch (Exception ex) {
|
||||
Debug.LogError("Error reading atlas file for atlas asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Sprite GenerateSprite (string name, out Material material) {
|
||||
AtlasRegion region = atlas.FindRegion(name);
|
||||
|
||||
Sprite sprite = null;
|
||||
material = null;
|
||||
|
||||
if (region != null) {
|
||||
//sprite.rect
|
||||
}
|
||||
|
||||
return sprite;
|
||||
}
|
||||
|
||||
public Mesh GenerateMesh (string name, Mesh mesh, out Material material, float scale = 0.01f) {
|
||||
AtlasRegion region = atlas.FindRegion(name);
|
||||
material = null;
|
||||
if (region != null) {
|
||||
if (mesh == null) {
|
||||
mesh = new Mesh();
|
||||
mesh.name = name;
|
||||
/// <returns>The atlas or null if it could not be loaded.</returns>
|
||||
public Atlas GetAtlas () {
|
||||
if (atlasFile == null) {
|
||||
Debug.LogError("Atlas file not set for atlas asset: " + name, this);
|
||||
Reset();
|
||||
return null;
|
||||
}
|
||||
|
||||
Vector3[] verts = new Vector3[4];
|
||||
Vector2[] uvs = new Vector2[4];
|
||||
Color[] colors = new Color[4] { Color.white, Color.white, Color.white, Color.white };
|
||||
int[] triangles = new int[6] { 0, 1, 2, 2, 3, 0 };
|
||||
if (materials == null || materials.Length == 0) {
|
||||
Debug.LogError("Materials not set for atlas asset: " + name, this);
|
||||
Reset();
|
||||
return null;
|
||||
}
|
||||
|
||||
float left, right, top, bottom;
|
||||
left = region.width / -2f;
|
||||
right = left * -1f;
|
||||
top = region.height / 2f;
|
||||
bottom = top * -1;
|
||||
if (atlas != null)
|
||||
return atlas;
|
||||
|
||||
verts[0] = new Vector3(left, bottom, 0) * scale;
|
||||
verts[1] = new Vector3(left, top, 0) * scale;
|
||||
verts[2] = new Vector3(right, top, 0) * scale;
|
||||
verts[3] = new Vector3(right, bottom, 0) * scale;
|
||||
float u, v, u2, v2;
|
||||
u = region.u;
|
||||
v = region.v;
|
||||
u2 = region.u2;
|
||||
v2 = region.v2;
|
||||
try {
|
||||
atlas = new Atlas(new StringReader(atlasFile.text), "", new MaterialsTextureLoader(this));
|
||||
atlas.FlipV();
|
||||
return atlas;
|
||||
} catch (Exception ex) {
|
||||
Debug.LogError("Error reading atlas file for atlas asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!region.rotate) {
|
||||
uvs[0] = new Vector2(u, v2);
|
||||
uvs[1] = new Vector2(u, v);
|
||||
uvs[2] = new Vector2(u2, v);
|
||||
uvs[3] = new Vector2(u2, v2);
|
||||
public Sprite GenerateSprite (string name, out Material material) {
|
||||
AtlasRegion region = atlas.FindRegion(name);
|
||||
|
||||
Sprite sprite = null;
|
||||
material = null;
|
||||
|
||||
if (region != null) {
|
||||
//sprite.rect
|
||||
}
|
||||
|
||||
return sprite;
|
||||
}
|
||||
|
||||
public Mesh GenerateMesh (string name, Mesh mesh, out Material material, float scale = 0.01f) {
|
||||
AtlasRegion region = atlas.FindRegion(name);
|
||||
material = null;
|
||||
if (region != null) {
|
||||
if (mesh == null) {
|
||||
mesh = new Mesh();
|
||||
mesh.name = name;
|
||||
}
|
||||
|
||||
Vector3[] verts = new Vector3[4];
|
||||
Vector2[] uvs = new Vector2[4];
|
||||
Color[] colors = new Color[4] { Color.white, Color.white, Color.white, Color.white };
|
||||
int[] triangles = new int[6] { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
float left, right, top, bottom;
|
||||
left = region.width / -2f;
|
||||
right = left * -1f;
|
||||
top = region.height / 2f;
|
||||
bottom = top * -1;
|
||||
|
||||
verts[0] = new Vector3(left, bottom, 0) * scale;
|
||||
verts[1] = new Vector3(left, top, 0) * scale;
|
||||
verts[2] = new Vector3(right, top, 0) * scale;
|
||||
verts[3] = new Vector3(right, bottom, 0) * scale;
|
||||
float u, v, u2, v2;
|
||||
u = region.u;
|
||||
v = region.v;
|
||||
u2 = region.u2;
|
||||
v2 = region.v2;
|
||||
|
||||
if (!region.rotate) {
|
||||
uvs[0] = new Vector2(u, v2);
|
||||
uvs[1] = new Vector2(u, v);
|
||||
uvs[2] = new Vector2(u2, v);
|
||||
uvs[3] = new Vector2(u2, v2);
|
||||
} else {
|
||||
uvs[0] = new Vector2(u2, v2);
|
||||
uvs[1] = new Vector2(u, v2);
|
||||
uvs[2] = new Vector2(u, v);
|
||||
uvs[3] = new Vector2(u2, v);
|
||||
}
|
||||
|
||||
mesh.triangles = new int[0];
|
||||
mesh.vertices = verts;
|
||||
mesh.uv = uvs;
|
||||
mesh.colors = colors;
|
||||
mesh.triangles = triangles;
|
||||
mesh.RecalculateNormals();
|
||||
mesh.RecalculateBounds();
|
||||
|
||||
material = (Material)region.page.rendererObject;
|
||||
} else {
|
||||
uvs[0] = new Vector2(u2, v2);
|
||||
uvs[1] = new Vector2(u, v2);
|
||||
uvs[2] = new Vector2(u, v);
|
||||
uvs[3] = new Vector2(u2, v);
|
||||
mesh = null;
|
||||
}
|
||||
|
||||
mesh.triangles = new int[0];
|
||||
mesh.vertices = verts;
|
||||
mesh.uv = uvs;
|
||||
mesh.colors = colors;
|
||||
mesh.triangles = triangles;
|
||||
mesh.RecalculateNormals();
|
||||
mesh.RecalculateBounds();
|
||||
return mesh;
|
||||
}
|
||||
}
|
||||
|
||||
material = (Material)region.page.rendererObject;
|
||||
} else {
|
||||
mesh = null;
|
||||
public class MaterialsTextureLoader : TextureLoader {
|
||||
AtlasAsset atlasAsset;
|
||||
|
||||
public MaterialsTextureLoader (AtlasAsset atlasAsset) {
|
||||
this.atlasAsset = atlasAsset;
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
}
|
||||
|
||||
public class MaterialsTextureLoader : TextureLoader {
|
||||
AtlasAsset atlasAsset;
|
||||
|
||||
public MaterialsTextureLoader (AtlasAsset atlasAsset) {
|
||||
this.atlasAsset = atlasAsset;
|
||||
}
|
||||
|
||||
public void Load (AtlasPage page, String path) {
|
||||
String name = Path.GetFileNameWithoutExtension(path);
|
||||
Material material = null;
|
||||
foreach (Material other in atlasAsset.materials) {
|
||||
if (other.mainTexture == null) {
|
||||
Debug.LogError("Material is missing texture: " + other.name, other);
|
||||
public void Load (AtlasPage page, String path) {
|
||||
String name = Path.GetFileNameWithoutExtension(path);
|
||||
Material material = null;
|
||||
foreach (Material other in atlasAsset.materials) {
|
||||
if (other.mainTexture == null) {
|
||||
Debug.LogError("Material is missing texture: " + other.name, other);
|
||||
return;
|
||||
}
|
||||
if (other.mainTexture.name == name) {
|
||||
material = other;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (material == null) {
|
||||
Debug.LogError("Material with texture name \"" + name + "\" not found for atlas asset: " + atlasAsset.name, atlasAsset);
|
||||
return;
|
||||
}
|
||||
if (other.mainTexture.name == name) {
|
||||
material = other;
|
||||
break;
|
||||
page.rendererObject = material;
|
||||
|
||||
// Very old atlas files expected the texture's actual size to be used at runtime.
|
||||
if (page.width == 0 || page.height == 0) {
|
||||
page.width = material.mainTexture.width;
|
||||
page.height = material.mainTexture.height;
|
||||
}
|
||||
}
|
||||
if (material == null) {
|
||||
Debug.LogError("Material with texture name \"" + name + "\" not found for atlas asset: " + atlasAsset.name, atlasAsset);
|
||||
return;
|
||||
}
|
||||
page.rendererObject = material;
|
||||
|
||||
// Very old atlas files expected the texture's actual size to be used at runtime.
|
||||
if (page.width == 0 || page.height == 0) {
|
||||
page.width = material.mainTexture.width;
|
||||
page.height = material.mainTexture.height;
|
||||
public void Unload (object texture) {
|
||||
}
|
||||
}
|
||||
|
||||
public void Unload (object texture) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,10 +182,7 @@ namespace Spine.Unity.Editor {
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(new GUIContent("Bake All Skins", SpineEditorUtilities.Icons.unityIcon), GUILayout.Height(32), GUILayout.Width(150)))
|
||||
SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, m_skeletonData.Skins, "", bakeAnimations, bakeIK, bakeEventOptions);
|
||||
|
||||
@ -200,35 +197,20 @@ namespace Spine.Unity.Editor {
|
||||
} else
|
||||
skinName = m_skeletonAnimation.skeleton.Skin.Name;
|
||||
|
||||
bool oops = false;
|
||||
|
||||
try {
|
||||
GUILayout.BeginVertical();
|
||||
using (var m = new EditorGUILayout.VerticalScope()) {
|
||||
if (GUILayout.Button(new GUIContent("Bake " + skinName, SpineEditorUtilities.Icons.unityIcon), GUILayout.Height(32), GUILayout.Width(250)))
|
||||
SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, new ExposedList<Skin>(new [] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions);
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label(new GUIContent("Skins", SpineEditorUtilities.Icons.skinsRoot), GUILayout.Width(50));
|
||||
if (GUILayout.Button(skinName, EditorStyles.popup, GUILayout.Width(196))) {
|
||||
SelectSkinContext();
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
GUILayout.Label(new GUIContent("Skins", SpineEditorUtilities.Icons.skinsRoot), GUILayout.Width(50));
|
||||
if (GUILayout.Button(skinName, EditorStyles.popup, GUILayout.Width(196))) {
|
||||
SelectSkinContext();
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
|
||||
|
||||
} catch {
|
||||
oops = true;
|
||||
//GUILayout.BeginVertical();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!oops)
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
@ -31,145 +31,147 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Spine;
|
||||
|
||||
public class SkeletonDataAsset : ScriptableObject {
|
||||
public AtlasAsset[] atlasAssets;
|
||||
#if SPINE_TK2D
|
||||
public tk2dSpriteCollectionData spriteCollection;
|
||||
#endif
|
||||
public TextAsset skeletonJSON;
|
||||
public float scale = 0.01f;
|
||||
public String[] fromAnimation;
|
||||
public String[] toAnimation;
|
||||
public float[] duration;
|
||||
public float defaultMix;
|
||||
public RuntimeAnimatorController controller;
|
||||
private SkeletonData skeletonData;
|
||||
private AnimationStateData stateData;
|
||||
namespace Spine.Unity {
|
||||
public class SkeletonDataAsset : ScriptableObject {
|
||||
public AtlasAsset[] atlasAssets;
|
||||
#if SPINE_TK2D
|
||||
public tk2dSpriteCollectionData spriteCollection;
|
||||
#endif
|
||||
public TextAsset skeletonJSON;
|
||||
public float scale = 0.01f;
|
||||
public String[] fromAnimation;
|
||||
public String[] toAnimation;
|
||||
public float[] duration;
|
||||
public float defaultMix;
|
||||
public RuntimeAnimatorController controller;
|
||||
private SkeletonData skeletonData;
|
||||
private AnimationStateData stateData;
|
||||
|
||||
void OnEnable () {
|
||||
if (atlasAssets == null)
|
||||
atlasAssets = new AtlasAsset[0];
|
||||
}
|
||||
|
||||
public void Reset () {
|
||||
skeletonData = null;
|
||||
stateData = 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();
|
||||
return null;
|
||||
void OnEnable () {
|
||||
if (atlasAssets == null)
|
||||
atlasAssets = new AtlasAsset[0];
|
||||
}
|
||||
|
||||
if (skeletonJSON == null) {
|
||||
if (!quiet)
|
||||
Debug.LogError("Skeleton JSON file not set for SkeletonData asset: " + name, this);
|
||||
Reset();
|
||||
return null;
|
||||
public void Reset () {
|
||||
skeletonData = null;
|
||||
stateData = null;
|
||||
}
|
||||
|
||||
#if !SPINE_TK2D
|
||||
if (atlasAssets.Length == 0) {
|
||||
Reset();
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
if (atlasAssets.Length == 0 && spriteCollection == null) {
|
||||
Reset();
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
Atlas[] atlasArr = new Atlas[atlasAssets.Length];
|
||||
for (int i = 0; i < atlasAssets.Length; i++) {
|
||||
if (atlasAssets[i] == 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();
|
||||
return null;
|
||||
}
|
||||
atlasArr[i] = atlasAssets[i].GetAtlas();
|
||||
if (atlasArr[i] == null) {
|
||||
|
||||
if (skeletonJSON == null) {
|
||||
if (!quiet)
|
||||
Debug.LogError("Skeleton JSON file not set for SkeletonData asset: " + name, this);
|
||||
Reset();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (skeletonData != null)
|
||||
return skeletonData;
|
||||
|
||||
AttachmentLoader attachmentLoader;
|
||||
float skeletonDataScale;
|
||||
|
||||
#if !SPINE_TK2D
|
||||
attachmentLoader = new AtlasAttachmentLoader(atlasArr);
|
||||
skeletonDataScale = scale;
|
||||
#else
|
||||
if (spriteCollection != null) {
|
||||
attachmentLoader = new SpriteCollectionAttachmentLoader(spriteCollection);
|
||||
skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale);
|
||||
} else {
|
||||
if (atlasArr.Length == 0) {
|
||||
#if !SPINE_TK2D
|
||||
if (atlasAssets.Length == 0) {
|
||||
Reset();
|
||||
if (!quiet) Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
if (atlasAssets.Length == 0 && spriteCollection == null) {
|
||||
Reset();
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
AttachmentLoader attachmentLoader;
|
||||
float skeletonDataScale;
|
||||
|
||||
#if !SPINE_TK2D
|
||||
attachmentLoader = new AtlasAttachmentLoader(atlasArr);
|
||||
skeletonDataScale = scale;
|
||||
}
|
||||
#endif
|
||||
|
||||
try {
|
||||
//var stopwatch = new System.Diagnostics.Stopwatch();
|
||||
if (skeletonJSON.name.ToLower().Contains(".skel")) {
|
||||
var input = new MemoryStream(skeletonJSON.bytes);
|
||||
var binary = new SkeletonBinary(attachmentLoader);
|
||||
binary.Scale = skeletonDataScale;
|
||||
//stopwatch.Start();
|
||||
skeletonData = binary.ReadSkeletonData(input);
|
||||
#else
|
||||
if (spriteCollection != null) {
|
||||
attachmentLoader = new Spine.Unity.TK2D.SpriteCollectionAttachmentLoader(spriteCollection);
|
||||
skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale);
|
||||
} else {
|
||||
var input = new StringReader(skeletonJSON.text);
|
||||
var json = new SkeletonJson(attachmentLoader);
|
||||
json.Scale = skeletonDataScale;
|
||||
//stopwatch.Start();
|
||||
skeletonData = json.ReadSkeletonData(input);
|
||||
if (atlasArr.Length == 0) {
|
||||
Reset();
|
||||
if (!quiet) Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
|
||||
return null;
|
||||
}
|
||||
attachmentLoader = new AtlasAttachmentLoader(atlasArr);
|
||||
skeletonDataScale = scale;
|
||||
}
|
||||
//stopwatch.Stop();
|
||||
//Debug.Log(stopwatch.Elapsed);
|
||||
} catch (Exception ex) {
|
||||
if (!quiet)
|
||||
Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this);
|
||||
return null;
|
||||
#endif
|
||||
|
||||
try {
|
||||
//var stopwatch = new System.Diagnostics.Stopwatch();
|
||||
if (skeletonJSON.name.ToLower().Contains(".skel")) {
|
||||
var input = new MemoryStream(skeletonJSON.bytes);
|
||||
var binary = new SkeletonBinary(attachmentLoader);
|
||||
binary.Scale = skeletonDataScale;
|
||||
//stopwatch.Start();
|
||||
skeletonData = binary.ReadSkeletonData(input);
|
||||
} else {
|
||||
var input = new StringReader(skeletonJSON.text);
|
||||
var json = new SkeletonJson(attachmentLoader);
|
||||
json.Scale = skeletonDataScale;
|
||||
//stopwatch.Start();
|
||||
skeletonData = json.ReadSkeletonData(input);
|
||||
}
|
||||
//stopwatch.Stop();
|
||||
//Debug.Log(stopwatch.Elapsed);
|
||||
} catch (Exception ex) {
|
||||
if (!quiet)
|
||||
Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this);
|
||||
return null;
|
||||
}
|
||||
|
||||
stateData = new AnimationStateData(skeletonData);
|
||||
FillStateData();
|
||||
|
||||
return skeletonData;
|
||||
}
|
||||
|
||||
stateData = new AnimationStateData(skeletonData);
|
||||
FillStateData();
|
||||
public void FillStateData () {
|
||||
if (stateData == null)
|
||||
return;
|
||||
|
||||
return skeletonData;
|
||||
}
|
||||
|
||||
public void FillStateData () {
|
||||
if (stateData == null)
|
||||
return;
|
||||
|
||||
stateData.DefaultMix = defaultMix;
|
||||
for (int i = 0, n = fromAnimation.Length; i < n; i++) {
|
||||
if (fromAnimation[i].Length == 0 || toAnimation[i].Length == 0)
|
||||
continue;
|
||||
stateData.SetMix(fromAnimation[i], toAnimation[i], duration[i]);
|
||||
stateData.DefaultMix = defaultMix;
|
||||
for (int i = 0, n = fromAnimation.Length; i < n; i++) {
|
||||
if (fromAnimation[i].Length == 0 || toAnimation[i].Length == 0)
|
||||
continue;
|
||||
stateData.SetMix(fromAnimation[i], toAnimation[i], duration[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AnimationStateData GetAnimationStateData () {
|
||||
if (stateData != null)
|
||||
public AnimationStateData GetAnimationStateData () {
|
||||
if (stateData != null)
|
||||
return stateData;
|
||||
GetSkeletonData(false);
|
||||
return stateData;
|
||||
GetSkeletonData(false);
|
||||
return stateData;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -28,119 +28,118 @@
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Spine;
|
||||
|
||||
/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Spine/BoneFollower")]
|
||||
public class BoneFollower : MonoBehaviour {
|
||||
namespace Spine.Unity {
|
||||
/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Spine/BoneFollower")]
|
||||
public class BoneFollower : MonoBehaviour {
|
||||
|
||||
#region Inspector
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
public SkeletonRenderer SkeletonRenderer {
|
||||
get { return skeletonRenderer; }
|
||||
set {
|
||||
skeletonRenderer = value;
|
||||
#region Inspector
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
public SkeletonRenderer SkeletonRenderer {
|
||||
get { return skeletonRenderer; }
|
||||
set {
|
||||
skeletonRenderer = value;
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
|
||||
[SpineBone(dataField: "skeletonRenderer")]
|
||||
public String boneName;
|
||||
|
||||
public bool followZPosition = true;
|
||||
public bool followBoneRotation = true;
|
||||
public bool resetOnAwake = true;
|
||||
#endregion
|
||||
|
||||
[NonSerialized]
|
||||
public bool valid;
|
||||
|
||||
[NonSerialized]
|
||||
public Bone bone;
|
||||
Transform skeletonTransform;
|
||||
|
||||
public void HandleResetRenderer (SkeletonRenderer skeletonRenderer) {
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
|
||||
[SpineBone(dataField: "skeletonRenderer")]
|
||||
public String boneName;
|
||||
|
||||
public bool followZPosition = true;
|
||||
public bool followBoneRotation = true;
|
||||
public bool resetOnAwake = true;
|
||||
#endregion
|
||||
public void Reset () {
|
||||
bone = null;
|
||||
valid = skeletonRenderer != null && skeletonRenderer.valid;
|
||||
|
||||
[NonSerialized]
|
||||
public bool valid;
|
||||
if (!valid) return;
|
||||
|
||||
[NonSerialized]
|
||||
public Bone bone;
|
||||
Transform skeletonTransform;
|
||||
|
||||
public void HandleResetRenderer (SkeletonRenderer skeletonRenderer) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset () {
|
||||
bone = null;
|
||||
valid = skeletonRenderer != null && skeletonRenderer.valid;
|
||||
|
||||
if (!valid) return;
|
||||
|
||||
skeletonTransform = skeletonRenderer.transform;
|
||||
skeletonRenderer.OnRebuild -= HandleResetRenderer;
|
||||
skeletonRenderer.OnRebuild += HandleResetRenderer;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor)
|
||||
DoUpdate();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
if (skeletonRenderer != null)
|
||||
skeletonTransform = skeletonRenderer.transform;
|
||||
skeletonRenderer.OnRebuild -= HandleResetRenderer;
|
||||
}
|
||||
skeletonRenderer.OnRebuild += HandleResetRenderer;
|
||||
|
||||
public void Awake () {
|
||||
if (resetOnAwake)
|
||||
Reset();
|
||||
}
|
||||
|
||||
void LateUpdate () {
|
||||
DoUpdate();
|
||||
}
|
||||
|
||||
public void DoUpdate () {
|
||||
if (!valid) {
|
||||
Reset();
|
||||
return;
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor)
|
||||
DoUpdate();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (bone == null) {
|
||||
if (boneName == null || boneName.Length == 0)
|
||||
void OnDestroy () {
|
||||
if (skeletonRenderer != null)
|
||||
skeletonRenderer.OnRebuild -= HandleResetRenderer;
|
||||
}
|
||||
|
||||
public void Awake () {
|
||||
if (resetOnAwake)
|
||||
Reset();
|
||||
}
|
||||
|
||||
void LateUpdate () {
|
||||
DoUpdate();
|
||||
}
|
||||
|
||||
public void DoUpdate () {
|
||||
if (!valid) {
|
||||
Reset();
|
||||
return;
|
||||
bone = skeletonRenderer.skeleton.FindBone(boneName);
|
||||
}
|
||||
|
||||
if (bone == null) {
|
||||
Debug.LogError("Bone not found: " + boneName, this);
|
||||
return;
|
||||
if (boneName == null || boneName.Length == 0)
|
||||
return;
|
||||
bone = skeletonRenderer.skeleton.FindBone(boneName);
|
||||
if (bone == null) {
|
||||
Debug.LogError("Bone not found: " + boneName, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Skeleton skeleton = skeletonRenderer.skeleton;
|
||||
float flipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f;
|
||||
Transform thisTransform = this.transform;
|
||||
|
||||
// Recommended setup: Use local transform properties if Spine GameObject is parent
|
||||
if (thisTransform.parent == skeletonTransform) {
|
||||
thisTransform.localPosition = new Vector3(bone.worldX, bone.worldY, followZPosition ? 0f : thisTransform.localPosition.z);
|
||||
|
||||
if (followBoneRotation) {
|
||||
Vector3 rotation = thisTransform.localRotation.eulerAngles;
|
||||
thisTransform.localRotation = Quaternion.Euler(rotation.x, rotation.y, bone.WorldRotationX * flipRotation);
|
||||
}
|
||||
|
||||
// For special cases: Use transform world properties if transform relationship is complicated
|
||||
} else {
|
||||
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY, 0f));
|
||||
if (!followZPosition)
|
||||
targetWorldPosition.z = thisTransform.position.z;
|
||||
|
||||
thisTransform.position = targetWorldPosition;
|
||||
|
||||
if (followBoneRotation) {
|
||||
Vector3 worldRotation = skeletonTransform.rotation.eulerAngles;
|
||||
thisTransform.rotation = Quaternion.Euler(worldRotation.x, worldRotation.y, skeletonTransform.rotation.eulerAngles.z + (bone.WorldRotationX * flipRotation));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Skeleton skeleton = skeletonRenderer.skeleton;
|
||||
float flipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f;
|
||||
Transform thisTransform = this.transform;
|
||||
|
||||
// Recommended setup: Use local transform properties if Spine GameObject is parent
|
||||
if (thisTransform.parent == skeletonTransform) {
|
||||
thisTransform.localPosition = new Vector3(bone.worldX, bone.worldY, followZPosition ? 0f : thisTransform.localPosition.z);
|
||||
|
||||
if (followBoneRotation) {
|
||||
Vector3 rotation = thisTransform.localRotation.eulerAngles;
|
||||
thisTransform.localRotation = Quaternion.Euler(rotation.x, rotation.y, bone.WorldRotationX * flipRotation);
|
||||
}
|
||||
|
||||
// For special cases: Use transform world properties if transform relationship is complicated
|
||||
} else {
|
||||
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY, 0f));
|
||||
if (!followZPosition)
|
||||
targetWorldPosition.z = thisTransform.position.z;
|
||||
|
||||
thisTransform.position = targetWorldPosition;
|
||||
|
||||
if (followBoneRotation) {
|
||||
Vector3 worldRotation = skeletonTransform.rotation.eulerAngles;
|
||||
thisTransform.rotation = Quaternion.Euler(worldRotation.x, worldRotation.y, skeletonTransform.rotation.eulerAngles.z + (bone.WorldRotationX * flipRotation));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -30,10 +30,8 @@
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using UnityEditor;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
public static class SpineInspectorUtility {
|
||||
|
||||
@ -28,53 +28,53 @@
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using Spine;
|
||||
|
||||
namespace Spine.Unity.Modules {
|
||||
public class AtlasRegionAttacher : MonoBehaviour {
|
||||
|
||||
public class AtlasRegionAttacher : MonoBehaviour {
|
||||
[System.Serializable]
|
||||
public class SlotRegionPair {
|
||||
[SpineSlot]
|
||||
public string slot;
|
||||
|
||||
[System.Serializable]
|
||||
public class SlotRegionPair {
|
||||
[SpineSlot]
|
||||
public string slot;
|
||||
|
||||
[SpineAtlasRegion]
|
||||
public string region;
|
||||
}
|
||||
|
||||
public AtlasAsset atlasAsset;
|
||||
public SlotRegionPair[] attachments;
|
||||
|
||||
Atlas atlas;
|
||||
|
||||
void Awake () {
|
||||
GetComponent<SkeletonRenderer>().OnRebuild += Apply;
|
||||
}
|
||||
|
||||
|
||||
void Apply (SkeletonRenderer skeletonRenderer) {
|
||||
atlas = atlasAsset.GetAtlas();
|
||||
|
||||
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 = skeletonRenderer.skeleton.FindSlot(entry.slot);
|
||||
slot.Attachment = regionAttachment;
|
||||
[SpineAtlasRegion]
|
||||
public string region;
|
||||
}
|
||||
}
|
||||
|
||||
public AtlasAsset atlasAsset;
|
||||
public SlotRegionPair[] attachments;
|
||||
|
||||
Atlas atlas;
|
||||
|
||||
void Awake () {
|
||||
GetComponent<SkeletonRenderer>().OnRebuild += Apply;
|
||||
}
|
||||
|
||||
|
||||
void Apply (SkeletonRenderer skeletonRenderer) {
|
||||
atlas = atlasAsset.GetAtlas();
|
||||
|
||||
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 = skeletonRenderer.skeleton.FindSlot(entry.slot);
|
||||
slot.Attachment = regionAttachment;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,167 +28,159 @@
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Spine;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
public class BoundingBoxFollower : MonoBehaviour {
|
||||
namespace Spine.Unity {
|
||||
[ExecuteInEditMode]
|
||||
public class BoundingBoxFollower : MonoBehaviour {
|
||||
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
[SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)]
|
||||
public string slotName;
|
||||
|
||||
[SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)]
|
||||
public string slotName;
|
||||
//TODO: not this
|
||||
[Tooltip("LOL JK, Someone else do it!")]
|
||||
public bool use3DMeshCollider;
|
||||
|
||||
//TODO: not this
|
||||
[Tooltip("LOL JK, Someone else do it!")]
|
||||
public bool use3DMeshCollider;
|
||||
private Slot slot;
|
||||
private BoundingBoxAttachment currentAttachment;
|
||||
private PolygonCollider2D currentCollider;
|
||||
private string currentAttachmentName;
|
||||
private bool valid = false;
|
||||
private bool hasReset;
|
||||
|
||||
private Slot slot;
|
||||
private BoundingBoxAttachment currentAttachment;
|
||||
private PolygonCollider2D currentCollider;
|
||||
private string currentAttachmentName;
|
||||
private bool valid = false;
|
||||
private bool hasReset;
|
||||
public Dictionary<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>();
|
||||
public Dictionary<BoundingBoxAttachment, string> attachmentNameTable = new Dictionary<BoundingBoxAttachment, string>();
|
||||
|
||||
public Dictionary<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>();
|
||||
public Dictionary<BoundingBoxAttachment, string> attachmentNameTable = new Dictionary<BoundingBoxAttachment, string>();
|
||||
|
||||
public string CurrentAttachmentName {
|
||||
get {
|
||||
return currentAttachmentName;
|
||||
public string CurrentAttachmentName {
|
||||
get { return currentAttachmentName; }
|
||||
}
|
||||
}
|
||||
|
||||
public BoundingBoxAttachment CurrentAttachment {
|
||||
get {
|
||||
return currentAttachment;
|
||||
public BoundingBoxAttachment CurrentAttachment {
|
||||
get { return currentAttachment; }
|
||||
}
|
||||
}
|
||||
|
||||
public PolygonCollider2D CurrentCollider {
|
||||
get {
|
||||
return currentCollider;
|
||||
public PolygonCollider2D CurrentCollider {
|
||||
get { return currentCollider; }
|
||||
}
|
||||
}
|
||||
|
||||
public Slot Slot {
|
||||
get {
|
||||
return slot;
|
||||
public Slot Slot {
|
||||
get { return slot; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnEnable () {
|
||||
ClearColliders();
|
||||
void OnEnable () {
|
||||
ClearColliders();
|
||||
|
||||
if (skeletonRenderer == null)
|
||||
skeletonRenderer = GetComponentInParent<SkeletonRenderer>();
|
||||
if (skeletonRenderer == null)
|
||||
skeletonRenderer = GetComponentInParent<SkeletonRenderer>();
|
||||
|
||||
if (skeletonRenderer != null) {
|
||||
if (skeletonRenderer != null) {
|
||||
skeletonRenderer.OnRebuild -= HandleReset;
|
||||
skeletonRenderer.OnRebuild += HandleReset;
|
||||
|
||||
if (hasReset)
|
||||
HandleReset(skeletonRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
skeletonRenderer.OnRebuild -= HandleReset;
|
||||
skeletonRenderer.OnRebuild += HandleReset;
|
||||
}
|
||||
|
||||
if (hasReset)
|
||||
void Start () {
|
||||
if (!hasReset && skeletonRenderer != null)
|
||||
HandleReset(skeletonRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
skeletonRenderer.OnRebuild -= HandleReset;
|
||||
}
|
||||
public void HandleReset (SkeletonRenderer renderer) {
|
||||
if (slotName == null || slotName == "")
|
||||
return;
|
||||
|
||||
void Start () {
|
||||
if (!hasReset && skeletonRenderer != null)
|
||||
HandleReset(skeletonRenderer);
|
||||
}
|
||||
hasReset = true;
|
||||
|
||||
public void HandleReset (SkeletonRenderer renderer) {
|
||||
if (slotName == null || slotName == "")
|
||||
return;
|
||||
ClearColliders();
|
||||
colliderTable.Clear();
|
||||
|
||||
hasReset = true;
|
||||
|
||||
ClearColliders();
|
||||
colliderTable.Clear();
|
||||
|
||||
if (skeletonRenderer.skeleton == null) {
|
||||
skeletonRenderer.OnRebuild -= HandleReset;
|
||||
skeletonRenderer.Initialize(false);
|
||||
skeletonRenderer.OnRebuild += HandleReset;
|
||||
}
|
||||
if (skeletonRenderer.skeleton == null) {
|
||||
skeletonRenderer.OnRebuild -= HandleReset;
|
||||
skeletonRenderer.Initialize(false);
|
||||
skeletonRenderer.OnRebuild += HandleReset;
|
||||
}
|
||||
|
||||
|
||||
var skeleton = skeletonRenderer.skeleton;
|
||||
slot = skeleton.FindSlot(slotName);
|
||||
int slotIndex = skeleton.FindSlotIndex(slotName);
|
||||
var skeleton = skeletonRenderer.skeleton;
|
||||
slot = skeleton.FindSlot(slotName);
|
||||
int slotIndex = skeleton.FindSlotIndex(slotName);
|
||||
|
||||
foreach (var skin in skeleton.Data.Skins) {
|
||||
List<string> attachmentNames = new List<string>();
|
||||
skin.FindNamesForSlot(slotIndex, attachmentNames);
|
||||
foreach (var skin in skeleton.Data.Skins) {
|
||||
List<string> attachmentNames = new List<string>();
|
||||
skin.FindNamesForSlot(slotIndex, attachmentNames);
|
||||
|
||||
foreach (var name in attachmentNames) {
|
||||
var attachment = skin.GetAttachment(slotIndex, name);
|
||||
if (attachment is BoundingBoxAttachment) {
|
||||
var collider = SkeletonUtility.AddBoundingBoxAsComponent((BoundingBoxAttachment)attachment, gameObject, true);
|
||||
collider.enabled = false;
|
||||
collider.hideFlags = HideFlags.HideInInspector;
|
||||
colliderTable.Add((BoundingBoxAttachment)attachment, collider);
|
||||
attachmentNameTable.Add((BoundingBoxAttachment)attachment, name);
|
||||
foreach (var name in attachmentNames) {
|
||||
var attachment = skin.GetAttachment(slotIndex, name);
|
||||
if (attachment is BoundingBoxAttachment) {
|
||||
var collider = SkeletonUtility.AddBoundingBoxAsComponent((BoundingBoxAttachment)attachment, gameObject, true);
|
||||
collider.enabled = false;
|
||||
collider.hideFlags = HideFlags.HideInInspector;
|
||||
colliderTable.Add((BoundingBoxAttachment)attachment, collider);
|
||||
attachmentNameTable.Add((BoundingBoxAttachment)attachment, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (colliderTable.Count == 0)
|
||||
valid = false;
|
||||
else
|
||||
valid = true;
|
||||
|
||||
if (!valid)
|
||||
Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!");
|
||||
}
|
||||
|
||||
if (colliderTable.Count == 0)
|
||||
valid = false;
|
||||
else
|
||||
valid = true;
|
||||
|
||||
if (!valid)
|
||||
Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!");
|
||||
}
|
||||
|
||||
void ClearColliders () {
|
||||
var colliders = GetComponents<PolygonCollider2D>();
|
||||
if (Application.isPlaying) {
|
||||
foreach (var c in colliders) {
|
||||
Destroy(c);
|
||||
void ClearColliders () {
|
||||
var colliders = GetComponents<PolygonCollider2D>();
|
||||
if (Application.isPlaying) {
|
||||
foreach (var c in colliders) {
|
||||
Destroy(c);
|
||||
}
|
||||
} else {
|
||||
foreach (var c in colliders) {
|
||||
DestroyImmediate(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach (var c in colliders) {
|
||||
DestroyImmediate(c);
|
||||
|
||||
colliderTable.Clear();
|
||||
attachmentNameTable.Clear();
|
||||
}
|
||||
|
||||
void LateUpdate () {
|
||||
if (!skeletonRenderer.valid)
|
||||
return;
|
||||
|
||||
if (slot != null) {
|
||||
if (slot.Attachment != currentAttachment)
|
||||
SetCurrent((BoundingBoxAttachment)slot.Attachment);
|
||||
}
|
||||
}
|
||||
|
||||
colliderTable.Clear();
|
||||
attachmentNameTable.Clear();
|
||||
}
|
||||
void SetCurrent (BoundingBoxAttachment attachment) {
|
||||
if (currentCollider)
|
||||
currentCollider.enabled = false;
|
||||
|
||||
void LateUpdate () {
|
||||
if (!skeletonRenderer.valid)
|
||||
return;
|
||||
if (attachment != null) {
|
||||
currentCollider = colliderTable[attachment];
|
||||
currentCollider.enabled = true;
|
||||
} else {
|
||||
currentCollider = null;
|
||||
}
|
||||
|
||||
if (slot != null) {
|
||||
if (slot.Attachment != currentAttachment)
|
||||
SetCurrent((BoundingBoxAttachment)slot.Attachment);
|
||||
currentAttachment = attachment;
|
||||
|
||||
currentAttachmentName = currentAttachment == null ? null : attachmentNameTable[attachment];
|
||||
}
|
||||
}
|
||||
|
||||
void SetCurrent (BoundingBoxAttachment attachment) {
|
||||
if (currentCollider)
|
||||
currentCollider.enabled = false;
|
||||
|
||||
if (attachment != null) {
|
||||
currentCollider = colliderTable[attachment];
|
||||
currentCollider.enabled = true;
|
||||
} else {
|
||||
currentCollider = null;
|
||||
}
|
||||
|
||||
currentAttachment = attachment;
|
||||
|
||||
currentAttachmentName = currentAttachment == null ? null : attachmentNameTable[attachment];
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,54 +28,56 @@
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using Spine;
|
||||
using Spine.Unity;
|
||||
|
||||
public class CustomSkin : MonoBehaviour {
|
||||
namespace Spine.Unity.Modules {
|
||||
public class CustomSkin : MonoBehaviour {
|
||||
|
||||
[System.Serializable]
|
||||
public class SkinPair {
|
||||
/// <summary>SpineAttachment attachment path to help find the attachment.</summary>
|
||||
/// <remarks>This use of SpineAttachment generates an attachment path string that can only be used by SpineAttachment.GetAttachment.</remarks>
|
||||
[SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skinSource")]
|
||||
[UnityEngine.Serialization.FormerlySerializedAs("sourceAttachment")]
|
||||
public string sourceAttachmentPath;
|
||||
[System.Serializable]
|
||||
public class SkinPair {
|
||||
/// <summary>SpineAttachment attachment path to help find the attachment.</summary>
|
||||
/// <remarks>This use of SpineAttachment generates an attachment path string that can only be used by SpineAttachment.GetAttachment.</remarks>
|
||||
[SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skinSource")]
|
||||
[UnityEngine.Serialization.FormerlySerializedAs("sourceAttachment")]
|
||||
public string sourceAttachmentPath;
|
||||
|
||||
[SpineSlot]
|
||||
public string targetSlot;
|
||||
[SpineSlot]
|
||||
public string targetSlot;
|
||||
|
||||
/// <summary>The name of the skin placeholder/skin dictionary entry this attachment should be associated with.</summary>
|
||||
/// <remarks>This name is used by the skin dictionary, used in the method Skin.AddAttachment as well as setting a slot attachment</remarks>
|
||||
[SpineAttachment(currentSkinOnly: true, placeholdersOnly: true)]
|
||||
public string targetAttachment;
|
||||
}
|
||||
|
||||
#region Inspector
|
||||
public SkeletonDataAsset skinSource;
|
||||
|
||||
[UnityEngine.Serialization.FormerlySerializedAs("skinning")]
|
||||
public SkinPair[] skinItems;
|
||||
|
||||
public Skin customSkin;
|
||||
#endregion
|
||||
|
||||
SkeletonRenderer skeletonRenderer;
|
||||
|
||||
void Start () {
|
||||
skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||
Skeleton skeleton = skeletonRenderer.skeleton;
|
||||
|
||||
customSkin = new Skin("CustomSkin");
|
||||
|
||||
foreach (var pair in skinItems) {
|
||||
var attachment = SpineAttachment.GetAttachment(pair.sourceAttachmentPath, skinSource);
|
||||
customSkin.AddAttachment(skeleton.FindSlotIndex(pair.targetSlot), pair.targetAttachment, attachment);
|
||||
/// <summary>The name of the skin placeholder/skin dictionary entry this attachment should be associated with.</summary>
|
||||
/// <remarks>This name is used by the skin dictionary, used in the method Skin.AddAttachment as well as setting a slot attachment</remarks>
|
||||
[SpineAttachment(currentSkinOnly: true, placeholdersOnly: true)]
|
||||
public string targetAttachment;
|
||||
}
|
||||
|
||||
// The custom skin does not need to be added to the skeleton data for it to work.
|
||||
// But it's useful for your script to keep a reference to it.
|
||||
skeleton.SetSkin(customSkin);
|
||||
#region Inspector
|
||||
public SkeletonDataAsset skinSource;
|
||||
|
||||
[UnityEngine.Serialization.FormerlySerializedAs("skinning")]
|
||||
public SkinPair[] skinItems;
|
||||
|
||||
public Skin customSkin;
|
||||
#endregion
|
||||
|
||||
SkeletonRenderer skeletonRenderer;
|
||||
|
||||
void Start () {
|
||||
skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||
Skeleton skeleton = skeletonRenderer.skeleton;
|
||||
|
||||
customSkin = new Skin("CustomSkin");
|
||||
|
||||
foreach (var pair in skinItems) {
|
||||
var attachment = SpineAttachment.GetAttachment(pair.sourceAttachmentPath, skinSource);
|
||||
customSkin.AddAttachment(skeleton.FindSlotIndex(pair.targetSlot), pair.targetAttachment, attachment);
|
||||
}
|
||||
|
||||
// The custom skin does not need to be added to the skeleton data for it to work.
|
||||
// But it's useful for your script to keep a reference to it.
|
||||
skeleton.SetSkin(customSkin);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -4,150 +4,153 @@
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[RequireComponent(typeof(SkeletonRenderer))]
|
||||
public class SkeletonGhost : MonoBehaviour {
|
||||
public bool ghostingEnabled = true;
|
||||
public float spawnRate = 0.05f;
|
||||
public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00); // default for additive.
|
||||
[Tooltip("Remember to set color alpha to 0 if Additive is true")]
|
||||
public bool additive = true;
|
||||
public int maximumGhosts = 10;
|
||||
public float fadeSpeed = 10;
|
||||
public Shader ghostShader;
|
||||
[Tooltip("0 is Color and Alpha, 1 is Alpha only.")]
|
||||
[Range(0, 1)]
|
||||
public float textureFade = 1;
|
||||
namespace Spine.Unity.Modules {
|
||||
|
||||
[RequireComponent(typeof(SkeletonRenderer))]
|
||||
public class SkeletonGhost : MonoBehaviour {
|
||||
public bool ghostingEnabled = true;
|
||||
public float spawnRate = 0.05f;
|
||||
public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00); // default for additive.
|
||||
[Tooltip("Remember to set color alpha to 0 if Additive is true")]
|
||||
public bool additive = true;
|
||||
public int maximumGhosts = 10;
|
||||
public float fadeSpeed = 10;
|
||||
public Shader ghostShader;
|
||||
[Tooltip("0 is Color and Alpha, 1 is Alpha only.")]
|
||||
[Range(0, 1)]
|
||||
public float textureFade = 1;
|
||||
|
||||
[Header("Sorting")]
|
||||
public bool sortWithDistanceOnly;
|
||||
public float zOffset = 0f;
|
||||
[Header("Sorting")]
|
||||
public bool sortWithDistanceOnly;
|
||||
public float zOffset = 0f;
|
||||
|
||||
float nextSpawnTime;
|
||||
SkeletonGhostRenderer[] pool;
|
||||
int poolIndex = 0;
|
||||
SkeletonRenderer skeletonRenderer;
|
||||
MeshRenderer meshRenderer;
|
||||
MeshFilter meshFilter;
|
||||
float nextSpawnTime;
|
||||
SkeletonGhostRenderer[] pool;
|
||||
int poolIndex = 0;
|
||||
SkeletonRenderer skeletonRenderer;
|
||||
MeshRenderer meshRenderer;
|
||||
MeshFilter meshFilter;
|
||||
|
||||
|
||||
Dictionary<Material, Material> materialTable = new Dictionary<Material, Material>();
|
||||
Dictionary<Material, Material> materialTable = new Dictionary<Material, Material>();
|
||||
|
||||
void Start () {
|
||||
if (ghostShader == null)
|
||||
ghostShader = Shader.Find("Spine/SkeletonGhost");
|
||||
void Start () {
|
||||
if (ghostShader == null)
|
||||
ghostShader = Shader.Find("Spine/SkeletonGhost");
|
||||
|
||||
skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||
meshFilter = GetComponent<MeshFilter>();
|
||||
meshRenderer = GetComponent<MeshRenderer>();
|
||||
nextSpawnTime = Time.time + spawnRate;
|
||||
pool = new SkeletonGhostRenderer[maximumGhosts];
|
||||
for (int i = 0; i < maximumGhosts; i++) {
|
||||
GameObject go = new GameObject(gameObject.name + " Ghost", typeof(SkeletonGhostRenderer));
|
||||
pool[i] = go.GetComponent<SkeletonGhostRenderer>();
|
||||
go.SetActive(false);
|
||||
go.hideFlags = HideFlags.HideInHierarchy;
|
||||
}
|
||||
|
||||
if (skeletonRenderer is SkeletonAnimation)
|
||||
((SkeletonAnimation)skeletonRenderer).state.Event += OnEvent;
|
||||
|
||||
skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||
meshFilter = GetComponent<MeshFilter>();
|
||||
meshRenderer = GetComponent<MeshRenderer>();
|
||||
nextSpawnTime = Time.time + spawnRate;
|
||||
pool = new SkeletonGhostRenderer[maximumGhosts];
|
||||
for (int i = 0; i < maximumGhosts; i++) {
|
||||
GameObject go = new GameObject(gameObject.name + " Ghost", typeof(SkeletonGhostRenderer));
|
||||
pool[i] = go.GetComponent<SkeletonGhostRenderer>();
|
||||
go.SetActive(false);
|
||||
go.hideFlags = HideFlags.HideInHierarchy;
|
||||
}
|
||||
|
||||
if (skeletonRenderer is SkeletonAnimation)
|
||||
((SkeletonAnimation)skeletonRenderer).state.Event += OnEvent;
|
||||
|
||||
}
|
||||
|
||||
//SkeletonAnimation
|
||||
/*
|
||||
//SkeletonAnimation
|
||||
/*
|
||||
* Int Value: 0 sets ghostingEnabled to false, 1 sets ghostingEnabled to true
|
||||
* Float Value: Values greater than 0 set the spawnRate equal the float value
|
||||
* String Value: Pass RGBA hex color values in to set the color property. IE: "A0FF8BFF"
|
||||
*/
|
||||
void OnEvent (Spine.AnimationState state, int trackIndex, Spine.Event e) {
|
||||
if (e.Data.Name == "Ghosting") {
|
||||
ghostingEnabled = e.Int > 0;
|
||||
if (e.Float > 0)
|
||||
spawnRate = e.Float;
|
||||
if (e.String != null) {
|
||||
this.color = HexToColor(e.String);
|
||||
void OnEvent (Spine.AnimationState state, int trackIndex, Spine.Event e) {
|
||||
if (e.Data.Name == "Ghosting") {
|
||||
ghostingEnabled = e.Int > 0;
|
||||
if (e.Float > 0)
|
||||
spawnRate = e.Float;
|
||||
if (e.String != null) {
|
||||
this.color = HexToColor(e.String);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//SkeletonAnimator
|
||||
//SkeletonAnimator or Mecanim based animations only support toggling ghostingEnabled. Be sure not to set anything other than the Int param in Spine or String will take priority.
|
||||
void Ghosting (float val) {
|
||||
ghostingEnabled = val > 0;
|
||||
}
|
||||
//SkeletonAnimator
|
||||
//SkeletonAnimator or Mecanim based animations only support toggling ghostingEnabled. Be sure not to set anything other than the Int param in Spine or String will take priority.
|
||||
void Ghosting (float val) {
|
||||
ghostingEnabled = val > 0;
|
||||
}
|
||||
|
||||
void Update () {
|
||||
if (!ghostingEnabled)
|
||||
return;
|
||||
void Update () {
|
||||
if (!ghostingEnabled)
|
||||
return;
|
||||
|
||||
if (Time.time >= nextSpawnTime) {
|
||||
GameObject go = pool[poolIndex].gameObject;
|
||||
if (Time.time >= nextSpawnTime) {
|
||||
GameObject go = pool[poolIndex].gameObject;
|
||||
|
||||
Material[] materials = meshRenderer.sharedMaterials;
|
||||
for (int i = 0; i < materials.Length; i++) {
|
||||
var originalMat = materials[i];
|
||||
Material ghostMat;
|
||||
if (!materialTable.ContainsKey(originalMat)) {
|
||||
ghostMat = new Material(originalMat);
|
||||
ghostMat.shader = ghostShader;
|
||||
ghostMat.color = Color.white;
|
||||
if (ghostMat.HasProperty("_TextureFade"))
|
||||
ghostMat.SetFloat("_TextureFade", textureFade);
|
||||
materialTable.Add(originalMat, ghostMat);
|
||||
} else {
|
||||
ghostMat = materialTable[originalMat];
|
||||
Material[] materials = meshRenderer.sharedMaterials;
|
||||
for (int i = 0; i < materials.Length; i++) {
|
||||
var originalMat = materials[i];
|
||||
Material ghostMat;
|
||||
if (!materialTable.ContainsKey(originalMat)) {
|
||||
ghostMat = new Material(originalMat);
|
||||
ghostMat.shader = ghostShader;
|
||||
ghostMat.color = Color.white;
|
||||
if (ghostMat.HasProperty("_TextureFade"))
|
||||
ghostMat.SetFloat("_TextureFade", textureFade);
|
||||
materialTable.Add(originalMat, ghostMat);
|
||||
} else {
|
||||
ghostMat = materialTable[originalMat];
|
||||
}
|
||||
|
||||
materials[i] = ghostMat;
|
||||
}
|
||||
|
||||
materials[i] = ghostMat;
|
||||
var goTransform = go.transform;
|
||||
goTransform.parent = transform;
|
||||
|
||||
pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingLayerID, (sortWithDistanceOnly) ? meshRenderer.sortingOrder : meshRenderer.sortingOrder - 1);
|
||||
|
||||
goTransform.localPosition = new Vector3(0f, 0f, zOffset);
|
||||
goTransform.localRotation = Quaternion.identity;
|
||||
goTransform.localScale = Vector3.one;
|
||||
|
||||
goTransform.parent = null;
|
||||
|
||||
poolIndex++;
|
||||
|
||||
if (poolIndex == pool.Length)
|
||||
poolIndex = 0;
|
||||
|
||||
nextSpawnTime = Time.time + spawnRate;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
for (int i = 0; i < maximumGhosts; i++) {
|
||||
if (pool[i] != null)
|
||||
pool[i].Cleanup();
|
||||
}
|
||||
|
||||
var goTransform = go.transform;
|
||||
goTransform.parent = transform;
|
||||
foreach (var mat in materialTable.Values)
|
||||
Destroy(mat);
|
||||
}
|
||||
|
||||
pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingLayerID, (sortWithDistanceOnly) ? meshRenderer.sortingOrder : meshRenderer.sortingOrder - 1);
|
||||
|
||||
goTransform.localPosition = new Vector3(0f, 0f, zOffset);
|
||||
goTransform.localRotation = Quaternion.identity;
|
||||
goTransform.localScale = Vector3.one;
|
||||
//based on UnifyWiki http://wiki.unity3d.com/index.php?title=HexConverter
|
||||
static Color32 HexToColor (string hex) {
|
||||
if (hex.Length < 6)
|
||||
return Color.magenta;
|
||||
|
||||
goTransform.parent = null;
|
||||
hex = hex.Replace("#", "");
|
||||
byte r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
byte g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
byte b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
byte a = 0xFF;
|
||||
if (hex.Length == 8)
|
||||
a = byte.Parse(hex.Substring(6, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
|
||||
poolIndex++;
|
||||
|
||||
if (poolIndex == pool.Length)
|
||||
poolIndex = 0;
|
||||
|
||||
nextSpawnTime = Time.time + spawnRate;
|
||||
return new Color32(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
for (int i = 0; i < maximumGhosts; i++) {
|
||||
if (pool[i] != null)
|
||||
pool[i].Cleanup();
|
||||
}
|
||||
|
||||
foreach (var mat in materialTable.Values)
|
||||
Destroy(mat);
|
||||
}
|
||||
|
||||
|
||||
//based on UnifyWiki http://wiki.unity3d.com/index.php?title=HexConverter
|
||||
static Color32 HexToColor (string hex) {
|
||||
if (hex.Length < 6)
|
||||
return Color.magenta;
|
||||
|
||||
hex = hex.Replace("#", "");
|
||||
byte r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
byte g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
byte b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
byte a = 0xFF;
|
||||
if (hex.Length == 8)
|
||||
a = byte.Parse(hex.Substring(6, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
|
||||
return new Color32(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,48 +5,49 @@
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[CustomEditor(typeof(SkeletonRagdoll2D))]
|
||||
public class SkeletonRagdoll2DInspector : Editor {
|
||||
SerializedProperty startingBoneName, stopBoneNames, applyOnStart, pinStartBone, enableJointCollision, gravityScale, disableIK, thickness, rotationLimit, colliderLayer, mix, rootMass, massFalloffFactor;
|
||||
namespace Spine.Unity.Modules {
|
||||
[CustomEditor(typeof(SkeletonRagdoll2D))]
|
||||
public class SkeletonRagdoll2DInspector : UnityEditor.Editor {
|
||||
SerializedProperty startingBoneName, stopBoneNames, applyOnStart, pinStartBone, enableJointCollision, gravityScale, disableIK, thickness, rotationLimit, colliderLayer, mix, rootMass, massFalloffFactor;
|
||||
|
||||
void OnEnable () {
|
||||
startingBoneName = serializedObject.FindProperty("startingBoneName");
|
||||
stopBoneNames = serializedObject.FindProperty("stopBoneNames");
|
||||
applyOnStart = serializedObject.FindProperty("applyOnStart");
|
||||
pinStartBone = serializedObject.FindProperty("pinStartBone");
|
||||
gravityScale = serializedObject.FindProperty("gravityScale");
|
||||
disableIK = serializedObject.FindProperty("disableIK");
|
||||
thickness = serializedObject.FindProperty("thickness");
|
||||
rotationLimit = serializedObject.FindProperty("rotationLimit");
|
||||
colliderLayer = serializedObject.FindProperty("colliderLayer");
|
||||
mix = serializedObject.FindProperty("mix");
|
||||
rootMass = serializedObject.FindProperty("rootMass");
|
||||
massFalloffFactor = serializedObject.FindProperty("massFalloffFactor");
|
||||
void OnEnable () {
|
||||
startingBoneName = serializedObject.FindProperty("startingBoneName");
|
||||
stopBoneNames = serializedObject.FindProperty("stopBoneNames");
|
||||
applyOnStart = serializedObject.FindProperty("applyOnStart");
|
||||
pinStartBone = serializedObject.FindProperty("pinStartBone");
|
||||
gravityScale = serializedObject.FindProperty("gravityScale");
|
||||
disableIK = serializedObject.FindProperty("disableIK");
|
||||
thickness = serializedObject.FindProperty("thickness");
|
||||
rotationLimit = serializedObject.FindProperty("rotationLimit");
|
||||
colliderLayer = serializedObject.FindProperty("colliderLayer");
|
||||
mix = serializedObject.FindProperty("mix");
|
||||
rootMass = serializedObject.FindProperty("rootMass");
|
||||
massFalloffFactor = serializedObject.FindProperty("massFalloffFactor");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
EditorGUILayout.PropertyField(startingBoneName);
|
||||
EditorGUILayout.PropertyField(stopBoneNames, true);
|
||||
EditorGUILayout.PropertyField(applyOnStart);
|
||||
EditorGUILayout.PropertyField(pinStartBone);
|
||||
EditorGUILayout.PropertyField(gravityScale);
|
||||
EditorGUILayout.PropertyField(disableIK);
|
||||
EditorGUILayout.PropertyField(thickness);
|
||||
EditorGUILayout.PropertyField(rotationLimit);
|
||||
EditorGUILayout.PropertyField(rootMass);
|
||||
EditorGUILayout.PropertyField(massFalloffFactor);
|
||||
colliderLayer.intValue = EditorGUILayout.LayerField(colliderLayer.displayName, colliderLayer.intValue);
|
||||
EditorGUILayout.PropertyField(mix);
|
||||
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
void Header (string name) {
|
||||
GUILayout.Space(20);
|
||||
EditorGUILayout.LabelField(name, EditorStyles.boldLabel);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
EditorGUILayout.PropertyField(startingBoneName);
|
||||
EditorGUILayout.PropertyField(stopBoneNames, true);
|
||||
EditorGUILayout.PropertyField(applyOnStart);
|
||||
EditorGUILayout.PropertyField(pinStartBone);
|
||||
EditorGUILayout.PropertyField(gravityScale);
|
||||
EditorGUILayout.PropertyField(disableIK);
|
||||
EditorGUILayout.PropertyField(thickness);
|
||||
EditorGUILayout.PropertyField(rotationLimit);
|
||||
EditorGUILayout.PropertyField(rootMass);
|
||||
EditorGUILayout.PropertyField(massFalloffFactor);
|
||||
colliderLayer.intValue = EditorGUILayout.LayerField(colliderLayer.displayName, colliderLayer.intValue);
|
||||
EditorGUILayout.PropertyField(mix);
|
||||
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
void Header (string name) {
|
||||
GUILayout.Space(20);
|
||||
EditorGUILayout.LabelField(name, EditorStyles.boldLabel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,46 +8,49 @@ using UnityEditor;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[CustomEditor(typeof(SkeletonRagdoll))]
|
||||
public class SkeletonRagdollInspector : Editor {
|
||||
SerializedProperty startingBoneName, stopBoneNames, applyOnStart, pinStartBone, enableJointCollision, useGravity, disableIK, thickness, rotationLimit, colliderLayer, mix, rootMass, massFalloffFactor;
|
||||
namespace Spine.Unity.Modules {
|
||||
[CustomEditor(typeof(SkeletonRagdoll))]
|
||||
public class SkeletonRagdollInspector : UnityEditor.Editor {
|
||||
SerializedProperty startingBoneName, stopBoneNames, applyOnStart, pinStartBone, enableJointCollision, useGravity, disableIK, thickness, rotationLimit, colliderLayer, mix, rootMass, massFalloffFactor;
|
||||
|
||||
void OnEnable () {
|
||||
startingBoneName = serializedObject.FindProperty("startingBoneName");
|
||||
stopBoneNames = serializedObject.FindProperty("stopBoneNames");
|
||||
applyOnStart = serializedObject.FindProperty("applyOnStart");
|
||||
pinStartBone = serializedObject.FindProperty("pinStartBone");
|
||||
enableJointCollision = serializedObject.FindProperty("enableJointCollision");
|
||||
useGravity = serializedObject.FindProperty("useGravity");
|
||||
disableIK = serializedObject.FindProperty("disableIK");
|
||||
thickness = serializedObject.FindProperty("thickness");
|
||||
rotationLimit = serializedObject.FindProperty("rotationLimit");
|
||||
colliderLayer = serializedObject.FindProperty("colliderLayer");
|
||||
mix = serializedObject.FindProperty("mix");
|
||||
rootMass = serializedObject.FindProperty("rootMass");
|
||||
massFalloffFactor = serializedObject.FindProperty("massFalloffFactor");
|
||||
void OnEnable () {
|
||||
startingBoneName = serializedObject.FindProperty("startingBoneName");
|
||||
stopBoneNames = serializedObject.FindProperty("stopBoneNames");
|
||||
applyOnStart = serializedObject.FindProperty("applyOnStart");
|
||||
pinStartBone = serializedObject.FindProperty("pinStartBone");
|
||||
enableJointCollision = serializedObject.FindProperty("enableJointCollision");
|
||||
useGravity = serializedObject.FindProperty("useGravity");
|
||||
disableIK = serializedObject.FindProperty("disableIK");
|
||||
thickness = serializedObject.FindProperty("thickness");
|
||||
rotationLimit = serializedObject.FindProperty("rotationLimit");
|
||||
colliderLayer = serializedObject.FindProperty("colliderLayer");
|
||||
mix = serializedObject.FindProperty("mix");
|
||||
rootMass = serializedObject.FindProperty("rootMass");
|
||||
massFalloffFactor = serializedObject.FindProperty("massFalloffFactor");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
EditorGUILayout.PropertyField(startingBoneName);
|
||||
EditorGUILayout.PropertyField(stopBoneNames, true);
|
||||
EditorGUILayout.PropertyField(applyOnStart);
|
||||
EditorGUILayout.PropertyField(pinStartBone);
|
||||
EditorGUILayout.PropertyField(enableJointCollision);
|
||||
EditorGUILayout.PropertyField(useGravity);
|
||||
EditorGUILayout.PropertyField(disableIK);
|
||||
EditorGUILayout.PropertyField(thickness);
|
||||
EditorGUILayout.PropertyField(rotationLimit);
|
||||
EditorGUILayout.PropertyField(rootMass);
|
||||
EditorGUILayout.PropertyField(massFalloffFactor);
|
||||
colliderLayer.intValue = EditorGUILayout.LayerField(colliderLayer.displayName, colliderLayer.intValue);
|
||||
EditorGUILayout.PropertyField(mix);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
void Header (string name) {
|
||||
GUILayout.Space(20);
|
||||
EditorGUILayout.LabelField(name, EditorStyles.boldLabel);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
EditorGUILayout.PropertyField(startingBoneName);
|
||||
EditorGUILayout.PropertyField(stopBoneNames, true);
|
||||
EditorGUILayout.PropertyField(applyOnStart);
|
||||
EditorGUILayout.PropertyField(pinStartBone);
|
||||
EditorGUILayout.PropertyField(enableJointCollision);
|
||||
EditorGUILayout.PropertyField(useGravity);
|
||||
EditorGUILayout.PropertyField(disableIK);
|
||||
EditorGUILayout.PropertyField(thickness);
|
||||
EditorGUILayout.PropertyField(rotationLimit);
|
||||
EditorGUILayout.PropertyField(rootMass);
|
||||
EditorGUILayout.PropertyField(massFalloffFactor);
|
||||
colliderLayer.intValue = EditorGUILayout.LayerField(colliderLayer.displayName, colliderLayer.intValue);
|
||||
EditorGUILayout.PropertyField(mix);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
void Header (string name) {
|
||||
GUILayout.Space(20);
|
||||
EditorGUILayout.LabelField(name, EditorStyles.boldLabel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,421 +6,423 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Spine;
|
||||
|
||||
[RequireComponent(typeof(SkeletonRenderer))]
|
||||
public class SkeletonRagdoll : MonoBehaviour {
|
||||
private static Transform helper;
|
||||
namespace Spine.Unity.Modules {
|
||||
[RequireComponent(typeof(SkeletonRenderer))]
|
||||
public class SkeletonRagdoll : MonoBehaviour {
|
||||
private static Transform helper;
|
||||
|
||||
[Header("Hierarchy")]
|
||||
[SpineBone]
|
||||
public string startingBoneName = "";
|
||||
[SpineBone]
|
||||
public List<string> stopBoneNames = new List<string>();
|
||||
[Header("Hierarchy")]
|
||||
[SpineBone]
|
||||
public string startingBoneName = "";
|
||||
[SpineBone]
|
||||
public List<string> stopBoneNames = new List<string>();
|
||||
|
||||
[Header("Parameters")]
|
||||
public bool applyOnStart;
|
||||
[Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")]
|
||||
public bool pinStartBone;
|
||||
[Tooltip("Enable Collision between adjacent ragdoll elements (IE: Neck and Head)")]
|
||||
public bool enableJointCollision;
|
||||
public bool useGravity = true;
|
||||
[Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")]
|
||||
public bool disableIK = true;
|
||||
[Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")]
|
||||
public float thickness = 0.125f;
|
||||
[Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")]
|
||||
public float rotationLimit = 20;
|
||||
public float rootMass = 20;
|
||||
[Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")]
|
||||
[Range(0.01f, 1f)]
|
||||
public float massFalloffFactor = 0.4f;
|
||||
[Tooltip("The layer assigned to all of the rigidbody parts.")]
|
||||
public int colliderLayer = 0;
|
||||
[Range(0, 1)]
|
||||
public float mix = 1;
|
||||
[Header("Parameters")]
|
||||
public bool applyOnStart;
|
||||
[Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")]
|
||||
public bool pinStartBone;
|
||||
[Tooltip("Enable Collision between adjacent ragdoll elements (IE: Neck and Head)")]
|
||||
public bool enableJointCollision;
|
||||
public bool useGravity = true;
|
||||
[Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")]
|
||||
public bool disableIK = true;
|
||||
[Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")]
|
||||
public float thickness = 0.125f;
|
||||
[Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")]
|
||||
public float rotationLimit = 20;
|
||||
public float rootMass = 20;
|
||||
[Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")]
|
||||
[Range(0.01f, 1f)]
|
||||
public float massFalloffFactor = 0.4f;
|
||||
[Tooltip("The layer assigned to all of the rigidbody parts.")]
|
||||
public int colliderLayer = 0;
|
||||
[Range(0, 1)]
|
||||
public float mix = 1;
|
||||
|
||||
public Rigidbody RootRigidbody {
|
||||
get {
|
||||
return this.rootRigidbody;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 RootOffset {
|
||||
get {
|
||||
return this.rootOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 EstimatedSkeletonPosition {
|
||||
get {
|
||||
return rootRigidbody.position - rootOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsActive {
|
||||
get {
|
||||
return this.isActive;
|
||||
}
|
||||
}
|
||||
|
||||
private Rigidbody rootRigidbody;
|
||||
private ISkeletonAnimation skeletonAnim;
|
||||
private Skeleton skeleton;
|
||||
private Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
|
||||
private Bone startingBone;
|
||||
private Transform ragdollRoot;
|
||||
private Vector3 rootOffset;
|
||||
private bool isActive;
|
||||
|
||||
IEnumerator Start () {
|
||||
skeletonAnim = (ISkeletonAnimation)GetComponent<SkeletonRenderer>();
|
||||
if (helper == null) {
|
||||
helper = (Transform)(new GameObject("Helper")).transform;
|
||||
helper.hideFlags = HideFlags.HideInHierarchy;
|
||||
public Rigidbody RootRigidbody {
|
||||
get {
|
||||
return this.rootRigidbody;
|
||||
}
|
||||
}
|
||||
|
||||
if (applyOnStart) {
|
||||
yield return null;
|
||||
Apply();
|
||||
}
|
||||
}
|
||||
|
||||
public Coroutine SmoothMix (float target, float duration) {
|
||||
return StartCoroutine(SmoothMixCoroutine(target, duration));
|
||||
}
|
||||
|
||||
IEnumerator SmoothMixCoroutine (float target, float duration) {
|
||||
float startTime = Time.time;
|
||||
float startMix = mix;
|
||||
while (mix > 0) {
|
||||
mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSkeletonPosition (Vector3 worldPosition) {
|
||||
if (!isActive) {
|
||||
Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!");
|
||||
return;
|
||||
public Vector3 RootOffset {
|
||||
get {
|
||||
return this.rootOffset;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 offset = worldPosition - transform.position;
|
||||
transform.position = worldPosition;
|
||||
foreach (Transform t in boneTable.Values) {
|
||||
t.position -= offset;
|
||||
public Vector3 EstimatedSkeletonPosition {
|
||||
get {
|
||||
return rootRigidbody.position - rootOffset;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateWorld(null);
|
||||
skeleton.UpdateWorldTransform();
|
||||
}
|
||||
|
||||
public Rigidbody[] GetRigidbodyArray () {
|
||||
if (!isActive)
|
||||
return new Rigidbody[0];
|
||||
|
||||
Rigidbody[] arr = new Rigidbody[boneTable.Count];
|
||||
int i = 0;
|
||||
foreach (Transform t in boneTable.Values) {
|
||||
arr[i] = t.GetComponent<Rigidbody>();
|
||||
i++;
|
||||
public bool IsActive {
|
||||
get {
|
||||
return this.isActive;
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
private Rigidbody rootRigidbody;
|
||||
private ISkeletonAnimation skeletonAnim;
|
||||
private Skeleton skeleton;
|
||||
private Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
|
||||
private Bone startingBone;
|
||||
private Transform ragdollRoot;
|
||||
private Vector3 rootOffset;
|
||||
private bool isActive;
|
||||
|
||||
IEnumerator Start () {
|
||||
skeletonAnim = (ISkeletonAnimation)GetComponent<SkeletonRenderer>();
|
||||
if (helper == null) {
|
||||
helper = (Transform)(new GameObject("Helper")).transform;
|
||||
helper.hideFlags = HideFlags.HideInHierarchy;
|
||||
}
|
||||
|
||||
if (applyOnStart) {
|
||||
yield return null;
|
||||
Apply();
|
||||
}
|
||||
}
|
||||
|
||||
public Coroutine SmoothMix (float target, float duration) {
|
||||
return StartCoroutine(SmoothMixCoroutine(target, duration));
|
||||
}
|
||||
|
||||
IEnumerator SmoothMixCoroutine (float target, float duration) {
|
||||
float startTime = Time.time;
|
||||
float startMix = mix;
|
||||
while (mix > 0) {
|
||||
mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSkeletonPosition (Vector3 worldPosition) {
|
||||
if (!isActive) {
|
||||
Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 offset = worldPosition - transform.position;
|
||||
transform.position = worldPosition;
|
||||
foreach (Transform t in boneTable.Values) {
|
||||
t.position -= offset;
|
||||
}
|
||||
|
||||
UpdateWorld(null);
|
||||
skeleton.UpdateWorldTransform();
|
||||
}
|
||||
|
||||
public Rigidbody[] GetRigidbodyArray () {
|
||||
if (!isActive)
|
||||
return new Rigidbody[0];
|
||||
|
||||
Rigidbody[] arr = new Rigidbody[boneTable.Count];
|
||||
int i = 0;
|
||||
foreach (Transform t in boneTable.Values) {
|
||||
arr[i] = t.GetComponent<Rigidbody>();
|
||||
i++;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
public Rigidbody GetRigidbody (string boneName) {
|
||||
var bone = skeleton.FindBone(boneName);
|
||||
if (bone == null)
|
||||
return null;
|
||||
|
||||
if (boneTable.ContainsKey(bone))
|
||||
return boneTable[bone].GetComponent<Rigidbody>();
|
||||
|
||||
public Rigidbody GetRigidbody (string boneName) {
|
||||
var bone = skeleton.FindBone(boneName);
|
||||
if (bone == null)
|
||||
return null;
|
||||
|
||||
if (boneTable.ContainsKey(bone))
|
||||
return boneTable[bone].GetComponent<Rigidbody>();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Remove () {
|
||||
isActive = false;
|
||||
foreach (var t in boneTable.Values) {
|
||||
Destroy(t.gameObject);
|
||||
}
|
||||
Destroy(ragdollRoot.gameObject);
|
||||
|
||||
boneTable.Clear();
|
||||
skeletonAnim.UpdateWorld -= UpdateWorld;
|
||||
}
|
||||
public void Remove () {
|
||||
isActive = false;
|
||||
foreach (var t in boneTable.Values) {
|
||||
Destroy(t.gameObject);
|
||||
}
|
||||
Destroy(ragdollRoot.gameObject);
|
||||
|
||||
public void Apply () {
|
||||
isActive = true;
|
||||
skeleton = skeletonAnim.Skeleton;
|
||||
mix = 1;
|
||||
boneTable.Clear();
|
||||
skeletonAnim.UpdateWorld -= UpdateWorld;
|
||||
}
|
||||
|
||||
var ragdollRootBone = skeleton.FindBone(startingBoneName);
|
||||
startingBone = ragdollRootBone;
|
||||
RecursivelyCreateBoneProxies(ragdollRootBone);
|
||||
public void Apply () {
|
||||
isActive = true;
|
||||
skeleton = skeletonAnim.Skeleton;
|
||||
mix = 1;
|
||||
|
||||
rootRigidbody = boneTable[ragdollRootBone].GetComponent<Rigidbody>();
|
||||
rootRigidbody.isKinematic = pinStartBone;
|
||||
var ragdollRootBone = skeleton.FindBone(startingBoneName);
|
||||
startingBone = ragdollRootBone;
|
||||
RecursivelyCreateBoneProxies(ragdollRootBone);
|
||||
|
||||
rootRigidbody.mass = rootMass;
|
||||
rootRigidbody = boneTable[ragdollRootBone].GetComponent<Rigidbody>();
|
||||
rootRigidbody.isKinematic = pinStartBone;
|
||||
|
||||
List<Collider> boneColliders = new List<Collider>();
|
||||
rootRigidbody.mass = rootMass;
|
||||
|
||||
foreach (var pair in boneTable) {
|
||||
var b = pair.Key;
|
||||
var t = pair.Value;
|
||||
Bone parentBone = null;
|
||||
Transform parentTransform = transform;
|
||||
List<Collider> boneColliders = new List<Collider>();
|
||||
|
||||
boneColliders.Add(t.GetComponent<Collider>());
|
||||
foreach (var pair in boneTable) {
|
||||
var b = pair.Key;
|
||||
var t = pair.Value;
|
||||
Bone parentBone = null;
|
||||
Transform parentTransform = transform;
|
||||
|
||||
if (b != startingBone) {
|
||||
parentBone = b.Parent;
|
||||
parentTransform = boneTable[parentBone];
|
||||
} else {
|
||||
ragdollRoot = new GameObject("RagdollRoot").transform;
|
||||
ragdollRoot.parent = transform;
|
||||
boneColliders.Add(t.GetComponent<Collider>());
|
||||
|
||||
if (b == skeleton.RootBone) {
|
||||
ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
|
||||
ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b));
|
||||
parentTransform = ragdollRoot;
|
||||
if (b != startingBone) {
|
||||
parentBone = b.Parent;
|
||||
parentTransform = boneTable[parentBone];
|
||||
} else {
|
||||
ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
|
||||
ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b.Parent));
|
||||
parentTransform = ragdollRoot;
|
||||
ragdollRoot = new GameObject("RagdollRoot").transform;
|
||||
ragdollRoot.parent = transform;
|
||||
|
||||
if (b == skeleton.RootBone) {
|
||||
ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
|
||||
ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b));
|
||||
parentTransform = ragdollRoot;
|
||||
} else {
|
||||
ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
|
||||
ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b.Parent));
|
||||
parentTransform = ragdollRoot;
|
||||
}
|
||||
|
||||
rootOffset = t.position - transform.position;
|
||||
}
|
||||
|
||||
rootOffset = t.position - transform.position;
|
||||
}
|
||||
var rbParent = parentTransform.GetComponent<Rigidbody>();
|
||||
|
||||
var rbParent = parentTransform.GetComponent<Rigidbody>();
|
||||
|
||||
if (rbParent != null) {
|
||||
var joint = t.gameObject.AddComponent<HingeJoint>();
|
||||
joint.connectedBody = rbParent;
|
||||
Vector3 localPos = parentTransform.InverseTransformPoint(t.position);
|
||||
localPos.x *= 1;
|
||||
joint.connectedAnchor = localPos;
|
||||
joint.axis = Vector3.forward;
|
||||
joint.GetComponent<Rigidbody>().mass = joint.connectedBody.mass * massFalloffFactor;
|
||||
JointLimits limits = new JointLimits();
|
||||
limits.min = -rotationLimit;
|
||||
limits.max = rotationLimit;
|
||||
joint.limits = limits;
|
||||
joint.useLimits = true;
|
||||
joint.enableCollision = enableJointCollision;
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 0; x < boneColliders.Count; x++) {
|
||||
for (int y = 0; y < boneColliders.Count; y++) {
|
||||
if (x == y) continue;
|
||||
Physics.IgnoreCollision(boneColliders[x], boneColliders[y]);
|
||||
}
|
||||
}
|
||||
|
||||
var utilityBones = GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
if (utilityBones.Length > 0) {
|
||||
List<string> destroyedUtilityBoneNames = new List<string>();
|
||||
foreach (var ub in utilityBones) {
|
||||
if (ub.mode == SkeletonUtilityBone.Mode.Override) {
|
||||
destroyedUtilityBoneNames.Add(ub.gameObject.name);
|
||||
Destroy(ub.gameObject);
|
||||
if (rbParent != null) {
|
||||
var joint = t.gameObject.AddComponent<HingeJoint>();
|
||||
joint.connectedBody = rbParent;
|
||||
Vector3 localPos = parentTransform.InverseTransformPoint(t.position);
|
||||
localPos.x *= 1;
|
||||
joint.connectedAnchor = localPos;
|
||||
joint.axis = Vector3.forward;
|
||||
joint.GetComponent<Rigidbody>().mass = joint.connectedBody.mass * massFalloffFactor;
|
||||
JointLimits limits = new JointLimits();
|
||||
limits.min = -rotationLimit;
|
||||
limits.max = rotationLimit;
|
||||
joint.limits = limits;
|
||||
joint.useLimits = true;
|
||||
joint.enableCollision = enableJointCollision;
|
||||
}
|
||||
}
|
||||
|
||||
if (destroyedUtilityBoneNames.Count > 0) {
|
||||
string msg = "Destroyed Utility Bones: ";
|
||||
for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) {
|
||||
msg += destroyedUtilityBoneNames[i];
|
||||
if (i != destroyedUtilityBoneNames.Count - 1) {
|
||||
msg += ",";
|
||||
for (int x = 0; x < boneColliders.Count; x++) {
|
||||
for (int y = 0; y < boneColliders.Count; y++) {
|
||||
if (x == y) continue;
|
||||
Physics.IgnoreCollision(boneColliders[x], boneColliders[y]);
|
||||
}
|
||||
}
|
||||
|
||||
var utilityBones = GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
if (utilityBones.Length > 0) {
|
||||
List<string> destroyedUtilityBoneNames = new List<string>();
|
||||
foreach (var ub in utilityBones) {
|
||||
if (ub.mode == SkeletonUtilityBone.Mode.Override) {
|
||||
destroyedUtilityBoneNames.Add(ub.gameObject.name);
|
||||
Destroy(ub.gameObject);
|
||||
}
|
||||
}
|
||||
Debug.LogWarning(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (disableIK) {
|
||||
foreach (IkConstraint ik in skeleton.IkConstraints) {
|
||||
ik.Mix = 0;
|
||||
}
|
||||
}
|
||||
|
||||
skeletonAnim.UpdateWorld += UpdateWorld;
|
||||
}
|
||||
|
||||
void RecursivelyCreateBoneProxies (Bone b) {
|
||||
if (stopBoneNames.Contains(b.Data.Name))
|
||||
return;
|
||||
|
||||
GameObject go = new GameObject(b.Data.Name);
|
||||
go.layer = colliderLayer;
|
||||
Transform t = go.transform;
|
||||
boneTable.Add(b, t);
|
||||
|
||||
t.parent = transform;
|
||||
|
||||
t.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
|
||||
// MITCH
|
||||
// t.localRotation = Quaternion.Euler(0, 0, b.WorldFlipX ^ b.WorldFlipY ? -b.WorldRotation : b.WorldRotation);
|
||||
t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX);
|
||||
t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 1);
|
||||
|
||||
float length = b.Data.Length;
|
||||
|
||||
var colliders = AttachBoundingBoxRagdollColliders(b);
|
||||
|
||||
if (length == 0) {
|
||||
//physics
|
||||
if (colliders.Count == 0) {
|
||||
var ball = go.AddComponent<SphereCollider>();
|
||||
ball.radius = thickness / 2f;
|
||||
}
|
||||
} else {
|
||||
//physics
|
||||
if (colliders.Count == 0) {
|
||||
var box = go.AddComponent<BoxCollider>();
|
||||
box.size = new Vector3(length, thickness, thickness);
|
||||
// MITCH
|
||||
// box.center = new Vector3((b.WorldFlipX ? -length : length) / 2, 0);
|
||||
box.center = new Vector3(length / 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
var rb = go.AddComponent<Rigidbody>();
|
||||
rb.constraints = RigidbodyConstraints.FreezePositionZ;
|
||||
foreach (Bone child in b.Children) {
|
||||
RecursivelyCreateBoneProxies(child);
|
||||
}
|
||||
}
|
||||
|
||||
List<Collider> AttachBoundingBoxRagdollColliders (Bone b) {
|
||||
List<Collider> colliders = new List<Collider>();
|
||||
|
||||
Transform t = boneTable[b];
|
||||
GameObject go = t.gameObject;
|
||||
var skin = skeleton.Skin;
|
||||
if (skin == null)
|
||||
skin = skeleton.Data.DefaultSkin;
|
||||
|
||||
// MITCH
|
||||
// bool flipX = b.WorldFlipX;
|
||||
// bool flipY = b.WorldFlipY;
|
||||
bool flipX = false;
|
||||
bool flipY = false;
|
||||
|
||||
List<Attachment> attachments = new List<Attachment>();
|
||||
foreach (Slot s in skeleton.Slots) {
|
||||
if (s.Bone == b) {
|
||||
skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments);
|
||||
foreach (var a in attachments) {
|
||||
if (a is BoundingBoxAttachment) {
|
||||
if (!a.Name.ToLower().Contains("ragdoll"))
|
||||
continue;
|
||||
|
||||
var collider = go.AddComponent<BoxCollider>();
|
||||
var bounds = SkeletonUtility.GetBoundingBoxBounds((BoundingBoxAttachment)a, thickness);
|
||||
|
||||
collider.center = bounds.center;
|
||||
collider.size = bounds.size;
|
||||
|
||||
if (flipX || flipY) {
|
||||
Vector3 center = collider.center;
|
||||
|
||||
if (flipX)
|
||||
center.x *= -1;
|
||||
|
||||
if (flipY)
|
||||
center.y *= -1;
|
||||
|
||||
collider.center = center;
|
||||
if (destroyedUtilityBoneNames.Count > 0) {
|
||||
string msg = "Destroyed Utility Bones: ";
|
||||
for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) {
|
||||
msg += destroyedUtilityBoneNames[i];
|
||||
if (i != destroyedUtilityBoneNames.Count - 1) {
|
||||
msg += ",";
|
||||
}
|
||||
}
|
||||
Debug.LogWarning(msg);
|
||||
}
|
||||
}
|
||||
|
||||
colliders.Add(collider);
|
||||
if (disableIK) {
|
||||
foreach (IkConstraint ik in skeleton.IkConstraints) {
|
||||
ik.Mix = 0;
|
||||
}
|
||||
}
|
||||
|
||||
skeletonAnim.UpdateWorld += UpdateWorld;
|
||||
}
|
||||
|
||||
void RecursivelyCreateBoneProxies (Bone b) {
|
||||
if (stopBoneNames.Contains(b.Data.Name))
|
||||
return;
|
||||
|
||||
GameObject go = new GameObject(b.Data.Name);
|
||||
go.layer = colliderLayer;
|
||||
Transform t = go.transform;
|
||||
boneTable.Add(b, t);
|
||||
|
||||
t.parent = transform;
|
||||
|
||||
t.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
|
||||
// MITCH
|
||||
// t.localRotation = Quaternion.Euler(0, 0, b.WorldFlipX ^ b.WorldFlipY ? -b.WorldRotation : b.WorldRotation);
|
||||
t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX);
|
||||
t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 1);
|
||||
|
||||
float length = b.Data.Length;
|
||||
|
||||
var colliders = AttachBoundingBoxRagdollColliders(b);
|
||||
|
||||
if (length == 0) {
|
||||
//physics
|
||||
if (colliders.Count == 0) {
|
||||
var ball = go.AddComponent<SphereCollider>();
|
||||
ball.radius = thickness / 2f;
|
||||
}
|
||||
} else {
|
||||
//physics
|
||||
if (colliders.Count == 0) {
|
||||
var box = go.AddComponent<BoxCollider>();
|
||||
box.size = new Vector3(length, thickness, thickness);
|
||||
// MITCH
|
||||
// box.center = new Vector3((b.WorldFlipX ? -length : length) / 2, 0);
|
||||
box.center = new Vector3(length / 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
var rb = go.AddComponent<Rigidbody>();
|
||||
rb.constraints = RigidbodyConstraints.FreezePositionZ;
|
||||
foreach (Bone child in b.Children) {
|
||||
RecursivelyCreateBoneProxies(child);
|
||||
}
|
||||
}
|
||||
|
||||
List<Collider> AttachBoundingBoxRagdollColliders (Bone b) {
|
||||
List<Collider> colliders = new List<Collider>();
|
||||
|
||||
Transform t = boneTable[b];
|
||||
GameObject go = t.gameObject;
|
||||
var skin = skeleton.Skin;
|
||||
if (skin == null)
|
||||
skin = skeleton.Data.DefaultSkin;
|
||||
|
||||
// MITCH
|
||||
// bool flipX = b.WorldFlipX;
|
||||
// bool flipY = b.WorldFlipY;
|
||||
bool flipX = false;
|
||||
bool flipY = false;
|
||||
|
||||
List<Attachment> attachments = new List<Attachment>();
|
||||
foreach (Slot s in skeleton.Slots) {
|
||||
if (s.Bone == b) {
|
||||
skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments);
|
||||
foreach (var a in attachments) {
|
||||
if (a is BoundingBoxAttachment) {
|
||||
if (!a.Name.ToLower().Contains("ragdoll"))
|
||||
continue;
|
||||
|
||||
var collider = go.AddComponent<BoxCollider>();
|
||||
var bounds = SkeletonUtility.GetBoundingBoxBounds((BoundingBoxAttachment)a, thickness);
|
||||
|
||||
collider.center = bounds.center;
|
||||
collider.size = bounds.size;
|
||||
|
||||
if (flipX || flipY) {
|
||||
Vector3 center = collider.center;
|
||||
|
||||
if (flipX)
|
||||
center.x *= -1;
|
||||
|
||||
if (flipY)
|
||||
center.y *= -1;
|
||||
|
||||
collider.center = center;
|
||||
}
|
||||
|
||||
colliders.Add(collider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return colliders;
|
||||
}
|
||||
|
||||
return colliders;
|
||||
}
|
||||
void UpdateWorld (ISkeletonAnimation skeletonRenderer) {
|
||||
foreach (var pair in boneTable) {
|
||||
var b = pair.Key;
|
||||
var t = pair.Value;
|
||||
// bool flip = false;
|
||||
bool flipX = false; //TODO: deal with negative scale instead of Flip Key for Spine 3.0
|
||||
bool flipY = false; //TODO: deal with negative scale instead of Flip Key for Spine 3.0
|
||||
Bone parentBone = null;
|
||||
Transform parentTransform = transform;
|
||||
|
||||
void UpdateWorld (ISkeletonAnimation skeletonRenderer) {
|
||||
foreach (var pair in boneTable) {
|
||||
var b = pair.Key;
|
||||
var t = pair.Value;
|
||||
// bool flip = false;
|
||||
bool flipX = false; //TODO: deal with negative scale instead of Flip Key for Spine 3.0
|
||||
bool flipY = false; //TODO: deal with negative scale instead of Flip Key for Spine 3.0
|
||||
Bone parentBone = null;
|
||||
Transform parentTransform = transform;
|
||||
if (b != startingBone) {
|
||||
parentBone = b.Parent;
|
||||
parentTransform = boneTable[parentBone];
|
||||
// MITCH
|
||||
// flipX = parentBone.WorldFlipX;
|
||||
// flipY = parentBone.WorldFlipY;
|
||||
|
||||
if (b != startingBone) {
|
||||
parentBone = b.Parent;
|
||||
parentTransform = boneTable[parentBone];
|
||||
// MITCH
|
||||
// flipX = parentBone.WorldFlipX;
|
||||
// flipY = parentBone.WorldFlipY;
|
||||
|
||||
} else {
|
||||
parentBone = b.Parent;
|
||||
parentTransform = ragdollRoot;
|
||||
if (b.Parent != null) {
|
||||
// MITCH
|
||||
// flipX = b.worldFlipX;
|
||||
// flipY = b.WorldFlipY;
|
||||
} else {
|
||||
flipX = b.Skeleton.FlipX;
|
||||
flipY = b.Skeleton.FlipY;
|
||||
} else {
|
||||
parentBone = b.Parent;
|
||||
parentTransform = ragdollRoot;
|
||||
if (b.Parent != null) {
|
||||
// MITCH
|
||||
// flipX = b.worldFlipX;
|
||||
// flipY = b.WorldFlipY;
|
||||
} else {
|
||||
flipX = b.Skeleton.FlipX;
|
||||
flipY = b.Skeleton.FlipY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//flip = flipX ^ flipY;
|
||||
//flip = flipX ^ flipY;
|
||||
|
||||
helper.position = parentTransform.position;
|
||||
helper.rotation = parentTransform.rotation;
|
||||
helper.localScale = new Vector3(flipX ? -parentTransform.localScale.x : parentTransform.localScale.x, flipY ? -parentTransform.localScale.y : parentTransform.localScale.y, 1);
|
||||
helper.position = parentTransform.position;
|
||||
helper.rotation = parentTransform.rotation;
|
||||
helper.localScale = new Vector3(flipX ? -parentTransform.localScale.x : parentTransform.localScale.x, flipY ? -parentTransform.localScale.y : parentTransform.localScale.y, 1);
|
||||
|
||||
|
||||
Vector3 pos = t.position;
|
||||
pos = helper.InverseTransformPoint(pos);
|
||||
b.X = Mathf.Lerp(b.X, pos.x, mix);
|
||||
b.Y = Mathf.Lerp(b.Y, pos.y, mix);
|
||||
Vector3 pos = t.position;
|
||||
pos = helper.InverseTransformPoint(pos);
|
||||
b.X = Mathf.Lerp(b.X, pos.x, mix);
|
||||
b.Y = Mathf.Lerp(b.Y, pos.y, mix);
|
||||
|
||||
Vector3 right = helper.InverseTransformDirection(t.right);
|
||||
Vector3 right = helper.InverseTransformDirection(t.right);
|
||||
|
||||
float a = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg;
|
||||
float a = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg;
|
||||
|
||||
// MITCH
|
||||
//if (b.WorldFlipX ^ b.WorldFlipY) {
|
||||
// a *= -1;
|
||||
//}
|
||||
|
||||
if (parentBone != null) {
|
||||
// MITCH
|
||||
//if ((b.WorldFlipX ^ b.WorldFlipY) != flip) {
|
||||
// a -= GetCompensatedRotationIK(parentBone) * 2;
|
||||
// MITCH
|
||||
//if (b.WorldFlipX ^ b.WorldFlipY) {
|
||||
// a *= -1;
|
||||
//}
|
||||
|
||||
if (parentBone != null) {
|
||||
// MITCH
|
||||
//if ((b.WorldFlipX ^ b.WorldFlipY) != flip) {
|
||||
// a -= GetCompensatedRotationIK(parentBone) * 2;
|
||||
//}
|
||||
}
|
||||
|
||||
b.Rotation = Mathf.Lerp(b.Rotation, a, mix);
|
||||
// MITCH
|
||||
// b.RotationIK = Mathf.Lerp(b.rotationIK, a, mix);
|
||||
}
|
||||
}
|
||||
|
||||
float GetCompensatedRotationIK (Bone b) {
|
||||
Bone parent = b.Parent;
|
||||
// MITCH
|
||||
float a = b.AppliedRotation;
|
||||
while (parent != null) {
|
||||
a += parent.AppliedRotation;
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
b.Rotation = Mathf.Lerp(b.Rotation, a, mix);
|
||||
// MITCH
|
||||
// b.RotationIK = Mathf.Lerp(b.rotationIK, a, mix);
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
float GetCompensatedRotationIK (Bone b) {
|
||||
Bone parent = b.Parent;
|
||||
// MITCH
|
||||
float a = b.AppliedRotation;
|
||||
while (parent != null) {
|
||||
a += parent.AppliedRotation;
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,432 +6,427 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Spine;
|
||||
using Spine.Unity;
|
||||
|
||||
[RequireComponent(typeof(SkeletonRenderer))]
|
||||
public class SkeletonRagdoll2D : MonoBehaviour {
|
||||
private static Transform helper;
|
||||
namespace Spine.Unity.Modules {
|
||||
[RequireComponent(typeof(SkeletonRenderer))]
|
||||
public class SkeletonRagdoll2D : MonoBehaviour {
|
||||
private static Transform helper;
|
||||
|
||||
[Header("Hierarchy")]
|
||||
[SpineBone]
|
||||
public string startingBoneName = "";
|
||||
[SpineBone]
|
||||
public List<string> stopBoneNames = new List<string>();
|
||||
[Header("Hierarchy")]
|
||||
[SpineBone]
|
||||
public string startingBoneName = "";
|
||||
[SpineBone]
|
||||
public List<string> stopBoneNames = new List<string>();
|
||||
|
||||
[Header("Parameters")]
|
||||
public bool applyOnStart;
|
||||
[Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")]
|
||||
public bool pinStartBone;
|
||||
public float gravityScale = 1;
|
||||
[Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")]
|
||||
public bool disableIK = true;
|
||||
[Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")]
|
||||
public float thickness = 0.125f;
|
||||
[Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")]
|
||||
public float rotationLimit = 20;
|
||||
public float rootMass = 20;
|
||||
[Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")]
|
||||
[Range(0.01f, 1f)]
|
||||
public float massFalloffFactor = 0.4f;
|
||||
[Tooltip("The layer assigned to all of the rigidbody parts.")]
|
||||
public int colliderLayer = 0;
|
||||
[Range(0, 1)]
|
||||
public float mix = 1;
|
||||
[Header("Parameters")]
|
||||
public bool applyOnStart;
|
||||
[Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")]
|
||||
public bool pinStartBone;
|
||||
public float gravityScale = 1;
|
||||
[Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")]
|
||||
public bool disableIK = true;
|
||||
[Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")]
|
||||
public float thickness = 0.125f;
|
||||
[Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")]
|
||||
public float rotationLimit = 20;
|
||||
public float rootMass = 20;
|
||||
[Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")]
|
||||
[Range(0.01f, 1f)]
|
||||
public float massFalloffFactor = 0.4f;
|
||||
[Tooltip("The layer assigned to all of the rigidbody parts.")]
|
||||
public int colliderLayer = 0;
|
||||
[Range(0, 1)]
|
||||
public float mix = 1;
|
||||
|
||||
public Rigidbody2D RootRigidbody {
|
||||
get {
|
||||
return this.rootRigidbody;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 RootOffset {
|
||||
get {
|
||||
return this.rootOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 EstimatedSkeletonPosition {
|
||||
get {
|
||||
return this.rootRigidbody.position - rootOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsActive {
|
||||
get {
|
||||
return this.isActive;
|
||||
}
|
||||
}
|
||||
|
||||
private Rigidbody2D rootRigidbody;
|
||||
private ISkeletonAnimation skeletonAnim;
|
||||
private Skeleton skeleton;
|
||||
private Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
|
||||
private Bone startingBone;
|
||||
private Transform ragdollRoot;
|
||||
private Vector2 rootOffset;
|
||||
private bool isActive;
|
||||
|
||||
|
||||
IEnumerator Start () {
|
||||
skeletonAnim = (ISkeletonAnimation)GetComponent<SkeletonRenderer>();
|
||||
if (helper == null) {
|
||||
helper = (Transform)(new GameObject("Helper")).transform;
|
||||
helper.hideFlags = HideFlags.HideInHierarchy;
|
||||
public Rigidbody2D RootRigidbody {
|
||||
get { return this.rootRigidbody; }
|
||||
}
|
||||
|
||||
if (applyOnStart) {
|
||||
yield return null;
|
||||
Apply();
|
||||
}
|
||||
}
|
||||
|
||||
public Coroutine SmoothMix (float target, float duration) {
|
||||
return StartCoroutine(SmoothMixCoroutine(target, duration));
|
||||
}
|
||||
|
||||
IEnumerator SmoothMixCoroutine (float target, float duration) {
|
||||
float startTime = Time.time;
|
||||
float startMix = mix;
|
||||
while (mix > 0) {
|
||||
mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSkeletonPosition (Vector3 worldPosition) {
|
||||
if (!isActive) {
|
||||
Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!");
|
||||
return;
|
||||
public Vector3 RootOffset {
|
||||
get { return this.rootOffset; }
|
||||
}
|
||||
|
||||
Vector3 offset = worldPosition - transform.position;
|
||||
transform.position = worldPosition;
|
||||
foreach (Transform t in boneTable.Values) {
|
||||
t.position -= offset;
|
||||
public Vector3 EstimatedSkeletonPosition {
|
||||
get { return this.rootRigidbody.position - rootOffset; }
|
||||
}
|
||||
|
||||
UpdateWorld(null);
|
||||
skeleton.UpdateWorldTransform();
|
||||
}
|
||||
|
||||
public Rigidbody2D[] GetRigidbodyArray () {
|
||||
if (!isActive)
|
||||
return new Rigidbody2D[0];
|
||||
|
||||
Rigidbody2D[] arr = new Rigidbody2D[boneTable.Count];
|
||||
int i = 0;
|
||||
foreach (Transform t in boneTable.Values) {
|
||||
arr[i] = t.GetComponent<Rigidbody2D>();
|
||||
i++;
|
||||
public bool IsActive {
|
||||
get { return this.isActive; }
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
private Rigidbody2D rootRigidbody;
|
||||
private ISkeletonAnimation skeletonAnim;
|
||||
private Skeleton skeleton;
|
||||
private Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
|
||||
private Bone startingBone;
|
||||
private Transform ragdollRoot;
|
||||
private Vector2 rootOffset;
|
||||
private bool isActive;
|
||||
|
||||
|
||||
IEnumerator Start () {
|
||||
skeletonAnim = (ISkeletonAnimation)GetComponent<SkeletonRenderer>();
|
||||
if (helper == null) {
|
||||
helper = (Transform)(new GameObject("Helper")).transform;
|
||||
helper.hideFlags = HideFlags.HideInHierarchy;
|
||||
}
|
||||
|
||||
if (applyOnStart) {
|
||||
yield return null;
|
||||
Apply();
|
||||
}
|
||||
}
|
||||
|
||||
public Coroutine SmoothMix (float target, float duration) {
|
||||
return StartCoroutine(SmoothMixCoroutine(target, duration));
|
||||
}
|
||||
|
||||
IEnumerator SmoothMixCoroutine (float target, float duration) {
|
||||
float startTime = Time.time;
|
||||
float startMix = mix;
|
||||
while (mix > 0) {
|
||||
mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSkeletonPosition (Vector3 worldPosition) {
|
||||
if (!isActive) {
|
||||
Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 offset = worldPosition - transform.position;
|
||||
transform.position = worldPosition;
|
||||
foreach (Transform t in boneTable.Values) {
|
||||
t.position -= offset;
|
||||
}
|
||||
|
||||
UpdateWorld(null);
|
||||
skeleton.UpdateWorldTransform();
|
||||
}
|
||||
|
||||
public Rigidbody2D[] GetRigidbodyArray () {
|
||||
if (!isActive)
|
||||
return new Rigidbody2D[0];
|
||||
|
||||
Rigidbody2D[] arr = new Rigidbody2D[boneTable.Count];
|
||||
int i = 0;
|
||||
foreach (Transform t in boneTable.Values) {
|
||||
arr[i] = t.GetComponent<Rigidbody2D>();
|
||||
i++;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
public Rigidbody2D GetRigidbody (string boneName) {
|
||||
var bone = skeleton.FindBone(boneName);
|
||||
if (bone == null)
|
||||
return null;
|
||||
|
||||
if (boneTable.ContainsKey(bone))
|
||||
return boneTable[bone].GetComponent<Rigidbody2D>();
|
||||
|
||||
public Rigidbody2D GetRigidbody (string boneName) {
|
||||
var bone = skeleton.FindBone(boneName);
|
||||
if (bone == null)
|
||||
return null;
|
||||
|
||||
if (boneTable.ContainsKey(bone))
|
||||
return boneTable[bone].GetComponent<Rigidbody2D>();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Remove () {
|
||||
isActive = false;
|
||||
foreach (var t in boneTable.Values) {
|
||||
Destroy(t.gameObject);
|
||||
}
|
||||
Destroy(ragdollRoot.gameObject);
|
||||
|
||||
boneTable.Clear();
|
||||
skeletonAnim.UpdateWorld -= UpdateWorld;
|
||||
}
|
||||
public void Remove () {
|
||||
isActive = false;
|
||||
foreach (var t in boneTable.Values) {
|
||||
Destroy(t.gameObject);
|
||||
}
|
||||
Destroy(ragdollRoot.gameObject);
|
||||
|
||||
public void Apply () {
|
||||
isActive = true;
|
||||
skeleton = skeletonAnim.Skeleton;
|
||||
mix = 1;
|
||||
boneTable.Clear();
|
||||
skeletonAnim.UpdateWorld -= UpdateWorld;
|
||||
}
|
||||
|
||||
var ragdollRootBone = skeleton.FindBone(startingBoneName);
|
||||
startingBone = ragdollRootBone;
|
||||
RecursivelyCreateBoneProxies(ragdollRootBone);
|
||||
public void Apply () {
|
||||
isActive = true;
|
||||
skeleton = skeletonAnim.Skeleton;
|
||||
mix = 1;
|
||||
|
||||
rootRigidbody = boneTable[ragdollRootBone].GetComponent<Rigidbody2D>();
|
||||
rootRigidbody.isKinematic = pinStartBone;
|
||||
rootRigidbody.mass = rootMass;
|
||||
var ragdollRootBone = skeleton.FindBone(startingBoneName);
|
||||
startingBone = ragdollRootBone;
|
||||
RecursivelyCreateBoneProxies(ragdollRootBone);
|
||||
|
||||
List<Collider2D> boneColliders = new List<Collider2D>();
|
||||
rootRigidbody = boneTable[ragdollRootBone].GetComponent<Rigidbody2D>();
|
||||
rootRigidbody.isKinematic = pinStartBone;
|
||||
rootRigidbody.mass = rootMass;
|
||||
|
||||
foreach (var pair in boneTable) {
|
||||
var b = pair.Key;
|
||||
var t = pair.Value;
|
||||
Bone parentBone = null;
|
||||
Transform parentTransform = transform;
|
||||
List<Collider2D> boneColliders = new List<Collider2D>();
|
||||
|
||||
boneColliders.Add(t.GetComponent<Collider2D>());
|
||||
foreach (var pair in boneTable) {
|
||||
var b = pair.Key;
|
||||
var t = pair.Value;
|
||||
Bone parentBone = null;
|
||||
Transform parentTransform = transform;
|
||||
|
||||
if (b != startingBone) {
|
||||
parentBone = b.Parent;
|
||||
parentTransform = boneTable[parentBone];
|
||||
} else {
|
||||
ragdollRoot = new GameObject("RagdollRoot").transform;
|
||||
ragdollRoot.parent = transform;
|
||||
boneColliders.Add(t.GetComponent<Collider2D>());
|
||||
|
||||
if (b == skeleton.RootBone) {
|
||||
ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
|
||||
ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b));
|
||||
parentTransform = ragdollRoot;
|
||||
if (b != startingBone) {
|
||||
parentBone = b.Parent;
|
||||
parentTransform = boneTable[parentBone];
|
||||
} else {
|
||||
ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
|
||||
ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b.Parent));
|
||||
parentTransform = ragdollRoot;
|
||||
ragdollRoot = new GameObject("RagdollRoot").transform;
|
||||
ragdollRoot.parent = transform;
|
||||
|
||||
if (b == skeleton.RootBone) {
|
||||
ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
|
||||
ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b));
|
||||
parentTransform = ragdollRoot;
|
||||
} else {
|
||||
ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
|
||||
ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b.Parent));
|
||||
parentTransform = ragdollRoot;
|
||||
}
|
||||
|
||||
rootOffset = t.position - transform.position;
|
||||
}
|
||||
|
||||
rootOffset = t.position - transform.position;
|
||||
}
|
||||
var rbParent = parentTransform.GetComponent<Rigidbody2D>();
|
||||
|
||||
var rbParent = parentTransform.GetComponent<Rigidbody2D>();
|
||||
|
||||
if (rbParent != null) {
|
||||
var joint = t.gameObject.AddComponent<HingeJoint2D>();
|
||||
joint.connectedBody = rbParent;
|
||||
Vector3 localPos = parentTransform.InverseTransformPoint(t.position);
|
||||
localPos.x *= 1;
|
||||
joint.connectedAnchor = localPos;
|
||||
joint.GetComponent<Rigidbody2D>().mass = joint.connectedBody.mass * massFalloffFactor;
|
||||
JointAngleLimits2D limits = new JointAngleLimits2D();
|
||||
limits.min = -rotationLimit;
|
||||
limits.max = rotationLimit;
|
||||
joint.limits = limits;
|
||||
joint.useLimits = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 0; x < boneColliders.Count; x++) {
|
||||
for (int y = 0; y < boneColliders.Count; y++) {
|
||||
if (x == y) continue;
|
||||
Physics2D.IgnoreCollision(boneColliders[x], boneColliders[y]);
|
||||
}
|
||||
}
|
||||
|
||||
var utilityBones = GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
if (utilityBones.Length > 0) {
|
||||
List<string> destroyedUtilityBoneNames = new List<string>();
|
||||
foreach (var ub in utilityBones) {
|
||||
if (ub.mode == SkeletonUtilityBone.Mode.Override) {
|
||||
destroyedUtilityBoneNames.Add(ub.gameObject.name);
|
||||
Destroy(ub.gameObject);
|
||||
if (rbParent != null) {
|
||||
var joint = t.gameObject.AddComponent<HingeJoint2D>();
|
||||
joint.connectedBody = rbParent;
|
||||
Vector3 localPos = parentTransform.InverseTransformPoint(t.position);
|
||||
localPos.x *= 1;
|
||||
joint.connectedAnchor = localPos;
|
||||
joint.GetComponent<Rigidbody2D>().mass = joint.connectedBody.mass * massFalloffFactor;
|
||||
JointAngleLimits2D limits = new JointAngleLimits2D();
|
||||
limits.min = -rotationLimit;
|
||||
limits.max = rotationLimit;
|
||||
joint.limits = limits;
|
||||
joint.useLimits = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (destroyedUtilityBoneNames.Count > 0) {
|
||||
string msg = "Destroyed Utility Bones: ";
|
||||
for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) {
|
||||
msg += destroyedUtilityBoneNames[i];
|
||||
if (i != destroyedUtilityBoneNames.Count - 1) {
|
||||
msg += ",";
|
||||
for (int x = 0; x < boneColliders.Count; x++) {
|
||||
for (int y = 0; y < boneColliders.Count; y++) {
|
||||
if (x == y) continue;
|
||||
Physics2D.IgnoreCollision(boneColliders[x], boneColliders[y]);
|
||||
}
|
||||
}
|
||||
|
||||
var utilityBones = GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
if (utilityBones.Length > 0) {
|
||||
List<string> destroyedUtilityBoneNames = new List<string>();
|
||||
foreach (var ub in utilityBones) {
|
||||
if (ub.mode == SkeletonUtilityBone.Mode.Override) {
|
||||
destroyedUtilityBoneNames.Add(ub.gameObject.name);
|
||||
Destroy(ub.gameObject);
|
||||
}
|
||||
}
|
||||
Debug.LogWarning(msg);
|
||||
|
||||
if (destroyedUtilityBoneNames.Count > 0) {
|
||||
string msg = "Destroyed Utility Bones: ";
|
||||
for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) {
|
||||
msg += destroyedUtilityBoneNames[i];
|
||||
if (i != destroyedUtilityBoneNames.Count - 1) {
|
||||
msg += ",";
|
||||
}
|
||||
}
|
||||
Debug.LogWarning(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (disableIK) {
|
||||
foreach (IkConstraint ik in skeleton.IkConstraints) {
|
||||
ik.Mix = 0;
|
||||
}
|
||||
}
|
||||
|
||||
skeletonAnim.UpdateWorld += UpdateWorld;
|
||||
}
|
||||
|
||||
void RecursivelyCreateBoneProxies (Bone b) {
|
||||
if (stopBoneNames.Contains(b.Data.Name))
|
||||
return;
|
||||
|
||||
GameObject go = new GameObject(b.Data.Name);
|
||||
go.layer = colliderLayer;
|
||||
Transform t = go.transform;
|
||||
boneTable.Add(b, t);
|
||||
|
||||
t.parent = transform;
|
||||
|
||||
t.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
|
||||
//TODO: deal with WorldFlipY
|
||||
// MITCH
|
||||
// t.localRotation = Quaternion.Euler(0, 0, b.WorldFlipX ? -b.WorldRotation : b.WorldRotation);
|
||||
t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX);
|
||||
t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 0);
|
||||
|
||||
float length = b.Data.Length;
|
||||
|
||||
//TODO proper ragdoll branching
|
||||
var colliders = AttachBoundingBoxRagdollColliders(b);
|
||||
|
||||
if (length == 0) {
|
||||
//physics
|
||||
if (colliders.Count == 0) {
|
||||
var circle = go.AddComponent<CircleCollider2D>();
|
||||
circle.radius = thickness / 2f;
|
||||
}
|
||||
} else {
|
||||
//physics
|
||||
if (colliders.Count == 0) {
|
||||
var box = go.AddComponent<BoxCollider2D>();
|
||||
box.size = new Vector2(length, thickness);
|
||||
#if UNITY_5
|
||||
// MITCH
|
||||
// box.offset = new Vector2((b.WorldFlipX ? -length : length) / 2, 0);
|
||||
box.offset = new Vector2(length / 2, 0);
|
||||
#else
|
||||
//box.center = new Vector2((b.WorldFlipX ? -length : length) / 2, 0);
|
||||
box.center = new Vector2(length/2, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
var rb = go.AddComponent<Rigidbody2D>();
|
||||
rb.gravityScale = gravityScale;
|
||||
|
||||
foreach (Bone child in b.Children) {
|
||||
RecursivelyCreateBoneProxies(child);
|
||||
}
|
||||
}
|
||||
|
||||
if (disableIK) {
|
||||
foreach (IkConstraint ik in skeleton.IkConstraints) {
|
||||
ik.Mix = 0;
|
||||
}
|
||||
}
|
||||
List<Collider2D> AttachBoundingBoxRagdollColliders (Bone b) {
|
||||
List<Collider2D> colliders = new List<Collider2D>();
|
||||
Transform t = boneTable[b];
|
||||
GameObject go = t.gameObject;
|
||||
var skin = skeleton.Skin;
|
||||
if (skin == null)
|
||||
skin = skeleton.Data.DefaultSkin;
|
||||
|
||||
skeletonAnim.UpdateWorld += UpdateWorld;
|
||||
}
|
||||
// MITCH
|
||||
// bool flipX = b.WorldFlipX;
|
||||
// bool flipY = b.WorldFlipY;
|
||||
bool flipX = false;
|
||||
bool flipY = false;
|
||||
|
||||
void RecursivelyCreateBoneProxies (Bone b) {
|
||||
if (stopBoneNames.Contains(b.Data.Name))
|
||||
return;
|
||||
List<Attachment> attachments = new List<Attachment>();
|
||||
foreach (Slot s in skeleton.Slots) {
|
||||
if (s.Bone == b) {
|
||||
skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments);
|
||||
foreach (var a in attachments) {
|
||||
if (a is BoundingBoxAttachment) {
|
||||
if (!a.Name.ToLower().Contains("ragdoll"))
|
||||
continue;
|
||||
|
||||
GameObject go = new GameObject(b.Data.Name);
|
||||
go.layer = colliderLayer;
|
||||
Transform t = go.transform;
|
||||
boneTable.Add(b, t);
|
||||
var collider = SkeletonUtility.AddBoundingBoxAsComponent((BoundingBoxAttachment)a, go, false);
|
||||
|
||||
t.parent = transform;
|
||||
if (flipX || flipY) {
|
||||
Vector2[] points = collider.points;
|
||||
|
||||
t.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
|
||||
//TODO: deal with WorldFlipY
|
||||
// MITCH
|
||||
// t.localRotation = Quaternion.Euler(0, 0, b.WorldFlipX ? -b.WorldRotation : b.WorldRotation);
|
||||
t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX);
|
||||
t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 0);
|
||||
for (int i = 0; i < points.Length; i++) {
|
||||
if (flipX)
|
||||
points[i].x *= -1;
|
||||
|
||||
float length = b.Data.Length;
|
||||
if (flipY)
|
||||
points[i].y *= -1;
|
||||
}
|
||||
|
||||
//TODO proper ragdoll branching
|
||||
var colliders = AttachBoundingBoxRagdollColliders(b);
|
||||
|
||||
if (length == 0) {
|
||||
//physics
|
||||
if (colliders.Count == 0) {
|
||||
var circle = go.AddComponent<CircleCollider2D>();
|
||||
circle.radius = thickness / 2f;
|
||||
}
|
||||
} else {
|
||||
//physics
|
||||
if (colliders.Count == 0) {
|
||||
var box = go.AddComponent<BoxCollider2D>();
|
||||
box.size = new Vector2(length, thickness);
|
||||
#if UNITY_5
|
||||
// MITCH
|
||||
// box.offset = new Vector2((b.WorldFlipX ? -length : length) / 2, 0);
|
||||
box.offset = new Vector2(length / 2, 0);
|
||||
#else
|
||||
//box.center = new Vector2((b.WorldFlipX ? -length : length) / 2, 0);
|
||||
box.center = new Vector2(length/2, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
var rb = go.AddComponent<Rigidbody2D>();
|
||||
rb.gravityScale = gravityScale;
|
||||
|
||||
foreach (Bone child in b.Children) {
|
||||
RecursivelyCreateBoneProxies(child);
|
||||
}
|
||||
}
|
||||
|
||||
List<Collider2D> AttachBoundingBoxRagdollColliders (Bone b) {
|
||||
List<Collider2D> colliders = new List<Collider2D>();
|
||||
Transform t = boneTable[b];
|
||||
GameObject go = t.gameObject;
|
||||
var skin = skeleton.Skin;
|
||||
if (skin == null)
|
||||
skin = skeleton.Data.DefaultSkin;
|
||||
|
||||
// MITCH
|
||||
// bool flipX = b.WorldFlipX;
|
||||
// bool flipY = b.WorldFlipY;
|
||||
bool flipX = false;
|
||||
bool flipY = false;
|
||||
|
||||
List<Attachment> attachments = new List<Attachment>();
|
||||
foreach (Slot s in skeleton.Slots) {
|
||||
if (s.Bone == b) {
|
||||
skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments);
|
||||
foreach (var a in attachments) {
|
||||
if (a is BoundingBoxAttachment) {
|
||||
if (!a.Name.ToLower().Contains("ragdoll"))
|
||||
continue;
|
||||
|
||||
var collider = SkeletonUtility.AddBoundingBoxAsComponent((BoundingBoxAttachment)a, go, false);
|
||||
|
||||
if (flipX || flipY) {
|
||||
Vector2[] points = collider.points;
|
||||
|
||||
for (int i = 0; i < points.Length; i++) {
|
||||
if (flipX)
|
||||
points[i].x *= -1;
|
||||
|
||||
if (flipY)
|
||||
points[i].y *= -1;
|
||||
collider.points = points;
|
||||
}
|
||||
|
||||
collider.points = points;
|
||||
colliders.Add(collider);
|
||||
}
|
||||
|
||||
colliders.Add(collider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return colliders;
|
||||
}
|
||||
|
||||
return colliders;
|
||||
}
|
||||
void UpdateWorld (ISkeletonAnimation skeletonRenderer) {
|
||||
foreach (var pair in boneTable) {
|
||||
var b = pair.Key;
|
||||
var t = pair.Value;
|
||||
//bool flip = false;
|
||||
bool flipX = false; //TODO: deal with negative scale instead of Flip Key
|
||||
bool flipY = false; //TODO: deal with negative scale instead of Flip Key
|
||||
Bone parentBone = null;
|
||||
Transform parentTransform = transform;
|
||||
|
||||
void UpdateWorld (ISkeletonAnimation skeletonRenderer) {
|
||||
foreach (var pair in boneTable) {
|
||||
var b = pair.Key;
|
||||
var t = pair.Value;
|
||||
//bool flip = false;
|
||||
bool flipX = false; //TODO: deal with negative scale instead of Flip Key
|
||||
bool flipY = false; //TODO: deal with negative scale instead of Flip Key
|
||||
Bone parentBone = null;
|
||||
Transform parentTransform = transform;
|
||||
if (b != startingBone) {
|
||||
parentBone = b.Parent;
|
||||
parentTransform = boneTable[parentBone];
|
||||
// MITCH
|
||||
// flipX = parentBone.WorldFlipX;
|
||||
// flipY = parentBone.WorldFlipY;
|
||||
|
||||
if (b != startingBone) {
|
||||
parentBone = b.Parent;
|
||||
parentTransform = boneTable[parentBone];
|
||||
// MITCH
|
||||
// flipX = parentBone.WorldFlipX;
|
||||
// flipY = parentBone.WorldFlipY;
|
||||
|
||||
} else {
|
||||
parentBone = b.Parent;
|
||||
parentTransform = ragdollRoot;
|
||||
if (b.Parent != null) {
|
||||
// MITCH
|
||||
// flipX = b.worldFlipX;
|
||||
// flipY = b.WorldFlipY;
|
||||
} else {
|
||||
flipX = b.Skeleton.FlipX;
|
||||
flipY = b.Skeleton.FlipY;
|
||||
parentBone = b.Parent;
|
||||
parentTransform = ragdollRoot;
|
||||
if (b.Parent != null) {
|
||||
// MITCH
|
||||
// flipX = b.worldFlipX;
|
||||
// flipY = b.WorldFlipY;
|
||||
} else {
|
||||
flipX = b.Skeleton.FlipX;
|
||||
flipY = b.Skeleton.FlipY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//flip = flipX ^ flipY;
|
||||
//flip = flipX ^ flipY;
|
||||
|
||||
helper.position = parentTransform.position;
|
||||
helper.rotation = parentTransform.rotation;
|
||||
helper.localScale = new Vector3(flipX ? -parentTransform.localScale.x : parentTransform.localScale.x, flipY ? -parentTransform.localScale.y : parentTransform.localScale.y, 1);
|
||||
helper.position = parentTransform.position;
|
||||
helper.rotation = parentTransform.rotation;
|
||||
helper.localScale = new Vector3(flipX ? -parentTransform.localScale.x : parentTransform.localScale.x, flipY ? -parentTransform.localScale.y : parentTransform.localScale.y, 1);
|
||||
|
||||
|
||||
Vector3 pos = t.position;
|
||||
pos = helper.InverseTransformPoint(pos);
|
||||
b.X = Mathf.Lerp(b.X, pos.x, mix);
|
||||
b.Y = Mathf.Lerp(b.Y, pos.y, mix);
|
||||
Vector3 pos = t.position;
|
||||
pos = helper.InverseTransformPoint(pos);
|
||||
b.X = Mathf.Lerp(b.X, pos.x, mix);
|
||||
b.Y = Mathf.Lerp(b.Y, pos.y, mix);
|
||||
|
||||
Vector3 right = helper.InverseTransformDirection(t.right);
|
||||
Vector3 right = helper.InverseTransformDirection(t.right);
|
||||
|
||||
float a = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg;
|
||||
float a = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg;
|
||||
|
||||
// MITCH
|
||||
//if (b.WorldFlipX ^ b.WorldFlipY) {
|
||||
// a *= -1;
|
||||
//}
|
||||
|
||||
if (parentBone != null) {
|
||||
// MITCH
|
||||
//if ((b.WorldFlipX ^ b.WorldFlipY) != flip) {
|
||||
// a -= GetCompensatedRotationIK(parentBone) * 2;
|
||||
// MITCH
|
||||
//if (b.WorldFlipX ^ b.WorldFlipY) {
|
||||
// a *= -1;
|
||||
//}
|
||||
|
||||
if (parentBone != null) {
|
||||
// MITCH
|
||||
//if ((b.WorldFlipX ^ b.WorldFlipY) != flip) {
|
||||
// a -= GetCompensatedRotationIK(parentBone) * 2;
|
||||
//}
|
||||
}
|
||||
|
||||
b.Rotation = Mathf.Lerp(b.Rotation, a, mix);
|
||||
// MITCH
|
||||
// b.RotationIK = Mathf.Lerp(b.rotationIK, a, mix);
|
||||
}
|
||||
}
|
||||
|
||||
float GetCompensatedRotationIK (Bone b) {
|
||||
Bone parent = b.Parent;
|
||||
// MITCH
|
||||
float a = b.AppliedRotation;
|
||||
while (parent != null) {
|
||||
a += parent.AppliedRotation;
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
b.Rotation = Mathf.Lerp(b.Rotation, a, mix);
|
||||
// MITCH
|
||||
// b.RotationIK = Mathf.Lerp(b.rotationIK, a, mix);
|
||||
}
|
||||
}
|
||||
|
||||
float GetCompensatedRotationIK (Bone b) {
|
||||
Bone parent = b.Parent;
|
||||
// MITCH
|
||||
float a = b.AppliedRotation;
|
||||
while (parent != null) {
|
||||
a += parent.AppliedRotation;
|
||||
parent = parent.parent;
|
||||
return a;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
void OnDrawGizmosSelected () {
|
||||
if (isActive) {
|
||||
Gizmos.DrawWireSphere(transform.position, thickness * 1.2f);
|
||||
Vector3 newTransformPos = rootRigidbody.position - rootOffset;
|
||||
Gizmos.DrawLine(transform.position, newTransformPos);
|
||||
Gizmos.DrawWireSphere(newTransformPos, thickness * 1.2f);
|
||||
void OnDrawGizmosSelected () {
|
||||
if (isActive) {
|
||||
Gizmos.DrawWireSphere(transform.position, thickness * 1.2f);
|
||||
Vector3 newTransformPos = rootRigidbody.position - rootOffset;
|
||||
Gizmos.DrawLine(transform.position, newTransformPos);
|
||||
Gizmos.DrawWireSphere(newTransformPos, thickness * 1.2f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,9 +1,38 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.3
|
||||
*
|
||||
* Copyright (c) 2013-2015, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
* non-transferable license to use, install, execute and perform the Spine
|
||||
* Runtimes Software (the "Software") and derivative works solely for personal
|
||||
* or internal use. Without the written permission of Esoteric Software (see
|
||||
* Section 2 of the Spine Software License Agreement), 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 SOFTWARE 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.
|
||||
*****************************************************************************/
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Spine.Unity;
|
||||
using Spine.Unity.Editor;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
namespace Spine.Unity.Modules {
|
||||
[CustomEditor(typeof(SkeletonPartsRenderer))]
|
||||
public class SkeletonRenderPartInspector : UnityEditor.Editor {
|
||||
SpineInspectorUtility.SerializedSortingProperties sortingProperties;
|
||||
@ -13,7 +42,6 @@ namespace Spine.Unity.Editor {
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
DrawDefaultInspector();
|
||||
SpineInspectorUtility.SortingPropertyFields(sortingProperties, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,40 @@
|
||||
using UnityEngine;
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.3
|
||||
*
|
||||
* Copyright (c) 2013-2015, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
* non-transferable license to use, install, execute and perform the Spine
|
||||
* Runtimes Software (the "Software") and derivative works solely for personal
|
||||
* or internal use. Without the written permission of Esoteric Software (see
|
||||
* Section 2 of the Spine Software License Agreement), 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 SOFTWARE 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.
|
||||
*****************************************************************************/
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
using Spine.Unity;
|
||||
using Spine.Unity.Editor;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
namespace Spine.Unity.Modules {
|
||||
|
||||
[CustomEditor(typeof(SkeletonRenderSeparator))]
|
||||
public class SkeletonRenderSeparatorInspector : UnityEditor.Editor {
|
||||
|
||||
@ -1,8 +1,37 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.3
|
||||
*
|
||||
* Copyright (c) 2013-2015, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
* non-transferable license to use, install, execute and perform the Spine
|
||||
* Runtimes Software (the "Software") and derivative works solely for personal
|
||||
* or internal use. Without the written permission of Esoteric Software (see
|
||||
* Section 2 of the Spine Software License Agreement), 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 SOFTWARE 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.
|
||||
*****************************************************************************/
|
||||
using UnityEngine;
|
||||
using Spine.Unity.MeshGeneration;
|
||||
|
||||
namespace Spine.Unity {
|
||||
namespace Spine.Unity.Modules {
|
||||
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
|
||||
public class SkeletonPartsRenderer : MonoBehaviour {
|
||||
|
||||
|
||||
@ -1,9 +1,38 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.3
|
||||
*
|
||||
* Copyright (c) 2013-2015, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
* non-transferable license to use, install, execute and perform the Spine
|
||||
* Runtimes Software (the "Software") and derivative works solely for personal
|
||||
* or internal use. Without the written permission of Esoteric Software (see
|
||||
* Section 2 of the Spine Software License Agreement), 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 SOFTWARE 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.
|
||||
*****************************************************************************/
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using Spine.Unity;
|
||||
|
||||
namespace Spine.Unity {
|
||||
namespace Spine.Unity.Modules {
|
||||
|
||||
[ExecuteInEditMode]
|
||||
[HelpURL("https://github.com/pharan/spine-unity-docs/blob/master/SkeletonRenderSeparator.md")]
|
||||
@ -28,7 +57,7 @@ namespace Spine.Unity {
|
||||
public bool copyPropertyBlock = false;
|
||||
[Tooltip("Copies MeshRenderer flags into ")]
|
||||
public bool copyMeshRendererFlags = false;
|
||||
public List<Spine.Unity.SkeletonPartsRenderer> partsRenderers = new List<SkeletonPartsRenderer>();
|
||||
public List<Spine.Unity.Modules.SkeletonPartsRenderer> partsRenderers = new List<SkeletonPartsRenderer>();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void Reset () {
|
||||
|
||||
@ -32,56 +32,57 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class SkeletonUtilityEyeConstraint : SkeletonUtilityConstraint {
|
||||
namespace Spine.Unity.Modules {
|
||||
public class SkeletonUtilityEyeConstraint : SkeletonUtilityConstraint {
|
||||
public Transform[] eyes;
|
||||
public float radius = 0.5f;
|
||||
public Transform target;
|
||||
public Vector3 targetPosition;
|
||||
public float speed = 10;
|
||||
Vector3[] origins;
|
||||
Vector3 centerPoint;
|
||||
|
||||
public Transform[] eyes;
|
||||
public float radius = 0.5f;
|
||||
public Transform target;
|
||||
public Vector3 targetPosition;
|
||||
public float speed = 10;
|
||||
Vector3[] origins;
|
||||
Vector3 centerPoint;
|
||||
protected override void OnEnable () {
|
||||
if (!Application.isPlaying)
|
||||
return;
|
||||
|
||||
protected override void OnEnable () {
|
||||
if (!Application.isPlaying)
|
||||
return;
|
||||
base.OnEnable();
|
||||
|
||||
base.OnEnable();
|
||||
Bounds centerBounds = new Bounds(eyes[0].localPosition, Vector3.zero);
|
||||
origins = new Vector3[eyes.Length];
|
||||
for (int i = 0; i < eyes.Length; i++) {
|
||||
origins[i] = eyes[i].localPosition;
|
||||
centerBounds.Encapsulate(origins[i]);
|
||||
}
|
||||
|
||||
Bounds centerBounds = new Bounds(eyes[0].localPosition, Vector3.zero);
|
||||
origins = new Vector3[eyes.Length];
|
||||
for (int i = 0; i < eyes.Length; i++) {
|
||||
origins[i] = eyes[i].localPosition;
|
||||
centerBounds.Encapsulate(origins[i]);
|
||||
centerPoint = centerBounds.center;
|
||||
}
|
||||
|
||||
centerPoint = centerBounds.center;
|
||||
}
|
||||
protected override void OnDisable () {
|
||||
if (!Application.isPlaying)
|
||||
return;
|
||||
|
||||
protected override void OnDisable () {
|
||||
if (!Application.isPlaying)
|
||||
return;
|
||||
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
public override void DoUpdate () {
|
||||
|
||||
if (target != null)
|
||||
targetPosition = target.position;
|
||||
|
||||
Vector3 goal = targetPosition;
|
||||
|
||||
Vector3 center = transform.TransformPoint(centerPoint);
|
||||
Vector3 dir = goal - center;
|
||||
|
||||
if (dir.magnitude > 1)
|
||||
dir.Normalize();
|
||||
|
||||
for (int i = 0; i < eyes.Length; i++) {
|
||||
center = transform.TransformPoint(origins[i]);
|
||||
eyes[i].position = Vector3.MoveTowards(eyes[i].position, center + (dir * radius), speed * Time.deltaTime);
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
public override void DoUpdate () {
|
||||
|
||||
if (target != null)
|
||||
targetPosition = target.position;
|
||||
|
||||
Vector3 goal = targetPosition;
|
||||
|
||||
Vector3 center = transform.TransformPoint(centerPoint);
|
||||
Vector3 dir = goal - center;
|
||||
|
||||
if (dir.magnitude > 1)
|
||||
dir.Normalize();
|
||||
|
||||
for (int i = 0; i < eyes.Length; i++) {
|
||||
center = transform.TransformPoint(origins[i]);
|
||||
eyes[i].position = Vector3.MoveTowards(eyes[i].position, center + (dir * radius), speed * Time.deltaTime);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,121 +32,124 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
[RequireComponent(typeof(SkeletonUtilityBone)), ExecuteInEditMode]
|
||||
public class SkeletonUtilityGroundConstraint : SkeletonUtilityConstraint {
|
||||
namespace Spine.Unity.Modules {
|
||||
[RequireComponent(typeof(SkeletonUtilityBone)), ExecuteInEditMode]
|
||||
public class SkeletonUtilityGroundConstraint : SkeletonUtilityConstraint {
|
||||
|
||||
#if UNITY_4_3
|
||||
public LayerMask groundMask;
|
||||
public bool use2D = false;
|
||||
public bool useRadius = false;
|
||||
public float castRadius = 0.1f;
|
||||
public float castDistance = 5f;
|
||||
public float castOffset = 0;
|
||||
public float groundOffset = 0;
|
||||
public float adjustSpeed = 5;
|
||||
#else
|
||||
[Tooltip("LayerMask for what objects to raycast against")]
|
||||
public LayerMask groundMask;
|
||||
[Tooltip("The 2D")]
|
||||
public bool use2D = false;
|
||||
[Tooltip("Uses SphereCast for 3D mode and CircleCast for 2D mode")]
|
||||
public bool useRadius = false;
|
||||
[Tooltip("The Radius")]
|
||||
public float castRadius = 0.1f;
|
||||
[Tooltip("How high above the target bone to begin casting from")]
|
||||
public float castDistance = 5f;
|
||||
[Tooltip("X-Axis adjustment")]
|
||||
public float castOffset = 0;
|
||||
[Tooltip("Y-Axis adjustment")]
|
||||
public float groundOffset = 0;
|
||||
[Tooltip("How fast the target IK position adjusts to the ground. Use smaller values to prevent snapping")]
|
||||
public float adjustSpeed = 5;
|
||||
#endif
|
||||
#if UNITY_4_3
|
||||
public LayerMask groundMask;
|
||||
public bool use2D = false;
|
||||
public bool useRadius = false;
|
||||
public float castRadius = 0.1f;
|
||||
public float castDistance = 5f;
|
||||
public float castOffset = 0;
|
||||
public float groundOffset = 0;
|
||||
public float adjustSpeed = 5;
|
||||
#else
|
||||
[Tooltip("LayerMask for what objects to raycast against")]
|
||||
public LayerMask groundMask;
|
||||
[Tooltip("The 2D")]
|
||||
public bool use2D = false;
|
||||
[Tooltip("Uses SphereCast for 3D mode and CircleCast for 2D mode")]
|
||||
public bool useRadius = false;
|
||||
[Tooltip("The Radius")]
|
||||
public float castRadius = 0.1f;
|
||||
[Tooltip("How high above the target bone to begin casting from")]
|
||||
public float castDistance = 5f;
|
||||
[Tooltip("X-Axis adjustment")]
|
||||
public float castOffset = 0;
|
||||
[Tooltip("Y-Axis adjustment")]
|
||||
public float groundOffset = 0;
|
||||
[Tooltip("How fast the target IK position adjusts to the ground. Use smaller values to prevent snapping")]
|
||||
public float adjustSpeed = 5;
|
||||
#endif
|
||||
|
||||
|
||||
Vector3 rayOrigin;
|
||||
Vector3 rayDir = new Vector3(0, -1, 0);
|
||||
float hitY;
|
||||
float lastHitY;
|
||||
Vector3 rayOrigin;
|
||||
Vector3 rayDir = new Vector3(0, -1, 0);
|
||||
float hitY;
|
||||
float lastHitY;
|
||||
|
||||
protected override void OnEnable () {
|
||||
base.OnEnable();
|
||||
lastHitY = transform.position.y;
|
||||
}
|
||||
|
||||
protected override void OnDisable () {
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
public override void DoUpdate () {
|
||||
rayOrigin = transform.position + new Vector3(castOffset, castDistance, 0);
|
||||
|
||||
hitY = float.MinValue;
|
||||
if (use2D) {
|
||||
RaycastHit2D hit;
|
||||
|
||||
if (useRadius) {
|
||||
#if UNITY_4_3
|
||||
//NOTE: Unity 4.3.x does not have CircleCast
|
||||
hit = Physics2D.Raycast(rayOrigin , rayDir, castDistance + groundOffset, groundMask);
|
||||
#else
|
||||
hit = Physics2D.CircleCast(rayOrigin, castRadius, rayDir, castDistance + groundOffset, groundMask);
|
||||
#endif
|
||||
} else {
|
||||
hit = Physics2D.Raycast(rayOrigin, rayDir, castDistance + groundOffset, groundMask);
|
||||
}
|
||||
|
||||
if (hit.collider != null) {
|
||||
hitY = hit.point.y + groundOffset;
|
||||
if (Application.isPlaying) {
|
||||
hitY = Mathf.MoveTowards(lastHitY, hitY, adjustSpeed * Time.deltaTime);
|
||||
}
|
||||
} else {
|
||||
if (Application.isPlaying)
|
||||
hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustSpeed * Time.deltaTime);
|
||||
}
|
||||
} else {
|
||||
RaycastHit hit;
|
||||
bool validHit = false;
|
||||
|
||||
if (useRadius) {
|
||||
validHit = Physics.SphereCast(rayOrigin, castRadius, rayDir, out hit, castDistance + groundOffset, groundMask);
|
||||
} else {
|
||||
validHit = Physics.Raycast(rayOrigin, rayDir, out hit, castDistance + groundOffset, groundMask);
|
||||
}
|
||||
|
||||
if (validHit) {
|
||||
hitY = hit.point.y + groundOffset;
|
||||
if (Application.isPlaying) {
|
||||
hitY = Mathf.MoveTowards(lastHitY, hitY, adjustSpeed * Time.deltaTime);
|
||||
}
|
||||
} else {
|
||||
if (Application.isPlaying)
|
||||
hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustSpeed * Time.deltaTime);
|
||||
}
|
||||
protected override void OnEnable () {
|
||||
base.OnEnable();
|
||||
lastHitY = transform.position.y;
|
||||
}
|
||||
|
||||
Vector3 v = transform.position;
|
||||
v.y = Mathf.Clamp(v.y, Mathf.Min(lastHitY, hitY), float.MaxValue);
|
||||
transform.position = v;
|
||||
|
||||
utilBone.bone.X = transform.localPosition.x;
|
||||
utilBone.bone.Y = transform.localPosition.y;
|
||||
|
||||
lastHitY = hitY;
|
||||
}
|
||||
|
||||
void OnDrawGizmos () {
|
||||
Vector3 hitEnd = rayOrigin + (rayDir * Mathf.Min(castDistance, rayOrigin.y - hitY));
|
||||
Vector3 clearEnd = rayOrigin + (rayDir * castDistance);
|
||||
Gizmos.DrawLine(rayOrigin, hitEnd);
|
||||
|
||||
if (useRadius) {
|
||||
Gizmos.DrawLine(new Vector3(hitEnd.x - castRadius, hitEnd.y - groundOffset, hitEnd.z), new Vector3(hitEnd.x + castRadius, hitEnd.y - groundOffset, hitEnd.z));
|
||||
Gizmos.DrawLine(new Vector3(clearEnd.x - castRadius, clearEnd.y, clearEnd.z), new Vector3(clearEnd.x + castRadius, clearEnd.y, clearEnd.z));
|
||||
protected override void OnDisable () {
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawLine(hitEnd, clearEnd);
|
||||
public override void DoUpdate () {
|
||||
rayOrigin = transform.position + new Vector3(castOffset, castDistance, 0);
|
||||
|
||||
hitY = float.MinValue;
|
||||
if (use2D) {
|
||||
RaycastHit2D hit;
|
||||
|
||||
if (useRadius) {
|
||||
#if UNITY_4_3
|
||||
//NOTE: Unity 4.3.x does not have CircleCast
|
||||
hit = Physics2D.Raycast(rayOrigin , rayDir, castDistance + groundOffset, groundMask);
|
||||
#else
|
||||
hit = Physics2D.CircleCast(rayOrigin, castRadius, rayDir, castDistance + groundOffset, groundMask);
|
||||
#endif
|
||||
} else {
|
||||
hit = Physics2D.Raycast(rayOrigin, rayDir, castDistance + groundOffset, groundMask);
|
||||
}
|
||||
|
||||
if (hit.collider != null) {
|
||||
hitY = hit.point.y + groundOffset;
|
||||
if (Application.isPlaying) {
|
||||
hitY = Mathf.MoveTowards(lastHitY, hitY, adjustSpeed * Time.deltaTime);
|
||||
}
|
||||
} else {
|
||||
if (Application.isPlaying)
|
||||
hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustSpeed * Time.deltaTime);
|
||||
}
|
||||
} else {
|
||||
RaycastHit hit;
|
||||
bool validHit = false;
|
||||
|
||||
if (useRadius) {
|
||||
validHit = Physics.SphereCast(rayOrigin, castRadius, rayDir, out hit, castDistance + groundOffset, groundMask);
|
||||
} else {
|
||||
validHit = Physics.Raycast(rayOrigin, rayDir, out hit, castDistance + groundOffset, groundMask);
|
||||
}
|
||||
|
||||
if (validHit) {
|
||||
hitY = hit.point.y + groundOffset;
|
||||
if (Application.isPlaying) {
|
||||
hitY = Mathf.MoveTowards(lastHitY, hitY, adjustSpeed * Time.deltaTime);
|
||||
}
|
||||
} else {
|
||||
if (Application.isPlaying)
|
||||
hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustSpeed * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 v = transform.position;
|
||||
v.y = Mathf.Clamp(v.y, Mathf.Min(lastHitY, hitY), float.MaxValue);
|
||||
transform.position = v;
|
||||
|
||||
utilBone.bone.X = transform.localPosition.x;
|
||||
utilBone.bone.Y = transform.localPosition.y;
|
||||
|
||||
lastHitY = hitY;
|
||||
}
|
||||
|
||||
void OnDrawGizmos () {
|
||||
Vector3 hitEnd = rayOrigin + (rayDir * Mathf.Min(castDistance, rayOrigin.y - hitY));
|
||||
Vector3 clearEnd = rayOrigin + (rayDir * castDistance);
|
||||
Gizmos.DrawLine(rayOrigin, hitEnd);
|
||||
|
||||
if (useRadius) {
|
||||
Gizmos.DrawLine(new Vector3(hitEnd.x - castRadius, hitEnd.y - groundOffset, hitEnd.z), new Vector3(hitEnd.x + castRadius, hitEnd.y - groundOffset, hitEnd.z));
|
||||
Gizmos.DrawLine(new Vector3(clearEnd.x - castRadius, clearEnd.y, clearEnd.z), new Vector3(clearEnd.x + castRadius, clearEnd.y, clearEnd.z));
|
||||
}
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawLine(hitEnd, clearEnd);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -30,85 +30,86 @@
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class SkeletonUtilityKinematicShadow : MonoBehaviour {
|
||||
public bool hideShadow = true;
|
||||
public Transform parent;
|
||||
Dictionary<Transform, Transform> shadowTable;
|
||||
GameObject shadowRoot;
|
||||
namespace Spine.Unity.Modules {
|
||||
public class SkeletonUtilityKinematicShadow : MonoBehaviour {
|
||||
public bool hideShadow = true;
|
||||
public Transform parent;
|
||||
Dictionary<Transform, Transform> shadowTable;
|
||||
GameObject shadowRoot;
|
||||
|
||||
void Start () {
|
||||
shadowRoot = (GameObject)Instantiate(gameObject);
|
||||
if (hideShadow)
|
||||
shadowRoot.hideFlags = HideFlags.HideInHierarchy;
|
||||
void Start () {
|
||||
shadowRoot = (GameObject)Instantiate(gameObject);
|
||||
if (hideShadow)
|
||||
shadowRoot.hideFlags = HideFlags.HideInHierarchy;
|
||||
|
||||
if (parent == null)
|
||||
shadowRoot.transform.parent = transform.root;
|
||||
else
|
||||
shadowRoot.transform.parent = parent;
|
||||
if (parent == null)
|
||||
shadowRoot.transform.parent = transform.root;
|
||||
else
|
||||
shadowRoot.transform.parent = parent;
|
||||
|
||||
shadowTable = new Dictionary<Transform, Transform>();
|
||||
shadowTable = new Dictionary<Transform, Transform>();
|
||||
|
||||
Destroy(shadowRoot.GetComponent<SkeletonUtilityKinematicShadow>());
|
||||
Destroy(shadowRoot.GetComponent<SkeletonUtilityKinematicShadow>());
|
||||
|
||||
shadowRoot.transform.position = transform.position;
|
||||
shadowRoot.transform.rotation = transform.rotation;
|
||||
shadowRoot.transform.position = transform.position;
|
||||
shadowRoot.transform.rotation = transform.rotation;
|
||||
|
||||
Vector3 scaleRef = transform.TransformPoint(Vector3.right);
|
||||
float scale = Vector3.Distance(transform.position, scaleRef);
|
||||
shadowRoot.transform.localScale = Vector3.one;
|
||||
Vector3 scaleRef = transform.TransformPoint(Vector3.right);
|
||||
float scale = Vector3.Distance(transform.position, scaleRef);
|
||||
shadowRoot.transform.localScale = Vector3.one;
|
||||
|
||||
var shadowJoints = shadowRoot.GetComponentsInChildren<Joint>();
|
||||
foreach (Joint j in shadowJoints) {
|
||||
j.connectedAnchor *= scale;
|
||||
}
|
||||
var shadowJoints = shadowRoot.GetComponentsInChildren<Joint>();
|
||||
foreach (Joint j in shadowJoints) {
|
||||
j.connectedAnchor *= scale;
|
||||
}
|
||||
|
||||
var joints = GetComponentsInChildren<Joint>();
|
||||
foreach (var j in joints)
|
||||
Destroy(j);
|
||||
var joints = GetComponentsInChildren<Joint>();
|
||||
foreach (var j in joints)
|
||||
Destroy(j);
|
||||
|
||||
var rbs = GetComponentsInChildren<Rigidbody>();
|
||||
foreach (var rb in rbs)
|
||||
Destroy(rb);
|
||||
var rbs = GetComponentsInChildren<Rigidbody>();
|
||||
foreach (var rb in rbs)
|
||||
Destroy(rb);
|
||||
|
||||
var colliders = GetComponentsInChildren<Collider>();
|
||||
foreach (var c in colliders)
|
||||
Destroy(c);
|
||||
var colliders = GetComponentsInChildren<Collider>();
|
||||
foreach (var c in colliders)
|
||||
Destroy(c);
|
||||
|
||||
|
||||
//match by bone name
|
||||
var shadowBones = shadowRoot.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
var bones = GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
//match by bone name
|
||||
var shadowBones = shadowRoot.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
var bones = GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
|
||||
//build bone lookup
|
||||
foreach (var b in bones) {
|
||||
if (b.gameObject == gameObject)
|
||||
continue;
|
||||
|
||||
foreach (var sb in shadowBones) {
|
||||
if (sb.GetComponent<Rigidbody>() == null)
|
||||
//build bone lookup
|
||||
foreach (var b in bones) {
|
||||
if (b.gameObject == gameObject)
|
||||
continue;
|
||||
|
||||
if (sb.boneName == b.boneName) {
|
||||
shadowTable.Add(sb.transform, b.transform);
|
||||
break;
|
||||
foreach (var sb in shadowBones) {
|
||||
if (sb.GetComponent<Rigidbody>() == null)
|
||||
continue;
|
||||
|
||||
if (sb.boneName == b.boneName) {
|
||||
shadowTable.Add(sb.transform, b.transform);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var b in shadowBones)
|
||||
Destroy(b);
|
||||
}
|
||||
|
||||
foreach (var b in shadowBones)
|
||||
Destroy(b);
|
||||
}
|
||||
void FixedUpdate () {
|
||||
shadowRoot.GetComponent<Rigidbody>().MovePosition(transform.position);
|
||||
shadowRoot.GetComponent<Rigidbody>().MoveRotation(transform.rotation);
|
||||
|
||||
void FixedUpdate () {
|
||||
shadowRoot.GetComponent<Rigidbody>().MovePosition(transform.position);
|
||||
shadowRoot.GetComponent<Rigidbody>().MoveRotation(transform.rotation);
|
||||
|
||||
foreach (var pair in shadowTable) {
|
||||
pair.Value.localPosition = pair.Key.localPosition;
|
||||
pair.Value.localRotation = pair.Key.localRotation;
|
||||
foreach (var pair in shadowTable) {
|
||||
pair.Value.localPosition = pair.Key.localPosition;
|
||||
pair.Value.localRotation = pair.Key.localRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,170 +30,198 @@
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Spine;
|
||||
|
||||
public class SpriteAttacher : MonoBehaviour {
|
||||
namespace Spine.Unity.Modules {
|
||||
public class SpriteAttacher : MonoBehaviour {
|
||||
public bool attachOnStart = true;
|
||||
public bool keepLoaderInMemory = true;
|
||||
|
||||
public Sprite sprite;
|
||||
|
||||
public bool attachOnStart = true;
|
||||
public bool keepLoaderInMemory = true;
|
||||
[SpineSlot]
|
||||
public string slot;
|
||||
|
||||
public Sprite sprite;
|
||||
private SpriteAttachmentLoader loader;
|
||||
private RegionAttachment attachment;
|
||||
|
||||
[SpineSlot]
|
||||
public string slot;
|
||||
|
||||
private SpriteAttachmentLoader loader;
|
||||
private RegionAttachment attachment;
|
||||
|
||||
void Start () {
|
||||
if (attachOnStart)
|
||||
Attach();
|
||||
}
|
||||
|
||||
public void Attach () {
|
||||
var skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||
|
||||
if (loader == null)
|
||||
//create loader instance, tell it what sprite and shader to use
|
||||
loader = new SpriteAttachmentLoader(sprite, Shader.Find("Spine/Skeleton"));
|
||||
|
||||
if (attachment == null)
|
||||
attachment = loader.NewRegionAttachment(null, sprite.name, "");
|
||||
|
||||
skeletonRenderer.skeleton.FindSlot(slot).Attachment = attachment;
|
||||
|
||||
if (!keepLoaderInMemory)
|
||||
loader = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class SpriteAttachmentLoader : AttachmentLoader {
|
||||
|
||||
//TODO: Memory cleanup functions
|
||||
|
||||
//IMPORTANT: Make sure you clear this when you don't need it anymore. Goodluck.
|
||||
public static Dictionary<int, AtlasRegion> atlasTable = new Dictionary<int, AtlasRegion>();
|
||||
|
||||
//Shouldn't need to clear this, should just prevent redoing premultiply alpha pass on packed atlases
|
||||
public static List<int> premultipliedAtlasIds = new List<int>();
|
||||
|
||||
Sprite sprite;
|
||||
Shader shader;
|
||||
|
||||
public SpriteAttachmentLoader (Sprite sprite, Shader shader) {
|
||||
|
||||
if (sprite.packed && sprite.packingMode == SpritePackingMode.Tight) {
|
||||
Debug.LogError("Tight Packer Policy not supported yet!");
|
||||
return;
|
||||
void Start () {
|
||||
if (attachOnStart)
|
||||
Attach();
|
||||
}
|
||||
|
||||
this.sprite = sprite;
|
||||
this.shader = shader;
|
||||
public void Attach () {
|
||||
var skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||
|
||||
Texture2D tex = sprite.texture;
|
||||
//premultiply texture if it hasn't been yet
|
||||
int instanceId = tex.GetInstanceID();
|
||||
if (!premultipliedAtlasIds.Contains(instanceId)) {
|
||||
try {
|
||||
var colors = tex.GetPixels();
|
||||
Color c;
|
||||
float a;
|
||||
for (int i = 0; i < colors.Length; i++) {
|
||||
c = colors[i];
|
||||
a = c.a;
|
||||
c.r *= a;
|
||||
c.g *= a;
|
||||
c.b *= a;
|
||||
colors[i] = c;
|
||||
if (loader == null)
|
||||
//create loader instance, tell it what sprite and shader to use
|
||||
loader = new SpriteAttachmentLoader(sprite, Shader.Find("Spine/Skeleton"));
|
||||
|
||||
if (attachment == null)
|
||||
attachment = loader.NewRegionAttachment(null, sprite.name, "");
|
||||
|
||||
skeletonRenderer.skeleton.FindSlot(slot).Attachment = attachment;
|
||||
|
||||
if (!keepLoaderInMemory)
|
||||
loader = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class SpriteAttachmentLoader : AttachmentLoader {
|
||||
|
||||
//TODO: Memory cleanup functions
|
||||
|
||||
//IMPORTANT: Make sure you clear this when you don't need it anymore. Goodluck.
|
||||
public static Dictionary<int, AtlasRegion> atlasTable = new Dictionary<int, AtlasRegion>();
|
||||
|
||||
//Shouldn't need to clear this, should just prevent redoing premultiply alpha pass on packed atlases
|
||||
public static List<int> premultipliedAtlasIds = new List<int>();
|
||||
|
||||
Sprite sprite;
|
||||
Shader shader;
|
||||
|
||||
public SpriteAttachmentLoader (Sprite sprite, Shader shader) {
|
||||
|
||||
if (sprite.packed && sprite.packingMode == SpritePackingMode.Tight) {
|
||||
Debug.LogError("Tight Packer Policy not supported yet!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.sprite = sprite;
|
||||
this.shader = shader;
|
||||
|
||||
Texture2D tex = sprite.texture;
|
||||
//premultiply texture if it hasn't been yet
|
||||
int instanceId = tex.GetInstanceID();
|
||||
if (!premultipliedAtlasIds.Contains(instanceId)) {
|
||||
try {
|
||||
var colors = tex.GetPixels();
|
||||
Color c;
|
||||
float a;
|
||||
for (int i = 0; i < colors.Length; i++) {
|
||||
c = colors[i];
|
||||
a = c.a;
|
||||
c.r *= a;
|
||||
c.g *= a;
|
||||
c.b *= a;
|
||||
colors[i] = c;
|
||||
}
|
||||
|
||||
tex.SetPixels(colors);
|
||||
tex.Apply();
|
||||
|
||||
premultipliedAtlasIds.Add(instanceId);
|
||||
} catch {
|
||||
//texture is not readable! Can't pre-multiply it, you're on your own.
|
||||
}
|
||||
|
||||
tex.SetPixels(colors);
|
||||
tex.Apply();
|
||||
|
||||
premultipliedAtlasIds.Add(instanceId);
|
||||
} catch {
|
||||
//texture is not readable! Can't pre-multiply it, you're on your own.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) {
|
||||
RegionAttachment attachment = new RegionAttachment(name);
|
||||
public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) {
|
||||
RegionAttachment attachment = new RegionAttachment(name);
|
||||
|
||||
Texture2D tex = sprite.texture;
|
||||
int instanceId = tex.GetInstanceID();
|
||||
AtlasRegion atlasRegion;
|
||||
Texture2D tex = sprite.texture;
|
||||
int instanceId = tex.GetInstanceID();
|
||||
AtlasRegion atlasRegion;
|
||||
|
||||
//check cache first
|
||||
if (atlasTable.ContainsKey(instanceId)) {
|
||||
atlasRegion = atlasTable[instanceId];
|
||||
} else {
|
||||
//Setup new material
|
||||
Material mat = new Material(shader);
|
||||
//check cache first
|
||||
if (atlasTable.ContainsKey(instanceId)) {
|
||||
atlasRegion = atlasTable[instanceId];
|
||||
} else {
|
||||
//Setup new material
|
||||
Material mat = new Material(shader);
|
||||
if (sprite.packed)
|
||||
mat.name = "Unity Packed Sprite Material";
|
||||
else
|
||||
mat.name = sprite.name + " Sprite Material";
|
||||
mat.mainTexture = tex;
|
||||
|
||||
//create faux-region to play nice with SkeletonRenderer
|
||||
atlasRegion = new AtlasRegion();
|
||||
AtlasPage page = new AtlasPage();
|
||||
page.rendererObject = mat;
|
||||
atlasRegion.page = page;
|
||||
|
||||
//cache it
|
||||
atlasTable[instanceId] = atlasRegion;
|
||||
}
|
||||
|
||||
Rect texRect = sprite.textureRect;
|
||||
|
||||
//normalize rect to UV space of packed atlas
|
||||
texRect.x = Mathf.InverseLerp(0, tex.width, texRect.x);
|
||||
texRect.y = Mathf.InverseLerp(0, tex.height, texRect.y);
|
||||
texRect.width = Mathf.InverseLerp(0, tex.width, texRect.width);
|
||||
texRect.height = Mathf.InverseLerp(0, tex.height, texRect.height);
|
||||
|
||||
Bounds bounds = sprite.bounds;
|
||||
Vector3 size = bounds.size;
|
||||
|
||||
//TODO: make sure this rotation thing actually works
|
||||
bool rotated = false;
|
||||
if (sprite.packed)
|
||||
mat.name = "Unity Packed Sprite Material";
|
||||
else
|
||||
mat.name = sprite.name + " Sprite Material";
|
||||
mat.mainTexture = tex;
|
||||
rotated = sprite.packingRotation == SpritePackingRotation.Any;
|
||||
|
||||
//create faux-region to play nice with SkeletonRenderer
|
||||
atlasRegion = new AtlasRegion();
|
||||
AtlasPage page = new AtlasPage();
|
||||
page.rendererObject = mat;
|
||||
atlasRegion.page = page;
|
||||
//do some math and assign UVs and sizes
|
||||
attachment.SetUVs(texRect.xMin, texRect.yMax, texRect.xMax, texRect.yMin, rotated);
|
||||
attachment.RendererObject = atlasRegion;
|
||||
attachment.SetColor(Color.white);
|
||||
attachment.ScaleX = 1;
|
||||
attachment.ScaleY = 1;
|
||||
attachment.RegionOffsetX = sprite.rect.width * (0.5f - Mathf.InverseLerp(bounds.min.x, bounds.max.x, 0)) / sprite.pixelsPerUnit;
|
||||
attachment.RegionOffsetY = sprite.rect.height * (0.5f - Mathf.InverseLerp(bounds.min.y, bounds.max.y, 0)) / sprite.pixelsPerUnit;
|
||||
attachment.Width = size.x;
|
||||
attachment.Height = size.y;
|
||||
attachment.RegionWidth = size.x;
|
||||
attachment.RegionHeight = size.y;
|
||||
attachment.RegionOriginalWidth = size.x;
|
||||
attachment.RegionOriginalHeight = size.y;
|
||||
attachment.UpdateOffset();
|
||||
|
||||
//cache it
|
||||
atlasTable[instanceId] = atlasRegion;
|
||||
return attachment;
|
||||
}
|
||||
|
||||
Rect texRect = sprite.textureRect;
|
||||
public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) {
|
||||
//TODO: Unity 5 only
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
//normalize rect to UV space of packed atlas
|
||||
texRect.x = Mathf.InverseLerp(0, tex.width, texRect.x);
|
||||
texRect.y = Mathf.InverseLerp(0, tex.height, texRect.y);
|
||||
texRect.width = Mathf.InverseLerp(0, tex.width, texRect.width);
|
||||
texRect.height = Mathf.InverseLerp(0, tex.height, texRect.height);
|
||||
public WeightedMeshAttachment NewWeightedMeshAttachment(Skin skin, string name, string path) {
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
Bounds bounds = sprite.bounds;
|
||||
Vector3 size = bounds.size;
|
||||
|
||||
//TODO: make sure this rotation thing actually works
|
||||
bool rotated = false;
|
||||
if (sprite.packed)
|
||||
rotated = sprite.packingRotation == SpritePackingRotation.Any;
|
||||
|
||||
//do some math and assign UVs and sizes
|
||||
attachment.SetUVs(texRect.xMin, texRect.yMax, texRect.xMax, texRect.yMin, rotated);
|
||||
attachment.RendererObject = atlasRegion;
|
||||
attachment.SetColor(Color.white);
|
||||
attachment.ScaleX = 1;
|
||||
attachment.ScaleY = 1;
|
||||
attachment.RegionOffsetX = sprite.rect.width * (0.5f - Mathf.InverseLerp(bounds.min.x, bounds.max.x, 0)) / sprite.pixelsPerUnit;
|
||||
attachment.RegionOffsetY = sprite.rect.height * (0.5f - Mathf.InverseLerp(bounds.min.y, bounds.max.y, 0)) / sprite.pixelsPerUnit;
|
||||
attachment.Width = size.x;
|
||||
attachment.Height = size.y;
|
||||
attachment.RegionWidth = size.x;
|
||||
attachment.RegionHeight = size.y;
|
||||
attachment.RegionOriginalWidth = size.x;
|
||||
attachment.RegionOriginalHeight = size.y;
|
||||
attachment.UpdateOffset();
|
||||
|
||||
return attachment;
|
||||
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) {
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) {
|
||||
//TODO: Unity 5 only
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
public static class SpriteAttachmentExtensions {
|
||||
public static Attachment AttachUnitySprite (this Skeleton skeleton, string slotName, Sprite sprite, string shaderName = "Spine/Skeleton") {
|
||||
var att = sprite.ToRegionAttachment(shaderName);
|
||||
skeleton.FindSlot(slotName).Attachment = att;
|
||||
return att;
|
||||
}
|
||||
|
||||
public WeightedMeshAttachment NewWeightedMeshAttachment(Skin skin, string name, string path) {
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
public static Attachment AddUnitySprite (this SkeletonData skeletonData, string slotName, Sprite sprite, string skinName = "", string shaderName = "Spine/Skeleton") {
|
||||
var att = sprite.ToRegionAttachment(shaderName);
|
||||
|
||||
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) {
|
||||
throw new System.NotImplementedException();
|
||||
var slotIndex = skeletonData.FindSlotIndex(slotName);
|
||||
Skin skin = skeletonData.defaultSkin;
|
||||
if (skinName != "")
|
||||
skin = skeletonData.FindSkin(skinName);
|
||||
|
||||
skin.AddAttachment(slotIndex, att.Name, att);
|
||||
|
||||
return att;
|
||||
}
|
||||
|
||||
public static RegionAttachment ToRegionAttachment (this Sprite sprite, string shaderName = "Spine/Skeleton") {
|
||||
var loader = new SpriteAttachmentLoader(sprite, Shader.Find(shaderName));
|
||||
var att = loader.NewRegionAttachment(null, sprite.name, "");
|
||||
loader = null;
|
||||
return att;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -35,131 +35,132 @@ using UnityEngine;
|
||||
using Spine;
|
||||
|
||||
// TODO: handle TPackerCW flip mode (probably not swap uv horizontaly)
|
||||
namespace Spine.Unity.TK2D {
|
||||
public class SpriteCollectionAttachmentLoader : AttachmentLoader {
|
||||
private tk2dSpriteCollectionData sprites;
|
||||
private float u, v, u2, v2;
|
||||
private bool regionRotated;
|
||||
private float regionOriginalWidth, regionOriginalHeight;
|
||||
private float regionWidth, regionHeight;
|
||||
private float regionOffsetX, regionOffsetY;
|
||||
private Material material;
|
||||
|
||||
public class SpriteCollectionAttachmentLoader : AttachmentLoader {
|
||||
private tk2dSpriteCollectionData sprites;
|
||||
private float u, v, u2, v2;
|
||||
private bool regionRotated;
|
||||
private float regionOriginalWidth, regionOriginalHeight;
|
||||
private float regionWidth, regionHeight;
|
||||
private float regionOffsetX, regionOffsetY;
|
||||
private Material material;
|
||||
|
||||
public SpriteCollectionAttachmentLoader (tk2dSpriteCollectionData sprites) {
|
||||
if (sprites == null)
|
||||
throw new ArgumentNullException("sprites cannot be null.");
|
||||
this.sprites = sprites;
|
||||
}
|
||||
|
||||
private void ProcessSpriteDefinition (String name) {
|
||||
// Strip folder names.
|
||||
int index = name.LastIndexOfAny(new char[] {'/', '\\'});
|
||||
if (index != -1)
|
||||
name = name.Substring(index + 1);
|
||||
|
||||
tk2dSpriteDefinition def = sprites.inst.GetSpriteDefinition(name);
|
||||
|
||||
if (def == null) {
|
||||
Debug.Log("Sprite not found in atlas: " + name, sprites);
|
||||
throw new Exception("Sprite not found in atlas: " + name);
|
||||
public SpriteCollectionAttachmentLoader (tk2dSpriteCollectionData sprites) {
|
||||
if (sprites == null)
|
||||
throw new ArgumentNullException("sprites cannot be null.");
|
||||
this.sprites = sprites;
|
||||
}
|
||||
if (def.complexGeometry)
|
||||
throw new NotImplementedException("Complex geometry is not supported: " + name);
|
||||
if (def.flipped == tk2dSpriteDefinition.FlipMode.TPackerCW)
|
||||
throw new NotImplementedException("Only 2D Toolkit atlases are supported: " + name);
|
||||
|
||||
Vector2 minTexCoords = Vector2.one, maxTexCoords = Vector2.zero;
|
||||
for (int i = 0; i < def.uvs.Length; ++i) {
|
||||
Vector2 uv = def.uvs[i];
|
||||
minTexCoords = Vector2.Min(minTexCoords, uv);
|
||||
maxTexCoords = Vector2.Max(maxTexCoords, uv);
|
||||
private void ProcessSpriteDefinition (String name) {
|
||||
// Strip folder names.
|
||||
int index = name.LastIndexOfAny(new char[] {'/', '\\'});
|
||||
if (index != -1)
|
||||
name = name.Substring(index + 1);
|
||||
|
||||
tk2dSpriteDefinition def = sprites.inst.GetSpriteDefinition(name);
|
||||
|
||||
if (def == null) {
|
||||
Debug.Log("Sprite not found in atlas: " + name, sprites);
|
||||
throw new Exception("Sprite not found in atlas: " + name);
|
||||
}
|
||||
if (def.complexGeometry)
|
||||
throw new NotImplementedException("Complex geometry is not supported: " + name);
|
||||
if (def.flipped == tk2dSpriteDefinition.FlipMode.TPackerCW)
|
||||
throw new NotImplementedException("Only 2D Toolkit atlases are supported: " + name);
|
||||
|
||||
Vector2 minTexCoords = Vector2.one, maxTexCoords = Vector2.zero;
|
||||
for (int i = 0; i < def.uvs.Length; ++i) {
|
||||
Vector2 uv = def.uvs[i];
|
||||
minTexCoords = Vector2.Min(minTexCoords, uv);
|
||||
maxTexCoords = Vector2.Max(maxTexCoords, uv);
|
||||
}
|
||||
regionRotated = def.flipped == tk2dSpriteDefinition.FlipMode.Tk2d;
|
||||
if (regionRotated) {
|
||||
float temp = minTexCoords.x;
|
||||
minTexCoords.x = maxTexCoords.x;
|
||||
maxTexCoords.x = temp;
|
||||
}
|
||||
u = minTexCoords.x;
|
||||
v = maxTexCoords.y;
|
||||
u2 = maxTexCoords.x;
|
||||
v2 = minTexCoords.y;
|
||||
|
||||
regionOriginalWidth = (int)(def.untrimmedBoundsData[1].x / def.texelSize.x);
|
||||
regionOriginalHeight = (int)(def.untrimmedBoundsData[1].y / def.texelSize.y);
|
||||
|
||||
regionWidth = (int)(def.boundsData[1].x / def.texelSize.x);
|
||||
regionHeight = (int)(def.boundsData[1].y / def.texelSize.y);
|
||||
|
||||
float x0 = def.untrimmedBoundsData[0].x - def.untrimmedBoundsData[1].x / 2;
|
||||
float x1 = def.boundsData[0].x - def.boundsData[1].x / 2;
|
||||
regionOffsetX = (int)((x1 - x0) / def.texelSize.x);
|
||||
|
||||
float y0 = def.untrimmedBoundsData[0].y - def.untrimmedBoundsData[1].y / 2;
|
||||
float y1 = def.boundsData[0].y - def.boundsData[1].y / 2;
|
||||
regionOffsetY = (int)((y1 - y0) / def.texelSize.y);
|
||||
|
||||
material = def.materialInst;
|
||||
}
|
||||
regionRotated = def.flipped == tk2dSpriteDefinition.FlipMode.Tk2d;
|
||||
if (regionRotated) {
|
||||
float temp = minTexCoords.x;
|
||||
minTexCoords.x = maxTexCoords.x;
|
||||
maxTexCoords.x = temp;
|
||||
|
||||
public RegionAttachment NewRegionAttachment (Skin skin, String name, String path) {
|
||||
ProcessSpriteDefinition(path);
|
||||
|
||||
RegionAttachment region = new RegionAttachment(name);
|
||||
region.Path = path;
|
||||
region.RendererObject = material;
|
||||
region.SetUVs(u, v, u2, v2, regionRotated);
|
||||
region.RegionOriginalWidth = regionOriginalWidth;
|
||||
region.RegionOriginalHeight = regionOriginalHeight;
|
||||
region.RegionWidth = regionWidth;
|
||||
region.RegionHeight = regionHeight;
|
||||
region.RegionOffsetX = regionOffsetX;
|
||||
region.RegionOffsetY = regionOffsetY;
|
||||
return region;
|
||||
}
|
||||
u = minTexCoords.x;
|
||||
v = maxTexCoords.y;
|
||||
u2 = maxTexCoords.x;
|
||||
v2 = minTexCoords.y;
|
||||
|
||||
regionOriginalWidth = (int)(def.untrimmedBoundsData[1].x / def.texelSize.x);
|
||||
regionOriginalHeight = (int)(def.untrimmedBoundsData[1].y / def.texelSize.y);
|
||||
|
||||
regionWidth = (int)(def.boundsData[1].x / def.texelSize.x);
|
||||
regionHeight = (int)(def.boundsData[1].y / def.texelSize.y);
|
||||
|
||||
float x0 = def.untrimmedBoundsData[0].x - def.untrimmedBoundsData[1].x / 2;
|
||||
float x1 = def.boundsData[0].x - def.boundsData[1].x / 2;
|
||||
regionOffsetX = (int)((x1 - x0) / def.texelSize.x);
|
||||
|
||||
float y0 = def.untrimmedBoundsData[0].y - def.untrimmedBoundsData[1].y / 2;
|
||||
float y1 = def.boundsData[0].y - def.boundsData[1].y / 2;
|
||||
regionOffsetY = (int)((y1 - y0) / def.texelSize.y);
|
||||
public MeshAttachment NewMeshAttachment (Skin skin, String name, String path) {
|
||||
ProcessSpriteDefinition(path);
|
||||
|
||||
material = def.materialInst;
|
||||
}
|
||||
MeshAttachment mesh = new MeshAttachment(name);
|
||||
mesh.Path = path;
|
||||
mesh.RendererObject = material;
|
||||
mesh.RegionU = u;
|
||||
mesh.RegionV = v;
|
||||
mesh.RegionU2 = u2;
|
||||
mesh.RegionV2 = v2;
|
||||
mesh.RegionRotate = regionRotated;
|
||||
mesh.RegionOriginalWidth = regionOriginalWidth;
|
||||
mesh.RegionOriginalHeight = regionOriginalHeight;
|
||||
mesh.RegionWidth = regionWidth;
|
||||
mesh.RegionHeight = regionHeight;
|
||||
mesh.RegionOffsetX = regionOffsetX;
|
||||
mesh.RegionOffsetY = regionOffsetY;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
public RegionAttachment NewRegionAttachment (Skin skin, String name, String path) {
|
||||
ProcessSpriteDefinition(path);
|
||||
|
||||
RegionAttachment region = new RegionAttachment(name);
|
||||
region.Path = path;
|
||||
region.RendererObject = material;
|
||||
region.SetUVs(u, v, u2, v2, regionRotated);
|
||||
region.RegionOriginalWidth = regionOriginalWidth;
|
||||
region.RegionOriginalHeight = regionOriginalHeight;
|
||||
region.RegionWidth = regionWidth;
|
||||
region.RegionHeight = regionHeight;
|
||||
region.RegionOffsetX = regionOffsetX;
|
||||
region.RegionOffsetY = regionOffsetY;
|
||||
return region;
|
||||
}
|
||||
|
||||
public MeshAttachment NewMeshAttachment (Skin skin, String name, String path) {
|
||||
ProcessSpriteDefinition(path);
|
||||
|
||||
MeshAttachment mesh = new MeshAttachment(name);
|
||||
mesh.Path = path;
|
||||
mesh.RendererObject = material;
|
||||
mesh.RegionU = u;
|
||||
mesh.RegionV = v;
|
||||
mesh.RegionU2 = u2;
|
||||
mesh.RegionV2 = v2;
|
||||
mesh.RegionRotate = regionRotated;
|
||||
mesh.RegionOriginalWidth = regionOriginalWidth;
|
||||
mesh.RegionOriginalHeight = regionOriginalHeight;
|
||||
mesh.RegionWidth = regionWidth;
|
||||
mesh.RegionHeight = regionHeight;
|
||||
mesh.RegionOffsetX = regionOffsetX;
|
||||
mesh.RegionOffsetY = regionOffsetY;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
public WeightedMeshAttachment NewWeightedMeshAttachment (Skin skin, String name, String path) {
|
||||
ProcessSpriteDefinition(path);
|
||||
|
||||
WeightedMeshAttachment mesh = new WeightedMeshAttachment(name);
|
||||
mesh.Path = path;
|
||||
mesh.RendererObject = material;
|
||||
mesh.RegionU = u;
|
||||
mesh.RegionV = v;
|
||||
mesh.RegionU2 = u2;
|
||||
mesh.RegionV2 = v2;
|
||||
mesh.RegionRotate = regionRotated;
|
||||
mesh.RegionOriginalWidth = regionOriginalWidth;
|
||||
mesh.RegionOriginalHeight = regionOriginalHeight;
|
||||
mesh.RegionWidth = regionWidth;
|
||||
mesh.RegionHeight = regionHeight;
|
||||
mesh.RegionOffsetX = regionOffsetX;
|
||||
mesh.RegionOffsetY = regionOffsetY;
|
||||
return mesh;
|
||||
}
|
||||
public WeightedMeshAttachment NewWeightedMeshAttachment (Skin skin, String name, String path) {
|
||||
ProcessSpriteDefinition(path);
|
||||
|
||||
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) {
|
||||
return new BoundingBoxAttachment(name);
|
||||
WeightedMeshAttachment mesh = new WeightedMeshAttachment(name);
|
||||
mesh.Path = path;
|
||||
mesh.RendererObject = material;
|
||||
mesh.RegionU = u;
|
||||
mesh.RegionV = v;
|
||||
mesh.RegionU2 = u2;
|
||||
mesh.RegionV2 = v2;
|
||||
mesh.RegionRotate = regionRotated;
|
||||
mesh.RegionOriginalWidth = regionOriginalWidth;
|
||||
mesh.RegionOriginalHeight = regionOriginalHeight;
|
||||
mesh.RegionWidth = regionWidth;
|
||||
mesh.RegionHeight = regionHeight;
|
||||
mesh.RegionOffsetX = regionOffsetX;
|
||||
mesh.RegionOffsetY = regionOffsetY;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) {
|
||||
return new BoundingBoxAttachment(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -30,163 +30,165 @@
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Spine;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Spine/SkeletonAnimation")]
|
||||
[HelpURL("http://esotericsoftware.com/spine-unity-documentation#Controlling-Animation")]
|
||||
public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
|
||||
namespace Spine.Unity {
|
||||
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Spine/SkeletonAnimation")]
|
||||
[HelpURL("http://esotericsoftware.com/spine-unity-documentation#Controlling-Animation")]
|
||||
public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
|
||||
|
||||
/// <summary>
|
||||
/// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it.
|
||||
/// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start</summary>
|
||||
public Spine.AnimationState state;
|
||||
/// <summary>
|
||||
/// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it.
|
||||
/// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start</summary>
|
||||
public Spine.AnimationState state;
|
||||
|
||||
public event UpdateBonesDelegate UpdateLocal {
|
||||
add { _UpdateLocal += value; }
|
||||
remove { _UpdateLocal -= value; }
|
||||
}
|
||||
|
||||
public event UpdateBonesDelegate UpdateWorld {
|
||||
add { _UpdateWorld += value; }
|
||||
remove { _UpdateWorld -= value; }
|
||||
}
|
||||
|
||||
public event UpdateBonesDelegate UpdateComplete {
|
||||
add { _UpdateComplete += value; }
|
||||
remove { _UpdateComplete -= value; }
|
||||
}
|
||||
|
||||
protected event UpdateBonesDelegate _UpdateLocal;
|
||||
protected event UpdateBonesDelegate _UpdateWorld;
|
||||
protected event UpdateBonesDelegate _UpdateComplete;
|
||||
|
||||
/// <summary>Gets the skeleton.</summary>
|
||||
public Skeleton Skeleton {
|
||||
get {
|
||||
this.Initialize(false);
|
||||
return this.skeleton;
|
||||
public event UpdateBonesDelegate UpdateLocal {
|
||||
add { _UpdateLocal += value; }
|
||||
remove { _UpdateLocal -= value; }
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
[SpineAnimation]
|
||||
private String _animationName;
|
||||
public event UpdateBonesDelegate UpdateWorld {
|
||||
add { _UpdateWorld += value; }
|
||||
remove { _UpdateWorld -= value; }
|
||||
}
|
||||
|
||||
public String AnimationName {
|
||||
get {
|
||||
if (!valid) {
|
||||
Debug.LogWarning("You tried access AnimationName but the SkeletonAnimation was not valid. Try checking your Skeleton Data for errors.");
|
||||
return null;
|
||||
public event UpdateBonesDelegate UpdateComplete {
|
||||
add { _UpdateComplete += value; }
|
||||
remove { _UpdateComplete -= value; }
|
||||
}
|
||||
|
||||
protected event UpdateBonesDelegate _UpdateLocal;
|
||||
protected event UpdateBonesDelegate _UpdateWorld;
|
||||
protected event UpdateBonesDelegate _UpdateComplete;
|
||||
|
||||
/// <summary>Gets the skeleton.</summary>
|
||||
public Skeleton Skeleton {
|
||||
get {
|
||||
this.Initialize(false);
|
||||
return this.skeleton;
|
||||
}
|
||||
|
||||
TrackEntry entry = state.GetCurrent(0);
|
||||
return entry == null ? null : entry.Animation.Name;
|
||||
}
|
||||
set {
|
||||
if (_animationName == value)
|
||||
|
||||
[SerializeField]
|
||||
[SpineAnimation]
|
||||
private String _animationName;
|
||||
|
||||
public String AnimationName {
|
||||
get {
|
||||
if (!valid) {
|
||||
Debug.LogWarning("You tried access AnimationName but the SkeletonAnimation was not valid. Try checking your Skeleton Data for errors.");
|
||||
return null;
|
||||
}
|
||||
|
||||
TrackEntry entry = state.GetCurrent(0);
|
||||
return entry == null ? null : entry.Animation.Name;
|
||||
}
|
||||
set {
|
||||
if (_animationName == value)
|
||||
return;
|
||||
_animationName = value;
|
||||
|
||||
if (!valid) {
|
||||
Debug.LogWarning("You tried to change AnimationName but the SkeletonAnimation was not valid. Try checking your Skeleton Data for errors.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == null || value.Length == 0)
|
||||
state.ClearTrack(0);
|
||||
else
|
||||
state.SetAnimation(0, value, loop);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Whether or not an animation should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.</summary>
|
||||
#if UNITY_5
|
||||
[Tooltip("Whether or not an animation should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.")]
|
||||
#endif
|
||||
public bool loop;
|
||||
|
||||
/// <summary>
|
||||
/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.</summary>
|
||||
/// <remarks>AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</remarks>
|
||||
#if UNITY_5
|
||||
[Tooltip("The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.")]
|
||||
#endif
|
||||
public float timeScale = 1;
|
||||
|
||||
#region Runtime Instantiation
|
||||
/// <summary>Adds and prepares a SkeletonAnimation component to a GameObject at runtime.</summary>
|
||||
/// <returns>The newly instantiated SkeletonAnimation</returns>
|
||||
public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) {
|
||||
return SkeletonRenderer.AddSpineComponent<SkeletonAnimation>(gameObject, skeletonDataAsset);
|
||||
}
|
||||
|
||||
/// <summary>Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it.</summary>
|
||||
/// <returns>The newly instantiated SkeletonAnimation component.</returns>
|
||||
public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset) {
|
||||
return SkeletonRenderer.NewSpineGameObject<SkeletonAnimation>(skeletonDataAsset);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override void Initialize (bool overwrite) {
|
||||
if (valid && !overwrite)
|
||||
return;
|
||||
_animationName = value;
|
||||
|
||||
if (!valid) {
|
||||
Debug.LogWarning("You tried to change AnimationName but the SkeletonAnimation was not valid. Try checking your Skeleton Data for errors.");
|
||||
|
||||
base.Initialize(overwrite);
|
||||
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!string.IsNullOrEmpty(_animationName)) {
|
||||
if (Application.isPlaying) {
|
||||
state.SetAnimation(0, _animationName, loop);
|
||||
} else {
|
||||
// Assume SkeletonAnimation is valid for skeletonData and skeleton. Checked above.
|
||||
var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName);
|
||||
if (animationObject != null)
|
||||
animationObject.Apply(skeleton, 0f, 0f, false, null);
|
||||
}
|
||||
Update(0);
|
||||
}
|
||||
|
||||
if (value == null || value.Length == 0)
|
||||
state.ClearTrack(0);
|
||||
else
|
||||
state.SetAnimation(0, value, loop);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Whether or not an animation should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.</summary>
|
||||
#if UNITY_5
|
||||
[Tooltip("Whether or not an animation should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.")]
|
||||
#endif
|
||||
public bool loop;
|
||||
|
||||
/// <summary>
|
||||
/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.</summary>
|
||||
/// <remarks>AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</remarks>
|
||||
#if UNITY_5
|
||||
[Tooltip("The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.")]
|
||||
#endif
|
||||
public float timeScale = 1;
|
||||
|
||||
#region Runtime Instantiation
|
||||
/// <summary>Adds and prepares a SkeletonAnimation component to a GameObject at runtime.</summary>
|
||||
/// <returns>The newly instantiated SkeletonAnimation</returns>
|
||||
public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) {
|
||||
return SkeletonRenderer.AddSpineComponent<SkeletonAnimation>(gameObject, skeletonDataAsset);
|
||||
}
|
||||
|
||||
/// <summary>Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it.</summary>
|
||||
/// <returns>The newly instantiated SkeletonAnimation component.</returns>
|
||||
public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset) {
|
||||
return SkeletonRenderer.NewSpineGameObject<SkeletonAnimation>(skeletonDataAsset);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override void Initialize (bool overwrite) {
|
||||
if (valid && !overwrite)
|
||||
return;
|
||||
|
||||
base.Initialize(overwrite);
|
||||
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!string.IsNullOrEmpty(_animationName)) {
|
||||
if (Application.isPlaying) {
|
||||
state.SetAnimation(0, _animationName, loop);
|
||||
} else {
|
||||
// Assume SkeletonAnimation is valid for skeletonData and skeleton. Checked above.
|
||||
var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName);
|
||||
if (animationObject != null)
|
||||
animationObject.Apply(skeleton, 0f, 0f, false, null);
|
||||
}
|
||||
Update(0);
|
||||
}
|
||||
#else
|
||||
if (!string.IsNullOrEmpty(_animationName)) {
|
||||
#else
|
||||
if (!string.IsNullOrEmpty(_animationName)) {
|
||||
state.SetAnimation(0, _animationName, loop);
|
||||
Update(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public virtual void Update () {
|
||||
Update(Time.deltaTime);
|
||||
}
|
||||
public virtual void Update () {
|
||||
Update(Time.deltaTime);
|
||||
}
|
||||
|
||||
public virtual void Update (float deltaTime) {
|
||||
if (!valid)
|
||||
return;
|
||||
public virtual void Update (float deltaTime) {
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
deltaTime *= timeScale;
|
||||
skeleton.Update(deltaTime);
|
||||
state.Update(deltaTime);
|
||||
state.Apply(skeleton);
|
||||
deltaTime *= timeScale;
|
||||
skeleton.Update(deltaTime);
|
||||
state.Update(deltaTime);
|
||||
state.Apply(skeleton);
|
||||
|
||||
if (_UpdateLocal != null)
|
||||
_UpdateLocal(this);
|
||||
if (_UpdateLocal != null)
|
||||
_UpdateLocal(this);
|
||||
|
||||
skeleton.UpdateWorldTransform();
|
||||
|
||||
if (_UpdateWorld != null) {
|
||||
_UpdateWorld(this);
|
||||
skeleton.UpdateWorldTransform();
|
||||
|
||||
if (_UpdateWorld != null) {
|
||||
_UpdateWorld(this);
|
||||
skeleton.UpdateWorldTransform();
|
||||
}
|
||||
|
||||
if (_UpdateComplete != null) {
|
||||
_UpdateComplete(this);
|
||||
}
|
||||
}
|
||||
|
||||
if (_UpdateComplete != null) {
|
||||
_UpdateComplete(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -5,161 +5,173 @@
|
||||
* Full irrevocable rights and permissions granted to Esoteric Software
|
||||
*****************************************************************************/
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Spine;
|
||||
|
||||
[RequireComponent(typeof(Animator))]
|
||||
public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation {
|
||||
namespace Spine.Unity {
|
||||
[RequireComponent(typeof(Animator))]
|
||||
public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation {
|
||||
|
||||
public enum MixMode { AlwaysMix, MixNext, SpineStyle }
|
||||
public MixMode[] layerMixModes = new MixMode[0];
|
||||
public enum MixMode { AlwaysMix, MixNext, SpineStyle }
|
||||
public MixMode[] layerMixModes = new MixMode[0];
|
||||
|
||||
public event UpdateBonesDelegate UpdateLocal {
|
||||
add { _UpdateLocal += value; }
|
||||
remove { _UpdateLocal -= value; }
|
||||
}
|
||||
|
||||
public event UpdateBonesDelegate UpdateWorld {
|
||||
add { _UpdateWorld += value; }
|
||||
remove { _UpdateWorld -= value; }
|
||||
}
|
||||
|
||||
public event UpdateBonesDelegate UpdateComplete {
|
||||
add { _UpdateComplete += value; }
|
||||
remove { _UpdateComplete -= value; }
|
||||
}
|
||||
|
||||
protected event UpdateBonesDelegate _UpdateLocal;
|
||||
protected event UpdateBonesDelegate _UpdateWorld;
|
||||
protected event UpdateBonesDelegate _UpdateComplete;
|
||||
|
||||
public Skeleton Skeleton {
|
||||
get {
|
||||
return this.skeleton;
|
||||
}
|
||||
}
|
||||
|
||||
readonly Dictionary<int, Spine.Animation> animationTable = new Dictionary<int, Spine.Animation>();
|
||||
readonly Dictionary<AnimationClip, int> clipNameHashCodeTable = new Dictionary<AnimationClip, int>();
|
||||
Animator animator;
|
||||
float lastTime;
|
||||
|
||||
public override void Initialize (bool overwrite) {
|
||||
if (valid && !overwrite)
|
||||
return;
|
||||
|
||||
base.Initialize(overwrite);
|
||||
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
animationTable.Clear();
|
||||
clipNameHashCodeTable.Clear();
|
||||
|
||||
var data = skeletonDataAsset.GetSkeletonData(true);
|
||||
|
||||
foreach (var a in data.Animations) {
|
||||
animationTable.Add(a.Name.GetHashCode(), a);
|
||||
public event UpdateBonesDelegate UpdateLocal {
|
||||
add { _UpdateLocal += value; }
|
||||
remove { _UpdateLocal -= value; }
|
||||
}
|
||||
|
||||
animator = GetComponent<Animator>();
|
||||
|
||||
lastTime = Time.time;
|
||||
}
|
||||
|
||||
void Update () {
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
if (layerMixModes.Length != animator.layerCount) {
|
||||
System.Array.Resize<MixMode>(ref layerMixModes, animator.layerCount);
|
||||
public event UpdateBonesDelegate UpdateWorld {
|
||||
add { _UpdateWorld += value; }
|
||||
remove { _UpdateWorld -= value; }
|
||||
}
|
||||
float deltaTime = Time.time - lastTime;
|
||||
|
||||
skeleton.Update(Time.deltaTime);
|
||||
public event UpdateBonesDelegate UpdateComplete {
|
||||
add { _UpdateComplete += value; }
|
||||
remove { _UpdateComplete -= value; }
|
||||
}
|
||||
|
||||
//apply
|
||||
int layerCount = animator.layerCount;
|
||||
protected event UpdateBonesDelegate _UpdateLocal;
|
||||
protected event UpdateBonesDelegate _UpdateWorld;
|
||||
protected event UpdateBonesDelegate _UpdateComplete;
|
||||
|
||||
for (int i = 0; i < layerCount; i++) {
|
||||
public Skeleton Skeleton {
|
||||
get {
|
||||
return this.skeleton;
|
||||
}
|
||||
}
|
||||
|
||||
float layerWeight = animator.GetLayerWeight(i);
|
||||
if (i == 0)
|
||||
layerWeight = 1;
|
||||
readonly Dictionary<int, Spine.Animation> animationTable = new Dictionary<int, Spine.Animation>();
|
||||
readonly Dictionary<AnimationClip, int> clipNameHashCodeTable = new Dictionary<AnimationClip, int>();
|
||||
Animator animator;
|
||||
float lastTime;
|
||||
|
||||
var stateInfo = animator.GetCurrentAnimatorStateInfo(i);
|
||||
var nextStateInfo = animator.GetNextAnimatorStateInfo(i);
|
||||
public override void Initialize (bool overwrite) {
|
||||
if (valid && !overwrite)
|
||||
return;
|
||||
|
||||
#if UNITY_5
|
||||
var clipInfo = animator.GetCurrentAnimatorClipInfo(i);
|
||||
var nextClipInfo = animator.GetNextAnimatorClipInfo(i);
|
||||
#else
|
||||
var clipInfo = animator.GetCurrentAnimationClipState(i);
|
||||
var nextClipInfo = animator.GetNextAnimationClipState(i);
|
||||
#endif
|
||||
MixMode mode = layerMixModes[i];
|
||||
base.Initialize(overwrite);
|
||||
|
||||
if (mode == MixMode.AlwaysMix) {
|
||||
//always use Mix instead of Applying the first non-zero weighted clip
|
||||
for (int c = 0; c < clipInfo.Length; c++) {
|
||||
var info = clipInfo[c];
|
||||
float weight = info.weight * layerWeight;
|
||||
if (weight == 0)
|
||||
continue;
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
float time = stateInfo.normalizedTime * info.clip.length;
|
||||
animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight);
|
||||
}
|
||||
#if UNITY_5
|
||||
if (nextStateInfo.fullPathHash != 0) {
|
||||
#else
|
||||
if (nextStateInfo.nameHash != 0) {
|
||||
#endif
|
||||
for (int c = 0; c < nextClipInfo.Length; c++) {
|
||||
var info = nextClipInfo[c];
|
||||
animationTable.Clear();
|
||||
clipNameHashCodeTable.Clear();
|
||||
|
||||
var data = skeletonDataAsset.GetSkeletonData(true);
|
||||
|
||||
foreach (var a in data.Animations) {
|
||||
animationTable.Add(a.Name.GetHashCode(), a);
|
||||
}
|
||||
|
||||
animator = GetComponent<Animator>();
|
||||
|
||||
lastTime = Time.time;
|
||||
}
|
||||
|
||||
void Update () {
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
if (layerMixModes.Length != animator.layerCount) {
|
||||
System.Array.Resize<MixMode>(ref layerMixModes, animator.layerCount);
|
||||
}
|
||||
float deltaTime = Time.time - lastTime;
|
||||
|
||||
skeleton.Update(Time.deltaTime);
|
||||
|
||||
//apply
|
||||
int layerCount = animator.layerCount;
|
||||
|
||||
for (int i = 0; i < layerCount; i++) {
|
||||
|
||||
float layerWeight = animator.GetLayerWeight(i);
|
||||
if (i == 0)
|
||||
layerWeight = 1;
|
||||
|
||||
var stateInfo = animator.GetCurrentAnimatorStateInfo(i);
|
||||
var nextStateInfo = animator.GetNextAnimatorStateInfo(i);
|
||||
|
||||
#if UNITY_5
|
||||
var clipInfo = animator.GetCurrentAnimatorClipInfo(i);
|
||||
var nextClipInfo = animator.GetNextAnimatorClipInfo(i);
|
||||
#else
|
||||
var clipInfo = animator.GetCurrentAnimationClipState(i);
|
||||
var nextClipInfo = animator.GetNextAnimationClipState(i);
|
||||
#endif
|
||||
MixMode mode = layerMixModes[i];
|
||||
|
||||
if (mode == MixMode.AlwaysMix) {
|
||||
//always use Mix instead of Applying the first non-zero weighted clip
|
||||
for (int c = 0; c < clipInfo.Length; c++) {
|
||||
var info = clipInfo[c];
|
||||
float weight = info.weight * layerWeight;
|
||||
if (weight == 0)
|
||||
continue;
|
||||
|
||||
float time = nextStateInfo.normalizedTime * info.clip.length;
|
||||
animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null, weight);
|
||||
float time = stateInfo.normalizedTime * info.clip.length;
|
||||
animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight);
|
||||
}
|
||||
}
|
||||
} else if (mode >= MixMode.MixNext) {
|
||||
//apply first non-zero weighted clip
|
||||
int c = 0;
|
||||
#if UNITY_5
|
||||
if (nextStateInfo.fullPathHash != 0) {
|
||||
#else
|
||||
if (nextStateInfo.nameHash != 0) {
|
||||
#endif
|
||||
for (int c = 0; c < nextClipInfo.Length; c++) {
|
||||
var info = nextClipInfo[c];
|
||||
float weight = info.weight * layerWeight;
|
||||
if (weight == 0)
|
||||
continue;
|
||||
|
||||
for (; c < clipInfo.Length; c++) {
|
||||
var info = clipInfo[c];
|
||||
float weight = info.weight * layerWeight;
|
||||
if (weight == 0)
|
||||
continue;
|
||||
float time = nextStateInfo.normalizedTime * info.clip.length;
|
||||
animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null, weight);
|
||||
}
|
||||
}
|
||||
} else if (mode >= MixMode.MixNext) {
|
||||
//apply first non-zero weighted clip
|
||||
int c = 0;
|
||||
|
||||
float time = stateInfo.normalizedTime * info.clip.length;
|
||||
animationTable[GetAnimationClipNameHashCode(info.clip)].Apply(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null);
|
||||
break;
|
||||
}
|
||||
for (; c < clipInfo.Length; c++) {
|
||||
var info = clipInfo[c];
|
||||
float weight = info.weight * layerWeight;
|
||||
if (weight == 0)
|
||||
continue;
|
||||
|
||||
//mix the rest
|
||||
for (; c < clipInfo.Length; c++) {
|
||||
var info = clipInfo[c];
|
||||
float weight = info.weight * layerWeight;
|
||||
if (weight == 0)
|
||||
continue;
|
||||
float time = stateInfo.normalizedTime * info.clip.length;
|
||||
animationTable[GetAnimationClipNameHashCode(info.clip)].Apply(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null);
|
||||
break;
|
||||
}
|
||||
|
||||
float time = stateInfo.normalizedTime * info.clip.length;
|
||||
animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight);
|
||||
}
|
||||
//mix the rest
|
||||
for (; c < clipInfo.Length; c++) {
|
||||
var info = clipInfo[c];
|
||||
float weight = info.weight * layerWeight;
|
||||
if (weight == 0)
|
||||
continue;
|
||||
|
||||
c = 0;
|
||||
#if UNITY_5
|
||||
if (nextStateInfo.fullPathHash != 0) {
|
||||
#else
|
||||
if (nextStateInfo.nameHash != 0) {
|
||||
#endif
|
||||
//apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights)
|
||||
if (mode == MixMode.SpineStyle) {
|
||||
float time = stateInfo.normalizedTime * info.clip.length;
|
||||
animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight);
|
||||
}
|
||||
|
||||
c = 0;
|
||||
#if UNITY_5
|
||||
if (nextStateInfo.fullPathHash != 0) {
|
||||
#else
|
||||
if (nextStateInfo.nameHash != 0) {
|
||||
#endif
|
||||
//apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights)
|
||||
if (mode == MixMode.SpineStyle) {
|
||||
for (; c < nextClipInfo.Length; c++) {
|
||||
var info = nextClipInfo[c];
|
||||
float weight = info.weight * layerWeight;
|
||||
if (weight == 0)
|
||||
continue;
|
||||
|
||||
float time = nextStateInfo.normalizedTime * info.clip.length;
|
||||
animationTable[GetAnimationClipNameHashCode(info.clip)].Apply(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//mix the rest
|
||||
for (; c < nextClipInfo.Length; c++) {
|
||||
var info = nextClipInfo[c];
|
||||
float weight = info.weight * layerWeight;
|
||||
@ -167,49 +179,37 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation {
|
||||
continue;
|
||||
|
||||
float time = nextStateInfo.normalizedTime * info.clip.length;
|
||||
animationTable[GetAnimationClipNameHashCode(info.clip)].Apply(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null);
|
||||
break;
|
||||
animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null, weight);
|
||||
}
|
||||
}
|
||||
|
||||
//mix the rest
|
||||
for (; c < nextClipInfo.Length; c++) {
|
||||
var info = nextClipInfo[c];
|
||||
float weight = info.weight * layerWeight;
|
||||
if (weight == 0)
|
||||
continue;
|
||||
|
||||
float time = nextStateInfo.normalizedTime * info.clip.length;
|
||||
animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_UpdateLocal != null)
|
||||
_UpdateLocal(this);
|
||||
if (_UpdateLocal != null)
|
||||
_UpdateLocal(this);
|
||||
|
||||
skeleton.UpdateWorldTransform();
|
||||
|
||||
if (_UpdateWorld != null) {
|
||||
_UpdateWorld(this);
|
||||
skeleton.UpdateWorldTransform();
|
||||
|
||||
if (_UpdateWorld != null) {
|
||||
_UpdateWorld(this);
|
||||
skeleton.UpdateWorldTransform();
|
||||
}
|
||||
|
||||
if (_UpdateComplete != null) {
|
||||
_UpdateComplete(this);
|
||||
}
|
||||
|
||||
lastTime = Time.time;
|
||||
}
|
||||
|
||||
if (_UpdateComplete != null) {
|
||||
_UpdateComplete(this);
|
||||
private int GetAnimationClipNameHashCode (AnimationClip clip) {
|
||||
int clipNameHashCode;
|
||||
if (!clipNameHashCodeTable.TryGetValue(clip, out clipNameHashCode)) {
|
||||
clipNameHashCode = clip.name.GetHashCode();
|
||||
clipNameHashCodeTable.Add(clip, clipNameHashCode);
|
||||
}
|
||||
|
||||
return clipNameHashCode;
|
||||
}
|
||||
|
||||
lastTime = Time.time;
|
||||
}
|
||||
|
||||
private int GetAnimationClipNameHashCode (AnimationClip clip) {
|
||||
int clipNameHashCode;
|
||||
if (!clipNameHashCodeTable.TryGetValue(clip, out clipNameHashCode)) {
|
||||
clipNameHashCode = clip.name.GetHashCode();
|
||||
clipNameHashCodeTable.Add(clip, clipNameHashCode);
|
||||
}
|
||||
|
||||
return clipNameHashCode;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,153 +6,125 @@
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using Spine;
|
||||
using Spine.Unity;
|
||||
|
||||
public static class SkeletonExtensions {
|
||||
namespace Spine.Unity {
|
||||
public static class SkeletonExtensions {
|
||||
|
||||
const float ByteToFloat = 1f / 255f;
|
||||
const float ByteToFloat = 1f / 255f;
|
||||
|
||||
#region Colors
|
||||
public static Color GetColor (this Skeleton s) { return new Color(s.r, s.g, s.b, s.a); }
|
||||
public static Color GetColor (this RegionAttachment a) { return new Color(a.r, a.g, a.b, a.a); }
|
||||
public static Color GetColor (this MeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); }
|
||||
public static Color GetColor (this WeightedMeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); }
|
||||
#region Colors
|
||||
public static Color GetColor (this Skeleton s) { return new Color(s.r, s.g, s.b, s.a); }
|
||||
public static Color GetColor (this RegionAttachment a) { return new Color(a.r, a.g, a.b, a.a); }
|
||||
public static Color GetColor (this MeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); }
|
||||
public static Color GetColor (this WeightedMeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); }
|
||||
|
||||
public static void SetColor (this Skeleton skeleton, Color color) {
|
||||
skeleton.A = color.a;
|
||||
skeleton.R = color.r;
|
||||
skeleton.G = color.g;
|
||||
skeleton.B = color.b;
|
||||
public static void SetColor (this Skeleton skeleton, Color color) {
|
||||
skeleton.A = color.a;
|
||||
skeleton.R = color.r;
|
||||
skeleton.G = color.g;
|
||||
skeleton.B = color.b;
|
||||
}
|
||||
|
||||
public static void SetColor (this Skeleton skeleton, Color32 color) {
|
||||
skeleton.A = color.a * ByteToFloat;
|
||||
skeleton.R = color.r * ByteToFloat;
|
||||
skeleton.G = color.g * ByteToFloat;
|
||||
skeleton.B = color.b * ByteToFloat;
|
||||
}
|
||||
|
||||
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) {
|
||||
slot.A = color.a * ByteToFloat;
|
||||
slot.R = color.r * ByteToFloat;
|
||||
slot.G = color.g * ByteToFloat;
|
||||
slot.B = color.b * ByteToFloat;
|
||||
}
|
||||
|
||||
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) {
|
||||
attachment.A = color.a * ByteToFloat;
|
||||
attachment.R = color.r * ByteToFloat;
|
||||
attachment.G = color.g * ByteToFloat;
|
||||
attachment.B = color.b * ByteToFloat;
|
||||
}
|
||||
|
||||
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) {
|
||||
attachment.A = color.a * ByteToFloat;
|
||||
attachment.R = color.r * ByteToFloat;
|
||||
attachment.G = color.g * ByteToFloat;
|
||||
attachment.B = color.b * ByteToFloat;
|
||||
}
|
||||
|
||||
public static void SetColor (this WeightedMeshAttachment attachment, Color color) {
|
||||
attachment.A = color.a;
|
||||
attachment.R = color.r;
|
||||
attachment.G = color.g;
|
||||
attachment.B = color.b;
|
||||
}
|
||||
|
||||
public static void SetColor (this WeightedMeshAttachment attachment, Color32 color) {
|
||||
attachment.A = color.a * ByteToFloat;
|
||||
attachment.R = color.r * ByteToFloat;
|
||||
attachment.G = color.g * ByteToFloat;
|
||||
attachment.B = color.b * ByteToFloat;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Bone 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) {
|
||||
bone.X = position.x;
|
||||
bone.Y = position.y;
|
||||
}
|
||||
|
||||
public static Vector2 GetSkeletonSpacePosition (this Bone bone) {
|
||||
return new Vector2(bone.worldX, bone.worldY);
|
||||
}
|
||||
|
||||
public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform parentTransform) {
|
||||
return parentTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Posing
|
||||
/// <summary>
|
||||
/// Shortcut for posing a skeleton at a specific time. Time is in seconds. (frameNumber / 30f) will give you seconds.
|
||||
/// If you need to do this often, you should get the Animation object yourself using skeleton.data.FindAnimation. and call Apply on that.</summary>
|
||||
/// <param name = "skeleton">The skeleton to pose.</param>
|
||||
/// <param name="animationName">The name of the animation to use.</param>
|
||||
/// <param name = "time">The time of the pose within the animation.</param>
|
||||
/// <param name = "loop">Wraps the time around if it is longer than the duration of the animation.</param>
|
||||
public static void PoseWithAnimation (this Skeleton skeleton, string animationName, float time, bool loop) {
|
||||
// Fail loud when skeleton.data is null.
|
||||
Spine.Animation animation = skeleton.data.FindAnimation(animationName);
|
||||
if (animation == null) return;
|
||||
animation.Apply(skeleton, 0, time, loop, null);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static void SetColor (this Skeleton skeleton, Color32 color) {
|
||||
skeleton.A = color.a * ByteToFloat;
|
||||
skeleton.R = color.r * ByteToFloat;
|
||||
skeleton.G = color.g * ByteToFloat;
|
||||
skeleton.B = color.b * ByteToFloat;
|
||||
}
|
||||
|
||||
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) {
|
||||
slot.A = color.a * ByteToFloat;
|
||||
slot.R = color.r * ByteToFloat;
|
||||
slot.G = color.g * ByteToFloat;
|
||||
slot.B = color.b * ByteToFloat;
|
||||
}
|
||||
|
||||
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) {
|
||||
attachment.A = color.a * ByteToFloat;
|
||||
attachment.R = color.r * ByteToFloat;
|
||||
attachment.G = color.g * ByteToFloat;
|
||||
attachment.B = color.b * ByteToFloat;
|
||||
}
|
||||
|
||||
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) {
|
||||
attachment.A = color.a * ByteToFloat;
|
||||
attachment.R = color.r * ByteToFloat;
|
||||
attachment.G = color.g * ByteToFloat;
|
||||
attachment.B = color.b * ByteToFloat;
|
||||
}
|
||||
|
||||
public static void SetColor (this WeightedMeshAttachment attachment, Color color) {
|
||||
attachment.A = color.a;
|
||||
attachment.R = color.r;
|
||||
attachment.G = color.g;
|
||||
attachment.B = color.b;
|
||||
}
|
||||
|
||||
public static void SetColor (this WeightedMeshAttachment attachment, Color32 color) {
|
||||
attachment.A = color.a * ByteToFloat;
|
||||
attachment.R = color.r * ByteToFloat;
|
||||
attachment.G = color.g * ByteToFloat;
|
||||
attachment.B = color.b * ByteToFloat;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Bone 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) {
|
||||
bone.X = position.x;
|
||||
bone.Y = position.y;
|
||||
}
|
||||
|
||||
public static Vector2 GetSkeletonSpacePosition (this Bone bone) {
|
||||
// TODO: This changes in v3.0
|
||||
return new Vector2(bone.worldX, bone.worldY);
|
||||
}
|
||||
|
||||
public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform parentTransform) {
|
||||
return parentTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Posing
|
||||
/// <summary>
|
||||
/// Shortcut for posing a skeleton at a specific time. Time is in seconds. (frameNumber / 30f) will give you seconds.
|
||||
/// If you need to do this often, you should get the Animation object yourself using skeleton.data.FindAnimation. and call Apply on that.</summary>
|
||||
/// <param name = "skeleton">The skeleton to pose.</param>
|
||||
/// <param name="animationName">The name of the animation to use.</param>
|
||||
/// <param name = "time">The time of the pose within the animation.</param>
|
||||
/// <param name = "loop">Wraps the time around if it is longer than the duration of the animation.</param>
|
||||
public static void PoseWithAnimation (this Skeleton skeleton, string animationName, float time, bool loop) {
|
||||
// Fail loud when skeleton.data is null.
|
||||
Spine.Animation animation = skeleton.data.FindAnimation(animationName);
|
||||
if (animation == null) return;
|
||||
animation.Apply(skeleton, 0, time, loop, null);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Unity Sprite To Attachments
|
||||
public static Attachment AttachUnitySprite (this Skeleton skeleton, string slotName, Sprite sprite, string shaderName = "Spine/Skeleton") {
|
||||
var att = sprite.ToRegionAttachment(shaderName);
|
||||
skeleton.FindSlot(slotName).Attachment = att;
|
||||
return att;
|
||||
}
|
||||
|
||||
public static Attachment AddUnitySprite (this SkeletonData skeletonData, string slotName, Sprite sprite, string skinName = "", string shaderName = "Spine/Skeleton") {
|
||||
var att = sprite.ToRegionAttachment(shaderName);
|
||||
|
||||
var slotIndex = skeletonData.FindSlotIndex(slotName);
|
||||
Skin skin = skeletonData.defaultSkin;
|
||||
if (skinName != "")
|
||||
skin = skeletonData.FindSkin(skinName);
|
||||
|
||||
skin.AddAttachment(slotIndex, att.Name, att);
|
||||
|
||||
return att;
|
||||
}
|
||||
|
||||
public static RegionAttachment ToRegionAttachment (this Sprite sprite, string shaderName = "Spine/Skeleton") {
|
||||
var loader = new SpriteAttachmentLoader(sprite, Shader.Find(shaderName));
|
||||
var att = loader.NewRegionAttachment(null, sprite.name, "");
|
||||
loader = null;
|
||||
return att;
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -10,345 +10,348 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Spine;
|
||||
|
||||
[CustomEditor(typeof(SkeletonUtilityBone)), CanEditMultipleObjects]
|
||||
public class SkeletonUtilityBoneInspector : Editor {
|
||||
SerializedProperty mode, boneName, zPosition, position, rotation, scale, overrideAlpha, parentReference;
|
||||
// MITCH
|
||||
// SerializedProperty flip, flipX;
|
||||
|
||||
//multi selected flags
|
||||
bool containsFollows, containsOverrides, multiObject;
|
||||
|
||||
//single selected helpers
|
||||
SkeletonUtilityBone utilityBone;
|
||||
SkeletonUtility skeletonUtility;
|
||||
bool canCreateHingeChain = false;
|
||||
|
||||
Dictionary<Slot, List<BoundingBoxAttachment>> boundingBoxTable = new Dictionary<Slot, List<BoundingBoxAttachment>>();
|
||||
string currentSkinName = "";
|
||||
|
||||
void OnEnable () {
|
||||
mode = this.serializedObject.FindProperty("mode");
|
||||
boneName = this.serializedObject.FindProperty("boneName");
|
||||
zPosition = this.serializedObject.FindProperty("zPosition");
|
||||
position = this.serializedObject.FindProperty("position");
|
||||
rotation = this.serializedObject.FindProperty("rotation");
|
||||
scale = this.serializedObject.FindProperty("scale");
|
||||
overrideAlpha = this.serializedObject.FindProperty("overrideAlpha");
|
||||
parentReference = this.serializedObject.FindProperty("parentReference");
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
[CustomEditor(typeof(SkeletonUtilityBone)), CanEditMultipleObjects]
|
||||
public class SkeletonUtilityBoneInspector : UnityEditor.Editor {
|
||||
SerializedProperty mode, boneName, zPosition, position, rotation, scale, overrideAlpha, parentReference;
|
||||
// MITCH
|
||||
// flip = this.serializedObject.FindProperty("flip");
|
||||
// flipX = this.serializedObject.FindProperty("flipX");
|
||||
// SerializedProperty flip, flipX;
|
||||
|
||||
EvaluateFlags();
|
||||
//multi selected flags
|
||||
bool containsFollows, containsOverrides, multiObject;
|
||||
|
||||
if (utilityBone.valid == false && skeletonUtility != null && skeletonUtility.skeletonRenderer != null) {
|
||||
skeletonUtility.skeletonRenderer.Initialize(false);
|
||||
}
|
||||
//single selected helpers
|
||||
SkeletonUtilityBone utilityBone;
|
||||
SkeletonUtility skeletonUtility;
|
||||
bool canCreateHingeChain = false;
|
||||
|
||||
canCreateHingeChain = CanCreateHingeChain();
|
||||
Dictionary<Slot, List<BoundingBoxAttachment>> boundingBoxTable = new Dictionary<Slot, List<BoundingBoxAttachment>>();
|
||||
string currentSkinName = "";
|
||||
|
||||
boundingBoxTable.Clear();
|
||||
|
||||
if (multiObject)
|
||||
return;
|
||||
|
||||
if (utilityBone.bone == null)
|
||||
return;
|
||||
|
||||
var skeleton = utilityBone.bone.Skeleton;
|
||||
int slotCount = skeleton.Slots.Count;
|
||||
Skin skin = skeleton.Skin;
|
||||
if (skeleton.Skin == null)
|
||||
skin = skeleton.Data.DefaultSkin;
|
||||
|
||||
currentSkinName = skin.Name;
|
||||
for(int i = 0; i < slotCount; i++){
|
||||
Slot slot = skeletonUtility.skeletonRenderer.skeleton.Slots.Items[i];
|
||||
if (slot.Bone == utilityBone.bone) {
|
||||
List<Attachment> attachments = new List<Attachment>();
|
||||
|
||||
|
||||
skin.FindAttachmentsForSlot(skeleton.FindSlotIndex(slot.Data.Name), attachments);
|
||||
|
||||
List<BoundingBoxAttachment> boundingBoxes = new List<BoundingBoxAttachment>();
|
||||
foreach (var att in attachments) {
|
||||
if (att is BoundingBoxAttachment) {
|
||||
boundingBoxes.Add((BoundingBoxAttachment)att);
|
||||
}
|
||||
}
|
||||
|
||||
if (boundingBoxes.Count > 0) {
|
||||
boundingBoxTable.Add(slot, boundingBoxes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void EvaluateFlags () {
|
||||
utilityBone = (SkeletonUtilityBone)target;
|
||||
skeletonUtility = utilityBone.skeletonUtility;
|
||||
|
||||
if (Selection.objects.Length == 1) {
|
||||
containsFollows = utilityBone.mode == SkeletonUtilityBone.Mode.Follow;
|
||||
containsOverrides = utilityBone.mode == SkeletonUtilityBone.Mode.Override;
|
||||
} else {
|
||||
int boneCount = 0;
|
||||
foreach (Object o in Selection.objects) {
|
||||
if (o is GameObject) {
|
||||
GameObject go = (GameObject)o;
|
||||
SkeletonUtilityBone sub = go.GetComponent<SkeletonUtilityBone>();
|
||||
if (sub != null) {
|
||||
boneCount++;
|
||||
if (sub.mode == SkeletonUtilityBone.Mode.Follow)
|
||||
containsFollows = true;
|
||||
if (sub.mode == SkeletonUtilityBone.Mode.Override)
|
||||
containsOverrides = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (boneCount > 1)
|
||||
multiObject = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(mode);
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
containsOverrides = mode.enumValueIndex == 1;
|
||||
containsFollows = mode.enumValueIndex == 0;
|
||||
}
|
||||
|
||||
EditorGUI.BeginDisabledGroup(multiObject);
|
||||
{
|
||||
string str = boneName.stringValue;
|
||||
if (str == "")
|
||||
str = "<None>";
|
||||
if (multiObject)
|
||||
str = "<Multiple>";
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel("Bone");
|
||||
|
||||
if (GUILayout.Button(str, EditorStyles.popup)) {
|
||||
BoneSelectorContextMenu(str, ((SkeletonUtilityBone)target).skeletonUtility.skeletonRenderer.skeleton.Bones, "<None>", TargetBoneSelected);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUILayout.PropertyField(zPosition);
|
||||
EditorGUILayout.PropertyField(position);
|
||||
EditorGUILayout.PropertyField(rotation);
|
||||
EditorGUILayout.PropertyField(scale);
|
||||
// MITCH
|
||||
// EditorGUILayout.PropertyField(flip);
|
||||
|
||||
EditorGUI.BeginDisabledGroup(containsFollows);
|
||||
{
|
||||
EditorGUILayout.PropertyField(overrideAlpha);
|
||||
EditorGUILayout.PropertyField(parentReference);
|
||||
void OnEnable () {
|
||||
mode = this.serializedObject.FindProperty("mode");
|
||||
boneName = this.serializedObject.FindProperty("boneName");
|
||||
zPosition = this.serializedObject.FindProperty("zPosition");
|
||||
position = this.serializedObject.FindProperty("position");
|
||||
rotation = this.serializedObject.FindProperty("rotation");
|
||||
scale = this.serializedObject.FindProperty("scale");
|
||||
overrideAlpha = this.serializedObject.FindProperty("overrideAlpha");
|
||||
parentReference = this.serializedObject.FindProperty("parentReference");
|
||||
|
||||
// MITCH
|
||||
// EditorGUI.BeginDisabledGroup(multiObject || !flip.boolValue);
|
||||
// {
|
||||
// EditorGUI.BeginChangeCheck();
|
||||
// EditorGUILayout.PropertyField(flipX);
|
||||
// if (EditorGUI.EndChangeCheck()) {
|
||||
// FlipX(flipX.boolValue);
|
||||
// }
|
||||
// }
|
||||
// EditorGUI.EndDisabledGroup();
|
||||
// flip = this.serializedObject.FindProperty("flip");
|
||||
// flipX = this.serializedObject.FindProperty("flipX");
|
||||
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
EvaluateFlags();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || utilityBone.bone == null || utilityBone.bone.Children.Count == 0);
|
||||
{
|
||||
if (GUILayout.Button(new GUIContent("Add Child", SpineEditorUtilities.Icons.bone), GUILayout.Width(150), GUILayout.Height(24)))
|
||||
BoneSelectorContextMenu("", utilityBone.bone.Children, "<Recursively>", SpawnChildBoneSelected);
|
||||
if (utilityBone.valid == false && skeletonUtility != null && skeletonUtility.skeletonRenderer != null) {
|
||||
skeletonUtility.skeletonRenderer.Initialize(false);
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || utilityBone.bone == null || containsOverrides);
|
||||
{
|
||||
if (GUILayout.Button(new GUIContent("Add Override", SpineEditorUtilities.Icons.poseBones), GUILayout.Width(150), GUILayout.Height(24)))
|
||||
SpawnOverride();
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
canCreateHingeChain = CanCreateHingeChain();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || !canCreateHingeChain);
|
||||
{
|
||||
if (GUILayout.Button(new GUIContent("Create Hinge Chain", SpineEditorUtilities.Icons.hingeChain), GUILayout.Width(150), GUILayout.Height(24)))
|
||||
CreateHingeChain();
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
boundingBoxTable.Clear();
|
||||
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
if (multiObject)
|
||||
return;
|
||||
|
||||
EditorGUI.BeginDisabledGroup(multiObject || boundingBoxTable.Count == 0);
|
||||
EditorGUILayout.LabelField(new GUIContent("Bounding Boxes", SpineEditorUtilities.Icons.boundingBox), EditorStyles.boldLabel);
|
||||
if (utilityBone.bone == null)
|
||||
return;
|
||||
|
||||
foreach(var entry in boundingBoxTable){
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.LabelField(entry.Key.Data.Name);
|
||||
EditorGUI.indentLevel++;
|
||||
foreach (var box in entry.Value) {
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(30);
|
||||
if (GUILayout.Button(box.Name, GUILayout.Width(200))) {
|
||||
var child = utilityBone.transform.FindChild("[BoundingBox]" + box.Name);
|
||||
if (child != null) {
|
||||
var originalCollider = child.GetComponent<PolygonCollider2D>();
|
||||
var updatedCollider = SkeletonUtility.AddBoundingBoxAsComponent(box, child.gameObject, originalCollider.isTrigger);
|
||||
originalCollider.points = updatedCollider.points;
|
||||
if (EditorApplication.isPlaying)
|
||||
Destroy(updatedCollider);
|
||||
else
|
||||
DestroyImmediate(updatedCollider);
|
||||
} else {
|
||||
utilityBone.AddBoundingBox(currentSkinName, entry.Key.Data.Name, box.Name);
|
||||
var skeleton = utilityBone.bone.Skeleton;
|
||||
int slotCount = skeleton.Slots.Count;
|
||||
Skin skin = skeleton.Skin;
|
||||
if (skeleton.Skin == null)
|
||||
skin = skeleton.Data.DefaultSkin;
|
||||
|
||||
currentSkinName = skin.Name;
|
||||
for(int i = 0; i < slotCount; i++){
|
||||
Slot slot = skeletonUtility.skeletonRenderer.skeleton.Slots.Items[i];
|
||||
if (slot.Bone == utilityBone.bone) {
|
||||
List<Attachment> attachments = new List<Attachment>();
|
||||
|
||||
|
||||
skin.FindAttachmentsForSlot(skeleton.FindSlotIndex(slot.Data.Name), attachments);
|
||||
|
||||
List<BoundingBoxAttachment> boundingBoxes = new List<BoundingBoxAttachment>();
|
||||
foreach (var att in attachments) {
|
||||
if (att is BoundingBoxAttachment) {
|
||||
boundingBoxes.Add((BoundingBoxAttachment)att);
|
||||
}
|
||||
}
|
||||
|
||||
if (boundingBoxes.Count > 0) {
|
||||
boundingBoxTable.Add(slot, boundingBoxes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void EvaluateFlags () {
|
||||
utilityBone = (SkeletonUtilityBone)target;
|
||||
skeletonUtility = utilityBone.skeletonUtility;
|
||||
|
||||
if (Selection.objects.Length == 1) {
|
||||
containsFollows = utilityBone.mode == SkeletonUtilityBone.Mode.Follow;
|
||||
containsOverrides = utilityBone.mode == SkeletonUtilityBone.Mode.Override;
|
||||
} else {
|
||||
int boneCount = 0;
|
||||
foreach (Object o in Selection.objects) {
|
||||
if (o is GameObject) {
|
||||
GameObject go = (GameObject)o;
|
||||
SkeletonUtilityBone sub = go.GetComponent<SkeletonUtilityBone>();
|
||||
if (sub != null) {
|
||||
boneCount++;
|
||||
if (sub.mode == SkeletonUtilityBone.Mode.Follow)
|
||||
containsFollows = true;
|
||||
if (sub.mode == SkeletonUtilityBone.Mode.Override)
|
||||
containsOverrides = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (boneCount > 1)
|
||||
multiObject = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(mode);
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
containsOverrides = mode.enumValueIndex == 1;
|
||||
containsFollows = mode.enumValueIndex == 0;
|
||||
}
|
||||
|
||||
EditorGUI.BeginDisabledGroup(multiObject);
|
||||
{
|
||||
string str = boneName.stringValue;
|
||||
if (str == "")
|
||||
str = "<None>";
|
||||
if (multiObject)
|
||||
str = "<Multiple>";
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel("Bone");
|
||||
|
||||
if (GUILayout.Button(str, EditorStyles.popup)) {
|
||||
BoneSelectorContextMenu(str, ((SkeletonUtilityBone)target).skeletonUtility.skeletonRenderer.skeleton.Bones, "<None>", TargetBoneSelected);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
EditorGUILayout.PropertyField(zPosition);
|
||||
EditorGUILayout.PropertyField(position);
|
||||
EditorGUILayout.PropertyField(rotation);
|
||||
EditorGUILayout.PropertyField(scale);
|
||||
// MITCH
|
||||
// EditorGUILayout.PropertyField(flip);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
EditorGUI.BeginDisabledGroup(containsFollows);
|
||||
{
|
||||
EditorGUILayout.PropertyField(overrideAlpha);
|
||||
EditorGUILayout.PropertyField(parentReference);
|
||||
|
||||
// MITCH
|
||||
// void FlipX (bool state) {
|
||||
// utilityBone.FlipX(state);
|
||||
// if (Application.isPlaying == false) {
|
||||
// skeletonUtility.skeletonAnimation.LateUpdate();
|
||||
// }
|
||||
// }
|
||||
// MITCH
|
||||
// EditorGUI.BeginDisabledGroup(multiObject || !flip.boolValue);
|
||||
// {
|
||||
// EditorGUI.BeginChangeCheck();
|
||||
// EditorGUILayout.PropertyField(flipX);
|
||||
// if (EditorGUI.EndChangeCheck()) {
|
||||
// FlipX(flipX.boolValue);
|
||||
// }
|
||||
// }
|
||||
// EditorGUI.EndDisabledGroup();
|
||||
|
||||
void BoneSelectorContextMenu (string current, ExposedList<Bone> bones, string topValue, GenericMenu.MenuFunction2 callback) {
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
if (topValue != "")
|
||||
menu.AddItem(new GUIContent(topValue), current == topValue, callback, null);
|
||||
|
||||
for (int i = 0; i < bones.Count; i++) {
|
||||
menu.AddItem(new GUIContent(bones.Items[i].Data.Name), bones.Items[i].Data.Name == current, callback, bones.Items[i]);
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
|
||||
}
|
||||
|
||||
void TargetBoneSelected (object obj) {
|
||||
if (obj == null) {
|
||||
boneName.stringValue = "";
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
} else {
|
||||
Bone bone = (Bone)obj;
|
||||
boneName.stringValue = bone.Data.Name;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
utilityBone.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnChildBoneSelected (object obj) {
|
||||
if (obj == null) {
|
||||
//add recursively
|
||||
foreach (var bone in utilityBone.bone.Children) {
|
||||
GameObject go = skeletonUtility.SpawnBoneRecursively(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
||||
SkeletonUtilityBone[] newUtilityBones = go.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
foreach (SkeletonUtilityBone utilBone in newUtilityBones)
|
||||
SkeletonUtilityInspector.AttachIcon(utilBone);
|
||||
}
|
||||
} else {
|
||||
Bone bone = (Bone)obj;
|
||||
GameObject go = skeletonUtility.SpawnBone(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || utilityBone.bone == null || utilityBone.bone.Children.Count == 0);
|
||||
{
|
||||
if (GUILayout.Button(new GUIContent("Add Child", SpineEditorUtilities.Icons.bone), GUILayout.Width(150), GUILayout.Height(24)))
|
||||
BoneSelectorContextMenu("", utilityBone.bone.Children, "<Recursively>", SpawnChildBoneSelected);
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || utilityBone.bone == null || containsOverrides);
|
||||
{
|
||||
if (GUILayout.Button(new GUIContent("Add Override", SpineEditorUtilities.Icons.poseBones), GUILayout.Width(150), GUILayout.Height(24)))
|
||||
SpawnOverride();
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || !canCreateHingeChain);
|
||||
{
|
||||
if (GUILayout.Button(new GUIContent("Create Hinge Chain", SpineEditorUtilities.Icons.hingeChain), GUILayout.Width(150), GUILayout.Height(24)))
|
||||
CreateHingeChain();
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(multiObject || boundingBoxTable.Count == 0);
|
||||
EditorGUILayout.LabelField(new GUIContent("Bounding Boxes", SpineEditorUtilities.Icons.boundingBox), EditorStyles.boldLabel);
|
||||
|
||||
foreach(var entry in boundingBoxTable){
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.LabelField(entry.Key.Data.Name);
|
||||
EditorGUI.indentLevel++;
|
||||
foreach (var box in entry.Value) {
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(30);
|
||||
if (GUILayout.Button(box.Name, GUILayout.Width(200))) {
|
||||
var child = utilityBone.transform.FindChild("[BoundingBox]" + box.Name);
|
||||
if (child != null) {
|
||||
var originalCollider = child.GetComponent<PolygonCollider2D>();
|
||||
var updatedCollider = SkeletonUtility.AddBoundingBoxAsComponent(box, child.gameObject, originalCollider.isTrigger);
|
||||
originalCollider.points = updatedCollider.points;
|
||||
if (EditorApplication.isPlaying)
|
||||
Destroy(updatedCollider);
|
||||
else
|
||||
DestroyImmediate(updatedCollider);
|
||||
} else {
|
||||
utilityBone.AddBoundingBox(currentSkinName, entry.Key.Data.Name, box.Name);
|
||||
}
|
||||
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
// MITCH
|
||||
// void FlipX (bool state) {
|
||||
// utilityBone.FlipX(state);
|
||||
// if (Application.isPlaying == false) {
|
||||
// skeletonUtility.skeletonAnimation.LateUpdate();
|
||||
// }
|
||||
// }
|
||||
|
||||
void BoneSelectorContextMenu (string current, ExposedList<Bone> bones, string topValue, GenericMenu.MenuFunction2 callback) {
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
if (topValue != "")
|
||||
menu.AddItem(new GUIContent(topValue), current == topValue, callback, null);
|
||||
|
||||
for (int i = 0; i < bones.Count; i++) {
|
||||
menu.AddItem(new GUIContent(bones.Items[i].Data.Name), bones.Items[i].Data.Name == current, callback, bones.Items[i]);
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
|
||||
}
|
||||
|
||||
void TargetBoneSelected (object obj) {
|
||||
if (obj == null) {
|
||||
boneName.stringValue = "";
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
} else {
|
||||
Bone bone = (Bone)obj;
|
||||
boneName.stringValue = bone.Data.Name;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
utilityBone.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnChildBoneSelected (object obj) {
|
||||
if (obj == null) {
|
||||
//add recursively
|
||||
foreach (var bone in utilityBone.bone.Children) {
|
||||
GameObject go = skeletonUtility.SpawnBoneRecursively(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
||||
SkeletonUtilityBone[] newUtilityBones = go.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
foreach (SkeletonUtilityBone utilBone in newUtilityBones)
|
||||
SkeletonUtilityInspector.AttachIcon(utilBone);
|
||||
}
|
||||
} else {
|
||||
Bone bone = (Bone)obj;
|
||||
GameObject go = skeletonUtility.SpawnBone(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
||||
SkeletonUtilityInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
|
||||
Selection.activeGameObject = go;
|
||||
EditorGUIUtility.PingObject(go);
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnOverride () {
|
||||
GameObject go = skeletonUtility.SpawnBone(utilityBone.bone, utilityBone.transform.parent, SkeletonUtilityBone.Mode.Override, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
||||
go.name = go.name + " [Override]";
|
||||
SkeletonUtilityInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
|
||||
Selection.activeGameObject = go;
|
||||
EditorGUIUtility.PingObject(go);
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnOverride () {
|
||||
GameObject go = skeletonUtility.SpawnBone(utilityBone.bone, utilityBone.transform.parent, SkeletonUtilityBone.Mode.Override, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
||||
go.name = go.name + " [Override]";
|
||||
SkeletonUtilityInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
|
||||
Selection.activeGameObject = go;
|
||||
EditorGUIUtility.PingObject(go);
|
||||
}
|
||||
bool CanCreateHingeChain () {
|
||||
if (utilityBone == null)
|
||||
return false;
|
||||
if (utilityBone.GetComponent<Rigidbody>() != null)
|
||||
return false;
|
||||
if (utilityBone.bone != null && utilityBone.bone.Children.Count == 0)
|
||||
return false;
|
||||
|
||||
bool CanCreateHingeChain () {
|
||||
if (utilityBone == null)
|
||||
return false;
|
||||
if (utilityBone.GetComponent<Rigidbody>() != null)
|
||||
return false;
|
||||
if (utilityBone.bone != null && utilityBone.bone.Children.Count == 0)
|
||||
return false;
|
||||
Rigidbody[] rigidbodies = utilityBone.GetComponentsInChildren<Rigidbody>();
|
||||
|
||||
Rigidbody[] rigidbodies = utilityBone.GetComponentsInChildren<Rigidbody>();
|
||||
if (rigidbodies.Length > 0)
|
||||
return false;
|
||||
|
||||
if (rigidbodies.Length > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CreateHingeChain () {
|
||||
var utilBoneArr = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
|
||||
foreach (var utilBone in utilBoneArr) {
|
||||
AttachRigidbody(utilBone);
|
||||
return true;
|
||||
}
|
||||
|
||||
utilityBone.GetComponent<Rigidbody>().isKinematic = true;
|
||||
void CreateHingeChain () {
|
||||
var utilBoneArr = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
|
||||
foreach (var utilBone in utilBoneArr) {
|
||||
if (utilBone == utilityBone)
|
||||
continue;
|
||||
foreach (var utilBone in utilBoneArr) {
|
||||
AttachRigidbody(utilBone);
|
||||
}
|
||||
|
||||
utilBone.mode = SkeletonUtilityBone.Mode.Override;
|
||||
utilityBone.GetComponent<Rigidbody>().isKinematic = true;
|
||||
|
||||
HingeJoint joint = utilBone.gameObject.AddComponent<HingeJoint>();
|
||||
joint.axis = Vector3.forward;
|
||||
joint.connectedBody = utilBone.transform.parent.GetComponent<Rigidbody>();
|
||||
joint.useLimits = true;
|
||||
JointLimits limits = new JointLimits();
|
||||
limits.min = -20;
|
||||
limits.max = 20;
|
||||
joint.limits = limits;
|
||||
utilBone.GetComponent<Rigidbody>().mass = utilBone.transform.parent.GetComponent<Rigidbody>().mass * 0.75f;
|
||||
}
|
||||
}
|
||||
|
||||
static void AttachRigidbody (SkeletonUtilityBone utilBone) {
|
||||
if (utilBone.GetComponent<Collider>() == null) {
|
||||
if (utilBone.bone.Data.Length == 0) {
|
||||
SphereCollider sphere = utilBone.gameObject.AddComponent<SphereCollider>();
|
||||
sphere.radius = 0.1f;
|
||||
} else {
|
||||
float length = utilBone.bone.Data.Length;
|
||||
BoxCollider box = utilBone.gameObject.AddComponent<BoxCollider>();
|
||||
box.size = new Vector3(length, length / 3, 0.2f);
|
||||
box.center = new Vector3(length / 2, 0, 0);
|
||||
foreach (var utilBone in utilBoneArr) {
|
||||
if (utilBone == utilityBone)
|
||||
continue;
|
||||
|
||||
utilBone.mode = SkeletonUtilityBone.Mode.Override;
|
||||
|
||||
HingeJoint joint = utilBone.gameObject.AddComponent<HingeJoint>();
|
||||
joint.axis = Vector3.forward;
|
||||
joint.connectedBody = utilBone.transform.parent.GetComponent<Rigidbody>();
|
||||
joint.useLimits = true;
|
||||
JointLimits limits = new JointLimits();
|
||||
limits.min = -20;
|
||||
limits.max = 20;
|
||||
joint.limits = limits;
|
||||
utilBone.GetComponent<Rigidbody>().mass = utilBone.transform.parent.GetComponent<Rigidbody>().mass * 0.75f;
|
||||
}
|
||||
}
|
||||
|
||||
utilBone.gameObject.AddComponent<Rigidbody>();
|
||||
static void AttachRigidbody (SkeletonUtilityBone utilBone) {
|
||||
if (utilBone.GetComponent<Collider>() == null) {
|
||||
if (utilBone.bone.Data.Length == 0) {
|
||||
SphereCollider sphere = utilBone.gameObject.AddComponent<SphereCollider>();
|
||||
sphere.radius = 0.1f;
|
||||
} else {
|
||||
float length = utilBone.bone.Data.Length;
|
||||
BoxCollider box = utilBone.gameObject.AddComponent<BoxCollider>();
|
||||
box.size = new Vector3(length, length / 3, 0.2f);
|
||||
box.center = new Vector3(length / 2, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
utilBone.gameObject.AddComponent<Rigidbody>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -18,264 +18,268 @@ using Spine;
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
[CustomEditor(typeof(SkeletonUtility))]
|
||||
public class SkeletonUtilityInspector : Editor {
|
||||
|
||||
public static void AttachIcon (SkeletonUtilityBone utilityBone) {
|
||||
Skeleton skeleton = utilityBone.skeletonUtility.skeletonRenderer.skeleton;
|
||||
Texture2D icon;
|
||||
if (utilityBone.bone.Data.Length == 0)
|
||||
icon = SpineEditorUtilities.Icons.nullBone;
|
||||
else
|
||||
icon = SpineEditorUtilities.Icons.boneNib;
|
||||
|
||||
foreach (IkConstraint c in skeleton.IkConstraints) {
|
||||
if (c.Target == utilityBone.bone) {
|
||||
icon = SpineEditorUtilities.Icons.constraintNib;
|
||||
break;
|
||||
namespace Spine.Unity.Editor {
|
||||
[CustomEditor(typeof(SkeletonUtility))]
|
||||
public class SkeletonUtilityInspector : UnityEditor.Editor {
|
||||
|
||||
public static void AttachIcon (SkeletonUtilityBone utilityBone) {
|
||||
Skeleton skeleton = utilityBone.skeletonUtility.skeletonRenderer.skeleton;
|
||||
Texture2D icon;
|
||||
if (utilityBone.bone.Data.Length == 0)
|
||||
icon = SpineEditorUtilities.Icons.nullBone;
|
||||
else
|
||||
icon = SpineEditorUtilities.Icons.boneNib;
|
||||
|
||||
foreach (IkConstraint c in skeleton.IkConstraints) {
|
||||
if (c.Target == utilityBone.bone) {
|
||||
icon = SpineEditorUtilities.Icons.constraintNib;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
typeof(EditorGUIUtility).InvokeMember("SetIconForObject", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic, null, null, new object[2] {
|
||||
utilityBone.gameObject,
|
||||
icon
|
||||
});
|
||||
}
|
||||
|
||||
static void AttachIconsToChildren (Transform root) {
|
||||
if (root != null) {
|
||||
var utilityBones = root.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
foreach (var utilBone in utilityBones) {
|
||||
AttachIcon(utilBone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typeof(EditorGUIUtility).InvokeMember("SetIconForObject", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic, null, null, new object[2] {
|
||||
utilityBone.gameObject,
|
||||
icon
|
||||
});
|
||||
}
|
||||
|
||||
static void AttachIconsToChildren (Transform root) {
|
||||
if (root != null) {
|
||||
var utilityBones = root.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
foreach (var utilBone in utilityBones) {
|
||||
AttachIcon(utilBone);
|
||||
}
|
||||
static SkeletonUtilityInspector () {
|
||||
#if UNITY_4_3
|
||||
showSlots = false;
|
||||
#else
|
||||
showSlots = new AnimBool(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static SkeletonUtilityInspector () {
|
||||
SkeletonUtility skeletonUtility;
|
||||
Skeleton skeleton;
|
||||
SkeletonRenderer skeletonRenderer;
|
||||
Transform transform;
|
||||
bool isPrefab;
|
||||
Dictionary<Slot, List<Attachment>> attachmentTable = new Dictionary<Slot, List<Attachment>>();
|
||||
|
||||
|
||||
//GUI stuff
|
||||
#if UNITY_4_3
|
||||
showSlots = false;
|
||||
static bool showSlots;
|
||||
#else
|
||||
showSlots = new AnimBool(false);
|
||||
static AnimBool showSlots;
|
||||
#endif
|
||||
}
|
||||
|
||||
SkeletonUtility skeletonUtility;
|
||||
Skeleton skeleton;
|
||||
SkeletonRenderer skeletonRenderer;
|
||||
Transform transform;
|
||||
bool isPrefab;
|
||||
Dictionary<Slot, List<Attachment>> attachmentTable = new Dictionary<Slot, List<Attachment>>();
|
||||
|
||||
|
||||
//GUI stuff
|
||||
#if UNITY_4_3
|
||||
static bool showSlots;
|
||||
#else
|
||||
static AnimBool showSlots;
|
||||
#endif
|
||||
|
||||
void OnEnable () {
|
||||
skeletonUtility = (SkeletonUtility)target;
|
||||
skeletonRenderer = skeletonUtility.GetComponent<SkeletonRenderer>();
|
||||
skeleton = skeletonRenderer.skeleton;
|
||||
transform = skeletonRenderer.transform;
|
||||
|
||||
if (skeleton == null) {
|
||||
skeletonRenderer.Initialize(false);
|
||||
skeletonRenderer.LateUpdate();
|
||||
|
||||
void OnEnable () {
|
||||
skeletonUtility = (SkeletonUtility)target;
|
||||
skeletonRenderer = skeletonUtility.GetComponent<SkeletonRenderer>();
|
||||
skeleton = skeletonRenderer.skeleton;
|
||||
transform = skeletonRenderer.transform;
|
||||
|
||||
if (skeleton == null) {
|
||||
skeletonRenderer.Initialize(false);
|
||||
skeletonRenderer.LateUpdate();
|
||||
|
||||
skeleton = skeletonRenderer.skeleton;
|
||||
}
|
||||
|
||||
UpdateAttachments();
|
||||
|
||||
if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
|
||||
isPrefab = true;
|
||||
|
||||
}
|
||||
|
||||
UpdateAttachments();
|
||||
|
||||
if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
|
||||
isPrefab = true;
|
||||
|
||||
}
|
||||
|
||||
void OnSceneGUI () {
|
||||
if (skeleton == null) {
|
||||
OnEnable();
|
||||
return;
|
||||
}
|
||||
|
||||
// MITCH
|
||||
//float flipRotation = skeleton.FlipX ? -1 : 1;
|
||||
const float flipRotation = 1;
|
||||
|
||||
foreach (Bone b in skeleton.Bones) {
|
||||
Vector3 vec = transform.TransformPoint(new Vector3(b.WorldX, b.WorldY, 0));
|
||||
|
||||
// MITCH
|
||||
Quaternion rot = Quaternion.Euler(0, 0, b.WorldRotationX * flipRotation);
|
||||
Vector3 forward = transform.TransformDirection(rot * Vector3.right);
|
||||
forward *= flipRotation;
|
||||
|
||||
SpineEditorUtilities.Icons.boneMaterial.SetPass(0);
|
||||
Graphics.DrawMeshNow(SpineEditorUtilities.Icons.boneMesh, Matrix4x4.TRS(vec, Quaternion.LookRotation(transform.forward, forward), Vector3.one * b.Data.Length * b.WorldScaleX));
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAttachments () {
|
||||
attachmentTable = new Dictionary<Slot, List<Attachment>>();
|
||||
Skin skin = skeleton.Skin;
|
||||
|
||||
if (skin == null) {
|
||||
skin = skeletonRenderer.skeletonDataAsset.GetSkeletonData(true).DefaultSkin;
|
||||
}
|
||||
|
||||
for (int i = skeleton.Slots.Count-1; i >= 0; i--) {
|
||||
List<Attachment> attachments = new List<Attachment>();
|
||||
skin.FindAttachmentsForSlot(i, attachments);
|
||||
|
||||
attachmentTable.Add(skeleton.Slots.Items[i], attachments);
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnHierarchyButton (string label, string tooltip, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca, params GUILayoutOption[] options) {
|
||||
GUIContent content = new GUIContent(label, tooltip);
|
||||
if (GUILayout.Button(content, options)) {
|
||||
if (skeletonUtility.skeletonRenderer == null)
|
||||
skeletonUtility.skeletonRenderer = skeletonUtility.GetComponent<SkeletonRenderer>();
|
||||
|
||||
if (skeletonUtility.boneRoot != null) {
|
||||
void OnSceneGUI () {
|
||||
if (skeleton == null) {
|
||||
OnEnable();
|
||||
return;
|
||||
}
|
||||
|
||||
skeletonUtility.SpawnHierarchy(mode, pos, rot, sca);
|
||||
// MITCH
|
||||
//float flipRotation = skeleton.FlipX ? -1 : 1;
|
||||
const float flipRotation = 1;
|
||||
|
||||
SkeletonUtilityBone[] boneComps = skeletonUtility.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
foreach (SkeletonUtilityBone b in boneComps)
|
||||
AttachIcon(b);
|
||||
}
|
||||
}
|
||||
foreach (Bone b in skeleton.Bones) {
|
||||
Vector3 vec = transform.TransformPoint(new Vector3(b.WorldX, b.WorldY, 0));
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
if (isPrefab) {
|
||||
GUILayout.Label(new GUIContent("Cannot edit Prefabs", SpineEditorUtilities.Icons.warning));
|
||||
return;
|
||||
// MITCH
|
||||
Quaternion rot = Quaternion.Euler(0, 0, b.WorldRotationX * flipRotation);
|
||||
Vector3 forward = transform.TransformDirection(rot * Vector3.right);
|
||||
forward *= flipRotation;
|
||||
|
||||
SpineEditorUtilities.Icons.boneMaterial.SetPass(0);
|
||||
Graphics.DrawMeshNow(SpineEditorUtilities.Icons.boneMesh, Matrix4x4.TRS(vec, Quaternion.LookRotation(transform.forward, forward), Vector3.one * b.Data.Length * b.WorldScaleX));
|
||||
}
|
||||
}
|
||||
|
||||
skeletonUtility.boneRoot = (Transform)EditorGUILayout.ObjectField("Bone Root", skeletonUtility.boneRoot, typeof(Transform), true);
|
||||
void UpdateAttachments () {
|
||||
attachmentTable = new Dictionary<Slot, List<Attachment>>();
|
||||
Skin skin = skeleton.Skin;
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
EditorGUI.BeginDisabledGroup(skeletonUtility.boneRoot != null);
|
||||
{
|
||||
if (GUILayout.Button(new GUIContent("Spawn Hierarchy", SpineEditorUtilities.Icons.skeleton), GUILayout.Width(150), GUILayout.Height(24)))
|
||||
SpawnHierarchyContextMenu();
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
if (skin == null) {
|
||||
skin = skeletonRenderer.skeletonDataAsset.GetSkeletonData(true).DefaultSkin;
|
||||
}
|
||||
|
||||
// if (GUILayout.Button(new GUIContent("Spawn Submeshes", SpineEditorUtilities.Icons.subMeshRenderer), GUILayout.Width(150), GUILayout.Height(24)))
|
||||
// skeletonUtility.SpawnSubRenderers(true);
|
||||
for (int i = skeleton.Slots.Count-1; i >= 0; i--) {
|
||||
List<Attachment> attachments = new List<Attachment>();
|
||||
skin.FindAttachmentsForSlot(i, attachments);
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
skeleton.FlipX = EditorGUILayout.ToggleLeft("Flip X", skeleton.FlipX);
|
||||
skeleton.FlipY = EditorGUILayout.ToggleLeft("Flip Y", skeleton.FlipY);
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
skeletonRenderer.LateUpdate();
|
||||
SceneView.RepaintAll();
|
||||
attachmentTable.Add(skeleton.Slots.Items[i], attachments);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_4_3
|
||||
showSlots = EditorGUILayout.Foldout(showSlots, "Slots");
|
||||
#else
|
||||
showSlots.target = EditorGUILayout.Foldout(showSlots.target, "Slots");
|
||||
if (EditorGUILayout.BeginFadeGroup(showSlots.faded)) {
|
||||
#endif
|
||||
foreach (KeyValuePair<Slot, List<Attachment>> pair in attachmentTable) {
|
||||
void SpawnHierarchyButton (string label, string tooltip, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca, params GUILayoutOption[] options) {
|
||||
GUIContent content = new GUIContent(label, tooltip);
|
||||
if (GUILayout.Button(content, options)) {
|
||||
if (skeletonUtility.skeletonRenderer == null)
|
||||
skeletonUtility.skeletonRenderer = skeletonUtility.GetComponent<SkeletonRenderer>();
|
||||
|
||||
Slot slot = pair.Key;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUI.indentLevel = 1;
|
||||
EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, SpineEditorUtilities.Icons.slot), GUILayout.ExpandWidth(false));
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Color c = EditorGUILayout.ColorField(new Color(slot.R, slot.G, slot.B, slot.A), GUILayout.Width(60));
|
||||
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
slot.SetColor(c);
|
||||
skeletonRenderer.LateUpdate();
|
||||
if (skeletonUtility.boneRoot != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
skeletonUtility.SpawnHierarchy(mode, pos, rot, sca);
|
||||
|
||||
SkeletonUtilityBone[] boneComps = skeletonUtility.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
foreach (SkeletonUtilityBone b in boneComps)
|
||||
AttachIcon(b);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
if (isPrefab) {
|
||||
GUILayout.Label(new GUIContent("Cannot edit Prefabs", SpineEditorUtilities.Icons.warning));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Attachment attachment in pair.Value) {
|
||||
skeletonUtility.boneRoot = (Transform)EditorGUILayout.ObjectField("Bone Root", skeletonUtility.boneRoot, typeof(Transform), true);
|
||||
|
||||
if (slot.Attachment == attachment) {
|
||||
GUI.contentColor = Color.white;
|
||||
} else {
|
||||
GUI.contentColor = Color.grey;
|
||||
GUILayout.BeginHorizontal();
|
||||
EditorGUI.BeginDisabledGroup(skeletonUtility.boneRoot != null);
|
||||
{
|
||||
if (GUILayout.Button(new GUIContent("Spawn Hierarchy", SpineEditorUtilities.Icons.skeleton), GUILayout.Width(150), GUILayout.Height(24)))
|
||||
SpawnHierarchyContextMenu();
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
// if (GUILayout.Button(new GUIContent("Spawn Submeshes", SpineEditorUtilities.Icons.subMeshRenderer), GUILayout.Width(150), GUILayout.Height(24)))
|
||||
// skeletonUtility.SpawnSubRenderers(true);
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
skeleton.FlipX = EditorGUILayout.ToggleLeft("Flip X", skeleton.FlipX);
|
||||
skeleton.FlipY = EditorGUILayout.ToggleLeft("Flip Y", skeleton.FlipY);
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
skeletonRenderer.LateUpdate();
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
#if UNITY_4_3
|
||||
showSlots = EditorGUILayout.Foldout(showSlots, "Slots");
|
||||
#else
|
||||
showSlots.target = EditorGUILayout.Foldout(showSlots.target, "Slots");
|
||||
if (EditorGUILayout.BeginFadeGroup(showSlots.faded)) {
|
||||
#endif
|
||||
foreach (KeyValuePair<Slot, List<Attachment>> pair in attachmentTable) {
|
||||
|
||||
Slot slot = pair.Key;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUI.indentLevel = 1;
|
||||
EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, SpineEditorUtilities.Icons.slot), GUILayout.ExpandWidth(false));
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Color c = EditorGUILayout.ColorField(new Color(slot.R, slot.G, slot.B, slot.A), GUILayout.Width(60));
|
||||
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
slot.SetColor(c);
|
||||
skeletonRenderer.LateUpdate();
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel = 2;
|
||||
bool isAttached = attachment == slot.Attachment;
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
Texture2D icon = null;
|
||||
|
||||
if (attachment is MeshAttachment || attachment is WeightedMeshAttachment)
|
||||
icon = SpineEditorUtilities.Icons.mesh;
|
||||
else
|
||||
icon = SpineEditorUtilities.Icons.image;
|
||||
|
||||
bool swap = EditorGUILayout.ToggleLeft(new GUIContent(attachment.Name, icon), attachment == slot.Attachment);
|
||||
foreach (Attachment attachment in pair.Value) {
|
||||
|
||||
if (!isAttached && swap) {
|
||||
slot.Attachment = attachment;
|
||||
skeletonRenderer.LateUpdate();
|
||||
} else if (isAttached && !swap) {
|
||||
if (slot.Attachment == attachment) {
|
||||
GUI.contentColor = Color.white;
|
||||
} else {
|
||||
GUI.contentColor = Color.grey;
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel = 2;
|
||||
bool isAttached = attachment == slot.Attachment;
|
||||
|
||||
Texture2D icon = null;
|
||||
|
||||
if (attachment is MeshAttachment || attachment is WeightedMeshAttachment)
|
||||
icon = SpineEditorUtilities.Icons.mesh;
|
||||
else
|
||||
icon = SpineEditorUtilities.Icons.image;
|
||||
|
||||
bool swap = EditorGUILayout.ToggleLeft(new GUIContent(attachment.Name, icon), attachment == slot.Attachment);
|
||||
|
||||
if (!isAttached && swap) {
|
||||
slot.Attachment = attachment;
|
||||
skeletonRenderer.LateUpdate();
|
||||
} else if (isAttached && !swap) {
|
||||
slot.Attachment = null;
|
||||
skeletonRenderer.LateUpdate();
|
||||
}
|
||||
|
||||
GUI.contentColor = Color.white;
|
||||
GUI.contentColor = Color.white;
|
||||
}
|
||||
}
|
||||
#if UNITY_4_3
|
||||
|
||||
#else
|
||||
}
|
||||
#if UNITY_4_3
|
||||
|
||||
#else
|
||||
EditorGUILayout.EndFadeGroup();
|
||||
if (showSlots.isAnimating)
|
||||
Repaint();
|
||||
#endif
|
||||
}
|
||||
|
||||
void SpawnHierarchyContextMenu () {
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
menu.AddItem(new GUIContent("Follow"), false, SpawnFollowHierarchy);
|
||||
menu.AddItem(new GUIContent("Follow (Root Only)"), false, SpawnFollowHierarchyRootOnly);
|
||||
menu.AddSeparator("");
|
||||
menu.AddItem(new GUIContent("Override"), false, SpawnOverrideHierarchy);
|
||||
menu.AddItem(new GUIContent("Override (Root Only)"), false, SpawnOverrideHierarchyRootOnly);
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
void SpawnFollowHierarchy () {
|
||||
Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Follow, true, true, true);
|
||||
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||
}
|
||||
|
||||
void SpawnFollowHierarchyRootOnly () {
|
||||
Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Follow, true, true, true);
|
||||
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||
}
|
||||
|
||||
void SpawnOverrideHierarchy () {
|
||||
Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Override, true, true, true);
|
||||
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||
}
|
||||
|
||||
void SpawnOverrideHierarchyRootOnly () {
|
||||
Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Override, true, true, true);
|
||||
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||
}
|
||||
EditorGUILayout.EndFadeGroup();
|
||||
if (showSlots.isAnimating)
|
||||
Repaint();
|
||||
#endif
|
||||
}
|
||||
|
||||
void SpawnHierarchyContextMenu () {
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
menu.AddItem(new GUIContent("Follow"), false, SpawnFollowHierarchy);
|
||||
menu.AddItem(new GUIContent("Follow (Root Only)"), false, SpawnFollowHierarchyRootOnly);
|
||||
menu.AddSeparator("");
|
||||
menu.AddItem(new GUIContent("Override"), false, SpawnOverrideHierarchy);
|
||||
menu.AddItem(new GUIContent("Override (Root Only)"), false, SpawnOverrideHierarchyRootOnly);
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
void SpawnFollowHierarchy () {
|
||||
Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Follow, true, true, true);
|
||||
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||
}
|
||||
|
||||
void SpawnFollowHierarchyRootOnly () {
|
||||
Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Follow, true, true, true);
|
||||
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||
}
|
||||
|
||||
void SpawnOverrideHierarchy () {
|
||||
Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Override, true, true, true);
|
||||
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||
}
|
||||
|
||||
void SpawnOverrideHierarchyRootOnly () {
|
||||
Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Override, true, true, true);
|
||||
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.3
|
||||
*
|
||||
* Copyright (c) 2013-2015, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
* non-transferable license to use, install, execute and perform the Spine
|
||||
* Runtimes Software (the "Software") and derivative works solely for personal
|
||||
* or internal use. Without the written permission of Esoteric Software (see
|
||||
* Section 2 of the Spine Software License Agreement), 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 SOFTWARE 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.
|
||||
*****************************************************************************/
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Spine.Unity.Editor;
|
||||
|
||||
namespace Spine.Unity.Modules {
|
||||
[CustomEditor(typeof(SkeletonUtilitySubmeshRenderer))]
|
||||
public class SkeletonUtilitySubmeshRendererInspector : UnityEditor.Editor {
|
||||
public SpineInspectorUtility.SerializedSortingProperties sorting;
|
||||
|
||||
void OnEnable () {
|
||||
sorting = new SpineInspectorUtility.SerializedSortingProperties((target as Component).GetComponent<Renderer>());
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
EditorGUILayout.HelpBox("SkeletonUtilitySubmeshRenderer is now obsolete. We recommend using SkeletonRenderSeparator.", MessageType.Info);
|
||||
SpineInspectorUtility.SortingPropertyFields(sorting, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67418e462bd4dc24e8c234b92f1d4d9b
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@ -10,49 +10,76 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Spine;
|
||||
|
||||
[RequireComponent(typeof(ISkeletonAnimation))]
|
||||
[ExecuteInEditMode]
|
||||
public class SkeletonUtility : MonoBehaviour {
|
||||
namespace Spine.Unity {
|
||||
[RequireComponent(typeof(ISkeletonAnimation))]
|
||||
[ExecuteInEditMode]
|
||||
public class SkeletonUtility : MonoBehaviour {
|
||||
|
||||
public static T GetInParent<T> (Transform origin) where T : Component {
|
||||
#if UNITY_4_3
|
||||
Transform parent = origin.parent;
|
||||
while(parent.GetComponent<T>() == null){
|
||||
public static T GetInParent<T> (Transform origin) where T : Component {
|
||||
#if UNITY_4_3
|
||||
Transform parent = origin.parent;
|
||||
while(parent.GetComponent<T>() == null){
|
||||
parent = parent.parent;
|
||||
if(parent == null)
|
||||
return default(T);
|
||||
return default(T);
|
||||
}
|
||||
|
||||
return parent.GetComponent<T>();
|
||||
#else
|
||||
return origin.GetComponentInParent<T>();
|
||||
#endif
|
||||
}
|
||||
|
||||
return parent.GetComponent<T>();
|
||||
#else
|
||||
return origin.GetComponentInParent<T>();
|
||||
#endif
|
||||
}
|
||||
public static PolygonCollider2D AddBoundingBox (Skeleton skeleton, string skinName, string slotName, string attachmentName, Transform parent, bool isTrigger = true) {
|
||||
// List<Attachment> attachments = new List<Attachment>();
|
||||
Skin skin;
|
||||
|
||||
public static PolygonCollider2D AddBoundingBox (Skeleton skeleton, string skinName, string slotName, string attachmentName, Transform parent, bool isTrigger = true) {
|
||||
// List<Attachment> attachments = new List<Attachment>();
|
||||
Skin skin;
|
||||
if (skinName == "")
|
||||
skinName = skeleton.Data.DefaultSkin.Name;
|
||||
|
||||
if (skinName == "")
|
||||
skinName = skeleton.Data.DefaultSkin.Name;
|
||||
skin = skeleton.Data.FindSkin(skinName);
|
||||
|
||||
skin = skeleton.Data.FindSkin(skinName);
|
||||
if (skin == null) {
|
||||
Debug.LogError("Skin " + skinName + " not found!");
|
||||
return null;
|
||||
}
|
||||
|
||||
var attachment = skin.GetAttachment(skeleton.FindSlotIndex(slotName), attachmentName);
|
||||
if (attachment is BoundingBoxAttachment) {
|
||||
GameObject go = new GameObject("[BoundingBox]" + attachmentName);
|
||||
go.transform.parent = parent;
|
||||
go.transform.localPosition = Vector3.zero;
|
||||
go.transform.localRotation = Quaternion.identity;
|
||||
go.transform.localScale = Vector3.one;
|
||||
var collider = go.AddComponent<PolygonCollider2D>();
|
||||
collider.isTrigger = isTrigger;
|
||||
var boundingBox = (BoundingBoxAttachment)attachment;
|
||||
float[] floats = boundingBox.Vertices;
|
||||
int floatCount = floats.Length;
|
||||
int vertCount = floatCount / 2;
|
||||
|
||||
Vector2[] verts = new Vector2[vertCount];
|
||||
int v = 0;
|
||||
for (int i = 0; i < floatCount; i += 2, v++) {
|
||||
verts[v].x = floats[i];
|
||||
verts[v].y = floats[i + 1];
|
||||
}
|
||||
|
||||
collider.SetPath(0, verts);
|
||||
|
||||
return collider;
|
||||
|
||||
}
|
||||
|
||||
if (skin == null) {
|
||||
Debug.LogError("Skin " + skinName + " not found!");
|
||||
return null;
|
||||
}
|
||||
|
||||
var attachment = skin.GetAttachment(skeleton.FindSlotIndex(slotName), attachmentName);
|
||||
if (attachment is BoundingBoxAttachment) {
|
||||
GameObject go = new GameObject("[BoundingBox]" + attachmentName);
|
||||
go.transform.parent = parent;
|
||||
go.transform.localPosition = Vector3.zero;
|
||||
go.transform.localRotation = Quaternion.identity;
|
||||
go.transform.localScale = Vector3.one;
|
||||
var collider = go.AddComponent<PolygonCollider2D>();
|
||||
public static PolygonCollider2D AddBoundingBoxAsComponent (BoundingBoxAttachment boundingBox, GameObject gameObject, bool isTrigger = true) {
|
||||
if (boundingBox == null)
|
||||
return null;
|
||||
|
||||
var collider = gameObject.AddComponent<PolygonCollider2D>();
|
||||
collider.isTrigger = isTrigger;
|
||||
var boundingBox = (BoundingBoxAttachment)attachment;
|
||||
float[] floats = boundingBox.Vertices;
|
||||
int floatCount = floats.Length;
|
||||
int vertCount = floatCount / 2;
|
||||
@ -67,318 +94,294 @@ public class SkeletonUtility : MonoBehaviour {
|
||||
collider.SetPath(0, verts);
|
||||
|
||||
return collider;
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static Bounds GetBoundingBoxBounds (BoundingBoxAttachment boundingBox, float depth = 0) {
|
||||
float[] floats = boundingBox.Vertices;
|
||||
int floatCount = floats.Length;
|
||||
|
||||
public static PolygonCollider2D AddBoundingBoxAsComponent (BoundingBoxAttachment boundingBox, GameObject gameObject, bool isTrigger = true) {
|
||||
if (boundingBox == null)
|
||||
return null;
|
||||
Bounds bounds = new Bounds();
|
||||
|
||||
var collider = gameObject.AddComponent<PolygonCollider2D>();
|
||||
collider.isTrigger = isTrigger;
|
||||
float[] floats = boundingBox.Vertices;
|
||||
int floatCount = floats.Length;
|
||||
int vertCount = floatCount / 2;
|
||||
bounds.center = new Vector3(floats[0], floats[1], 0);
|
||||
for (int i = 2; i < floatCount; i += 2) {
|
||||
bounds.Encapsulate(new Vector3(floats[i], floats[i + 1], 0));
|
||||
}
|
||||
Vector3 size = bounds.size;
|
||||
size.z = depth;
|
||||
bounds.size = size;
|
||||
|
||||
Vector2[] verts = new Vector2[vertCount];
|
||||
int v = 0;
|
||||
for (int i = 0; i < floatCount; i += 2, v++) {
|
||||
verts[v].x = floats[i];
|
||||
verts[v].y = floats[i + 1];
|
||||
return bounds;
|
||||
}
|
||||
|
||||
collider.SetPath(0, verts);
|
||||
public delegate void SkeletonUtilityDelegate ();
|
||||
|
||||
return collider;
|
||||
}
|
||||
public event SkeletonUtilityDelegate OnReset;
|
||||
|
||||
public static Bounds GetBoundingBoxBounds (BoundingBoxAttachment boundingBox, float depth = 0) {
|
||||
float[] floats = boundingBox.Vertices;
|
||||
int floatCount = floats.Length;
|
||||
public Transform boneRoot;
|
||||
|
||||
Bounds bounds = new Bounds();
|
||||
void Update () {
|
||||
if (boneRoot != null && skeletonRenderer.skeleton != null) {
|
||||
Vector3 flipScale = Vector3.one;
|
||||
if (skeletonRenderer.skeleton.FlipX)
|
||||
flipScale.x = -1;
|
||||
|
||||
bounds.center = new Vector3(floats[0], floats[1], 0);
|
||||
for (int i = 2; i < floatCount; i += 2) {
|
||||
bounds.Encapsulate(new Vector3(floats[i], floats[i + 1], 0));
|
||||
}
|
||||
Vector3 size = bounds.size;
|
||||
size.z = depth;
|
||||
bounds.size = size;
|
||||
if (skeletonRenderer.skeleton.FlipY)
|
||||
flipScale.y = -1;
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public delegate void SkeletonUtilityDelegate ();
|
||||
|
||||
public event SkeletonUtilityDelegate OnReset;
|
||||
|
||||
public Transform boneRoot;
|
||||
|
||||
void Update () {
|
||||
if (boneRoot != null && skeletonRenderer.skeleton != null) {
|
||||
Vector3 flipScale = Vector3.one;
|
||||
if (skeletonRenderer.skeleton.FlipX)
|
||||
flipScale.x = -1;
|
||||
|
||||
if (skeletonRenderer.skeleton.FlipY)
|
||||
flipScale.y = -1;
|
||||
|
||||
boneRoot.localScale = flipScale;
|
||||
}
|
||||
}
|
||||
|
||||
[HideInInspector]
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
[HideInInspector]
|
||||
public ISkeletonAnimation skeletonAnimation;
|
||||
[System.NonSerialized]
|
||||
public List<SkeletonUtilityBone> utilityBones = new List<SkeletonUtilityBone>();
|
||||
[System.NonSerialized]
|
||||
public List<SkeletonUtilityConstraint> utilityConstraints = new List<SkeletonUtilityConstraint>();
|
||||
// Dictionary<Bone, SkeletonUtilityBone> utilityBoneTable;
|
||||
|
||||
protected bool hasTransformBones;
|
||||
protected bool hasUtilityConstraints;
|
||||
protected bool needToReprocessBones;
|
||||
|
||||
void OnEnable () {
|
||||
if (skeletonRenderer == null) {
|
||||
skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||
boneRoot.localScale = flipScale;
|
||||
}
|
||||
}
|
||||
|
||||
if (skeletonAnimation == null) {
|
||||
skeletonAnimation = GetComponent<SkeletonAnimation>();
|
||||
if (skeletonAnimation == null)
|
||||
skeletonAnimation = GetComponent<SkeletonAnimator>();
|
||||
}
|
||||
[HideInInspector]
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
[HideInInspector]
|
||||
public ISkeletonAnimation skeletonAnimation;
|
||||
[System.NonSerialized]
|
||||
public List<SkeletonUtilityBone> utilityBones = new List<SkeletonUtilityBone>();
|
||||
[System.NonSerialized]
|
||||
public List<SkeletonUtilityConstraint> utilityConstraints = new List<SkeletonUtilityConstraint>();
|
||||
// Dictionary<Bone, SkeletonUtilityBone> utilityBoneTable;
|
||||
|
||||
skeletonRenderer.OnRebuild -= HandleRendererReset;
|
||||
skeletonRenderer.OnRebuild += HandleRendererReset;
|
||||
protected bool hasTransformBones;
|
||||
protected bool hasUtilityConstraints;
|
||||
protected bool needToReprocessBones;
|
||||
|
||||
if (skeletonAnimation != null) {
|
||||
skeletonAnimation.UpdateLocal -= UpdateLocal;
|
||||
skeletonAnimation.UpdateLocal += UpdateLocal;
|
||||
}
|
||||
|
||||
|
||||
CollectBones();
|
||||
}
|
||||
|
||||
void Start () {
|
||||
//recollect because order of operations failure when switching between game mode and edit mode...
|
||||
// CollectBones();
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
skeletonRenderer.OnRebuild -= HandleRendererReset;
|
||||
|
||||
if (skeletonAnimation != null) {
|
||||
skeletonAnimation.UpdateLocal -= UpdateLocal;
|
||||
skeletonAnimation.UpdateWorld -= UpdateWorld;
|
||||
skeletonAnimation.UpdateComplete -= UpdateComplete;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleRendererReset (SkeletonRenderer r) {
|
||||
if (OnReset != null)
|
||||
OnReset();
|
||||
|
||||
CollectBones();
|
||||
}
|
||||
|
||||
public void RegisterBone (SkeletonUtilityBone bone) {
|
||||
if (utilityBones.Contains(bone))
|
||||
return;
|
||||
else {
|
||||
utilityBones.Add(bone);
|
||||
needToReprocessBones = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void UnregisterBone (SkeletonUtilityBone bone) {
|
||||
utilityBones.Remove(bone);
|
||||
}
|
||||
|
||||
public void RegisterConstraint (SkeletonUtilityConstraint constraint) {
|
||||
|
||||
if (utilityConstraints.Contains(constraint))
|
||||
return;
|
||||
else {
|
||||
utilityConstraints.Add(constraint);
|
||||
needToReprocessBones = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void UnregisterConstraint (SkeletonUtilityConstraint constraint) {
|
||||
utilityConstraints.Remove(constraint);
|
||||
}
|
||||
|
||||
public void CollectBones () {
|
||||
if (skeletonRenderer.skeleton == null)
|
||||
return;
|
||||
|
||||
if (boneRoot != null) {
|
||||
List<string> constraintTargetNames = new List<string>();
|
||||
|
||||
ExposedList<IkConstraint> ikConstraints = skeletonRenderer.skeleton.IkConstraints;
|
||||
for (int i = 0, n = ikConstraints.Count; i < n; i++)
|
||||
constraintTargetNames.Add(ikConstraints.Items[i].Target.Data.Name);
|
||||
|
||||
foreach (var b in utilityBones) {
|
||||
if (b.bone == null) {
|
||||
return;
|
||||
}
|
||||
if (b.mode == SkeletonUtilityBone.Mode.Override) {
|
||||
hasTransformBones = true;
|
||||
}
|
||||
|
||||
if (constraintTargetNames.Contains(b.bone.Data.Name)) {
|
||||
hasUtilityConstraints = true;
|
||||
}
|
||||
void OnEnable () {
|
||||
if (skeletonRenderer == null) {
|
||||
skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||
}
|
||||
|
||||
if (utilityConstraints.Count > 0)
|
||||
hasUtilityConstraints = true;
|
||||
if (skeletonAnimation == null) {
|
||||
skeletonAnimation = GetComponent<SkeletonAnimation>();
|
||||
if (skeletonAnimation == null)
|
||||
skeletonAnimation = GetComponent<SkeletonAnimator>();
|
||||
}
|
||||
|
||||
skeletonRenderer.OnRebuild -= HandleRendererReset;
|
||||
skeletonRenderer.OnRebuild += HandleRendererReset;
|
||||
|
||||
if (skeletonAnimation != null) {
|
||||
skeletonAnimation.UpdateWorld -= UpdateWorld;
|
||||
skeletonAnimation.UpdateComplete -= UpdateComplete;
|
||||
|
||||
if (hasTransformBones || hasUtilityConstraints) {
|
||||
skeletonAnimation.UpdateWorld += UpdateWorld;
|
||||
}
|
||||
|
||||
if (hasUtilityConstraints) {
|
||||
skeletonAnimation.UpdateComplete += UpdateComplete;
|
||||
}
|
||||
skeletonAnimation.UpdateLocal -= UpdateLocal;
|
||||
skeletonAnimation.UpdateLocal += UpdateLocal;
|
||||
}
|
||||
|
||||
needToReprocessBones = false;
|
||||
} else {
|
||||
utilityBones.Clear();
|
||||
utilityConstraints.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void UpdateLocal (ISkeletonAnimation anim) {
|
||||
|
||||
if (needToReprocessBones)
|
||||
CollectBones();
|
||||
|
||||
if (utilityBones == null)
|
||||
return;
|
||||
|
||||
foreach (SkeletonUtilityBone b in utilityBones) {
|
||||
b.transformLerpComplete = false;
|
||||
}
|
||||
|
||||
UpdateAllBones();
|
||||
}
|
||||
|
||||
void UpdateWorld (ISkeletonAnimation anim) {
|
||||
UpdateAllBones();
|
||||
|
||||
foreach (SkeletonUtilityConstraint c in utilityConstraints)
|
||||
c.DoUpdate();
|
||||
}
|
||||
|
||||
void UpdateComplete (ISkeletonAnimation anim) {
|
||||
UpdateAllBones();
|
||||
}
|
||||
|
||||
void UpdateAllBones () {
|
||||
if (boneRoot == null) {
|
||||
CollectBones();
|
||||
}
|
||||
|
||||
if (utilityBones == null)
|
||||
return;
|
||||
|
||||
foreach (SkeletonUtilityBone b in utilityBones) {
|
||||
b.DoUpdate();
|
||||
void Start () {
|
||||
//recollect because order of operations failure when switching between game mode and edit mode...
|
||||
// CollectBones();
|
||||
}
|
||||
}
|
||||
|
||||
public Transform GetBoneRoot () {
|
||||
if (boneRoot != null)
|
||||
void OnDisable () {
|
||||
skeletonRenderer.OnRebuild -= HandleRendererReset;
|
||||
|
||||
if (skeletonAnimation != null) {
|
||||
skeletonAnimation.UpdateLocal -= UpdateLocal;
|
||||
skeletonAnimation.UpdateWorld -= UpdateWorld;
|
||||
skeletonAnimation.UpdateComplete -= UpdateComplete;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleRendererReset (SkeletonRenderer r) {
|
||||
if (OnReset != null)
|
||||
OnReset();
|
||||
|
||||
CollectBones();
|
||||
}
|
||||
|
||||
public void RegisterBone (SkeletonUtilityBone bone) {
|
||||
if (utilityBones.Contains(bone))
|
||||
return;
|
||||
else {
|
||||
utilityBones.Add(bone);
|
||||
needToReprocessBones = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void UnregisterBone (SkeletonUtilityBone bone) {
|
||||
utilityBones.Remove(bone);
|
||||
}
|
||||
|
||||
public void RegisterConstraint (SkeletonUtilityConstraint constraint) {
|
||||
|
||||
if (utilityConstraints.Contains(constraint))
|
||||
return;
|
||||
else {
|
||||
utilityConstraints.Add(constraint);
|
||||
needToReprocessBones = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void UnregisterConstraint (SkeletonUtilityConstraint constraint) {
|
||||
utilityConstraints.Remove(constraint);
|
||||
}
|
||||
|
||||
public void CollectBones () {
|
||||
if (skeletonRenderer.skeleton == null)
|
||||
return;
|
||||
|
||||
if (boneRoot != null) {
|
||||
List<string> constraintTargetNames = new List<string>();
|
||||
|
||||
ExposedList<IkConstraint> ikConstraints = skeletonRenderer.skeleton.IkConstraints;
|
||||
for (int i = 0, n = ikConstraints.Count; i < n; i++)
|
||||
constraintTargetNames.Add(ikConstraints.Items[i].Target.Data.Name);
|
||||
|
||||
foreach (var b in utilityBones) {
|
||||
if (b.bone == null) {
|
||||
return;
|
||||
}
|
||||
if (b.mode == SkeletonUtilityBone.Mode.Override) {
|
||||
hasTransformBones = true;
|
||||
}
|
||||
|
||||
if (constraintTargetNames.Contains(b.bone.Data.Name)) {
|
||||
hasUtilityConstraints = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (utilityConstraints.Count > 0)
|
||||
hasUtilityConstraints = true;
|
||||
|
||||
if (skeletonAnimation != null) {
|
||||
skeletonAnimation.UpdateWorld -= UpdateWorld;
|
||||
skeletonAnimation.UpdateComplete -= UpdateComplete;
|
||||
|
||||
if (hasTransformBones || hasUtilityConstraints) {
|
||||
skeletonAnimation.UpdateWorld += UpdateWorld;
|
||||
}
|
||||
|
||||
if (hasUtilityConstraints) {
|
||||
skeletonAnimation.UpdateComplete += UpdateComplete;
|
||||
}
|
||||
}
|
||||
|
||||
needToReprocessBones = false;
|
||||
} else {
|
||||
utilityBones.Clear();
|
||||
utilityConstraints.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void UpdateLocal (ISkeletonAnimation anim) {
|
||||
|
||||
if (needToReprocessBones)
|
||||
CollectBones();
|
||||
|
||||
if (utilityBones == null)
|
||||
return;
|
||||
|
||||
foreach (SkeletonUtilityBone b in utilityBones) {
|
||||
b.transformLerpComplete = false;
|
||||
}
|
||||
|
||||
UpdateAllBones();
|
||||
}
|
||||
|
||||
void UpdateWorld (ISkeletonAnimation anim) {
|
||||
UpdateAllBones();
|
||||
|
||||
foreach (SkeletonUtilityConstraint c in utilityConstraints)
|
||||
c.DoUpdate();
|
||||
}
|
||||
|
||||
void UpdateComplete (ISkeletonAnimation anim) {
|
||||
UpdateAllBones();
|
||||
}
|
||||
|
||||
void UpdateAllBones () {
|
||||
if (boneRoot == null) {
|
||||
CollectBones();
|
||||
}
|
||||
|
||||
if (utilityBones == null)
|
||||
return;
|
||||
|
||||
foreach (SkeletonUtilityBone b in utilityBones) {
|
||||
b.DoUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public Transform GetBoneRoot () {
|
||||
if (boneRoot != null)
|
||||
return boneRoot;
|
||||
|
||||
boneRoot = new GameObject("SkeletonUtility-Root").transform;
|
||||
boneRoot.parent = transform;
|
||||
boneRoot.localPosition = Vector3.zero;
|
||||
boneRoot.localRotation = Quaternion.identity;
|
||||
boneRoot.localScale = Vector3.one;
|
||||
|
||||
return boneRoot;
|
||||
|
||||
boneRoot = new GameObject("SkeletonUtility-Root").transform;
|
||||
boneRoot.parent = transform;
|
||||
boneRoot.localPosition = Vector3.zero;
|
||||
boneRoot.localRotation = Quaternion.identity;
|
||||
boneRoot.localScale = Vector3.one;
|
||||
|
||||
return boneRoot;
|
||||
}
|
||||
|
||||
public GameObject SpawnRoot (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
|
||||
GetBoneRoot();
|
||||
Skeleton skeleton = this.skeletonRenderer.skeleton;
|
||||
|
||||
GameObject go = SpawnBone(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
|
||||
|
||||
CollectBones();
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
public GameObject SpawnHierarchy (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
|
||||
GetBoneRoot();
|
||||
|
||||
Skeleton skeleton = this.skeletonRenderer.skeleton;
|
||||
|
||||
GameObject go = SpawnBoneRecursively(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
|
||||
|
||||
CollectBones();
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
public GameObject SpawnBoneRecursively (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
|
||||
GameObject go = SpawnBone(bone, parent, mode, pos, rot, sca);
|
||||
|
||||
ExposedList<Bone> childrenBones = bone.Children;
|
||||
for (int i = 0, n = childrenBones.Count; i < n; i++) {
|
||||
Bone child = childrenBones.Items[i];
|
||||
SpawnBoneRecursively(child, go.transform, mode, pos, rot, sca);
|
||||
}
|
||||
|
||||
return go;
|
||||
}
|
||||
public GameObject SpawnRoot (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
|
||||
GetBoneRoot();
|
||||
Skeleton skeleton = this.skeletonRenderer.skeleton;
|
||||
|
||||
public GameObject SpawnBone (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
|
||||
GameObject go = new GameObject(bone.Data.Name);
|
||||
go.transform.parent = parent;
|
||||
GameObject go = SpawnBone(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
|
||||
|
||||
SkeletonUtilityBone b = go.AddComponent<SkeletonUtilityBone>();
|
||||
b.skeletonUtility = this;
|
||||
b.position = pos;
|
||||
b.rotation = rot;
|
||||
b.scale = sca;
|
||||
b.mode = mode;
|
||||
b.zPosition = true;
|
||||
b.Reset();
|
||||
b.bone = bone;
|
||||
b.boneName = bone.Data.Name;
|
||||
b.valid = true;
|
||||
CollectBones();
|
||||
|
||||
if (mode == SkeletonUtilityBone.Mode.Override) {
|
||||
if (rot)
|
||||
go.transform.localRotation = Quaternion.Euler(0, 0, b.bone.AppliedRotation);
|
||||
|
||||
if (pos)
|
||||
go.transform.localPosition = new Vector3(b.bone.X, b.bone.Y, 0);
|
||||
|
||||
go.transform.localScale = new Vector3(b.bone.scaleX, b.bone.scaleY, 0);
|
||||
return go;
|
||||
}
|
||||
|
||||
public GameObject SpawnHierarchy (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
|
||||
GetBoneRoot();
|
||||
|
||||
Skeleton skeleton = this.skeletonRenderer.skeleton;
|
||||
|
||||
GameObject go = SpawnBoneRecursively(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
|
||||
|
||||
CollectBones();
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
public GameObject SpawnBoneRecursively (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
|
||||
GameObject go = SpawnBone(bone, parent, mode, pos, rot, sca);
|
||||
|
||||
ExposedList<Bone> childrenBones = bone.Children;
|
||||
for (int i = 0, n = childrenBones.Count; i < n; i++) {
|
||||
Bone child = childrenBones.Items[i];
|
||||
SpawnBoneRecursively(child, go.transform, mode, pos, rot, sca);
|
||||
}
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
public GameObject SpawnBone (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
|
||||
GameObject go = new GameObject(bone.Data.Name);
|
||||
go.transform.parent = parent;
|
||||
|
||||
SkeletonUtilityBone b = go.AddComponent<SkeletonUtilityBone>();
|
||||
b.skeletonUtility = this;
|
||||
b.position = pos;
|
||||
b.rotation = rot;
|
||||
b.scale = sca;
|
||||
b.mode = mode;
|
||||
b.zPosition = true;
|
||||
b.Reset();
|
||||
b.bone = bone;
|
||||
b.boneName = bone.Data.Name;
|
||||
b.valid = true;
|
||||
|
||||
if (mode == SkeletonUtilityBone.Mode.Override) {
|
||||
if (rot)
|
||||
go.transform.localRotation = Quaternion.Euler(0, 0, b.bone.AppliedRotation);
|
||||
|
||||
if (pos)
|
||||
go.transform.localPosition = new Vector3(b.bone.X, b.bone.Y, 0);
|
||||
|
||||
go.transform.localScale = new Vector3(b.bone.scaleX, b.bone.scaleY, 0);
|
||||
}
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11,274 +11,276 @@ using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Spine;
|
||||
|
||||
/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Spine/SkeletonUtilityBone")]
|
||||
public class SkeletonUtilityBone : MonoBehaviour {
|
||||
namespace Spine.Unity {
|
||||
/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Spine/SkeletonUtilityBone")]
|
||||
public class SkeletonUtilityBone : MonoBehaviour {
|
||||
|
||||
public enum Mode {
|
||||
Follow,
|
||||
Override
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
public bool valid;
|
||||
[System.NonSerialized]
|
||||
public SkeletonUtility skeletonUtility;
|
||||
[System.NonSerialized]
|
||||
public Bone bone;
|
||||
public Mode mode;
|
||||
public bool zPosition = true;
|
||||
public bool position;
|
||||
public bool rotation;
|
||||
public bool scale;
|
||||
// MITCH : remove flipX
|
||||
// Kept these fields public to retain serialization. Probably remove eventually?
|
||||
public bool flip;
|
||||
public bool flipX;
|
||||
[Range(0f, 1f)]
|
||||
public float overrideAlpha = 1;
|
||||
|
||||
/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
|
||||
public String boneName;
|
||||
public Transform parentReference;
|
||||
|
||||
[System.NonSerialized]
|
||||
public bool transformLerpComplete;
|
||||
|
||||
protected Transform cachedTransform;
|
||||
protected Transform skeletonTransform;
|
||||
|
||||
// MITCH : nonuniform scale
|
||||
// private bool nonUniformScaleWarning;
|
||||
// public bool NonUniformScaleWarning {
|
||||
// get { return nonUniformScaleWarning; }
|
||||
// }
|
||||
|
||||
private bool disableInheritScaleWarning;
|
||||
public bool DisableInheritScaleWarning {
|
||||
get { return disableInheritScaleWarning; }
|
||||
}
|
||||
|
||||
public void Reset () {
|
||||
bone = null;
|
||||
cachedTransform = transform;
|
||||
valid = skeletonUtility != null && skeletonUtility.skeletonRenderer != null && skeletonUtility.skeletonRenderer.valid;
|
||||
if (!valid)
|
||||
return;
|
||||
skeletonTransform = skeletonUtility.transform;
|
||||
skeletonUtility.OnReset -= HandleOnReset;
|
||||
skeletonUtility.OnReset += HandleOnReset;
|
||||
DoUpdate();
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
skeletonUtility = SkeletonUtility.GetInParent<SkeletonUtility>(transform);
|
||||
|
||||
if (skeletonUtility == null)
|
||||
return;
|
||||
|
||||
skeletonUtility.RegisterBone(this);
|
||||
skeletonUtility.OnReset += HandleOnReset;
|
||||
}
|
||||
|
||||
void HandleOnReset () {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
if (skeletonUtility != null) {
|
||||
skeletonUtility.OnReset -= HandleOnReset;
|
||||
skeletonUtility.UnregisterBone(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void DoUpdate () {
|
||||
if (!valid) {
|
||||
Reset();
|
||||
return;
|
||||
public enum Mode {
|
||||
Follow,
|
||||
Override
|
||||
}
|
||||
|
||||
Spine.Skeleton skeleton = skeletonUtility.skeletonRenderer.skeleton;
|
||||
[System.NonSerialized]
|
||||
public bool valid;
|
||||
[System.NonSerialized]
|
||||
public SkeletonUtility skeletonUtility;
|
||||
[System.NonSerialized]
|
||||
public Bone bone;
|
||||
public Mode mode;
|
||||
public bool zPosition = true;
|
||||
public bool position;
|
||||
public bool rotation;
|
||||
public bool scale;
|
||||
// MITCH : remove flipX
|
||||
// Kept these fields public to retain serialization. Probably remove eventually?
|
||||
public bool flip;
|
||||
public bool flipX;
|
||||
[Range(0f, 1f)]
|
||||
public float overrideAlpha = 1;
|
||||
|
||||
if (bone == null) {
|
||||
if (boneName == null || boneName.Length == 0)
|
||||
/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
|
||||
public String boneName;
|
||||
public Transform parentReference;
|
||||
|
||||
[System.NonSerialized]
|
||||
public bool transformLerpComplete;
|
||||
|
||||
protected Transform cachedTransform;
|
||||
protected Transform skeletonTransform;
|
||||
|
||||
// MITCH : nonuniform scale
|
||||
// private bool nonUniformScaleWarning;
|
||||
// public bool NonUniformScaleWarning {
|
||||
// get { return nonUniformScaleWarning; }
|
||||
// }
|
||||
|
||||
private bool disableInheritScaleWarning;
|
||||
public bool DisableInheritScaleWarning {
|
||||
get { return disableInheritScaleWarning; }
|
||||
}
|
||||
|
||||
public void Reset () {
|
||||
bone = null;
|
||||
cachedTransform = transform;
|
||||
valid = skeletonUtility != null && skeletonUtility.skeletonRenderer != null && skeletonUtility.skeletonRenderer.valid;
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
bone = skeleton.FindBone(boneName);
|
||||
skeletonTransform = skeletonUtility.transform;
|
||||
skeletonUtility.OnReset -= HandleOnReset;
|
||||
skeletonUtility.OnReset += HandleOnReset;
|
||||
DoUpdate();
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
skeletonUtility = SkeletonUtility.GetInParent<SkeletonUtility>(transform);
|
||||
|
||||
if (skeletonUtility == null)
|
||||
return;
|
||||
|
||||
skeletonUtility.RegisterBone(this);
|
||||
skeletonUtility.OnReset += HandleOnReset;
|
||||
}
|
||||
|
||||
void HandleOnReset () {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
if (skeletonUtility != null) {
|
||||
skeletonUtility.OnReset -= HandleOnReset;
|
||||
skeletonUtility.UnregisterBone(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void DoUpdate () {
|
||||
if (!valid) {
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
Spine.Skeleton skeleton = skeletonUtility.skeletonRenderer.skeleton;
|
||||
|
||||
if (bone == null) {
|
||||
Debug.LogError("Bone not found: " + boneName, this);
|
||||
return;
|
||||
if (boneName == null || boneName.Length == 0)
|
||||
return;
|
||||
|
||||
bone = skeleton.FindBone(boneName);
|
||||
|
||||
if (bone == null) {
|
||||
Debug.LogError("Bone not found: " + boneName, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float skeletonFlipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f;
|
||||
float skeletonFlipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f;
|
||||
|
||||
// MITCH : remove flipX
|
||||
// float flipCompensation = 0;
|
||||
// if (flip && (flipX || (flipX != bone.flipX)) && bone.parent != null) {
|
||||
// flipCompensation = bone.parent.WorldRotation * -2;
|
||||
// }
|
||||
|
||||
if (mode == Mode.Follow) {
|
||||
// MITCH : remove flipX
|
||||
// if (flip)
|
||||
// flipX = bone.flipX;
|
||||
|
||||
if (position)
|
||||
cachedTransform.localPosition = new Vector3(bone.x, bone.y, 0);
|
||||
// float flipCompensation = 0;
|
||||
// if (flip && (flipX || (flipX != bone.flipX)) && bone.parent != null) {
|
||||
// flipCompensation = bone.parent.WorldRotation * -2;
|
||||
// }
|
||||
|
||||
if (rotation) {
|
||||
if (bone.Data.InheritRotation) {
|
||||
// MITCH : remove flipX
|
||||
//if (bone.FlipX) {
|
||||
// cachedTransform.localRotation = Quaternion.Euler(0, 180, bone.rotationIK - flipCompensation);
|
||||
//} else {
|
||||
cachedTransform.localRotation = Quaternion.Euler(0, 0, bone.AppliedRotation);
|
||||
//}
|
||||
} else {
|
||||
Vector3 euler = skeletonTransform.rotation.eulerAngles;
|
||||
cachedTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation));
|
||||
}
|
||||
}
|
||||
if (mode == Mode.Follow) {
|
||||
// MITCH : remove flipX
|
||||
// if (flip)
|
||||
// flipX = bone.flipX;
|
||||
|
||||
if (scale) {
|
||||
cachedTransform.localScale = new Vector3(bone.scaleX, bone.scaleY, bone.WorldSignX);
|
||||
// MITCH : nonuniform scale
|
||||
//nonUniformScaleWarning = (bone.scaleX != bone.scaleY);
|
||||
disableInheritScaleWarning = !bone.data.inheritScale;
|
||||
}
|
||||
|
||||
} else if (mode == Mode.Override) {
|
||||
if (transformLerpComplete)
|
||||
return;
|
||||
|
||||
if (parentReference == null) {
|
||||
if (position) {
|
||||
bone.x = Mathf.Lerp(bone.x, cachedTransform.localPosition.x, overrideAlpha);
|
||||
bone.y = Mathf.Lerp(bone.y, cachedTransform.localPosition.y, overrideAlpha);
|
||||
}
|
||||
if (position)
|
||||
cachedTransform.localPosition = new Vector3(bone.x, bone.y, 0);
|
||||
|
||||
if (rotation) {
|
||||
float angle = Mathf.LerpAngle(bone.Rotation, cachedTransform.localRotation.eulerAngles.z, overrideAlpha);
|
||||
|
||||
// MITCH : remove flipX
|
||||
// float angle = Mathf.LerpAngle(bone.Rotation, cachedTransform.localRotation.eulerAngles.z, overrideAlpha) + flipCompensation;
|
||||
// if (flip) {
|
||||
//
|
||||
// if ((!flipX && bone.flipX)) {
|
||||
// angle -= flipCompensation;
|
||||
// }
|
||||
//
|
||||
// //TODO fix this...
|
||||
// if (angle >= 360)
|
||||
// angle -= 360;
|
||||
// else if (angle <= -360)
|
||||
// angle += 360;
|
||||
// }
|
||||
bone.Rotation = angle;
|
||||
bone.AppliedRotation = angle;
|
||||
if (bone.Data.InheritRotation) {
|
||||
// MITCH : remove flipX
|
||||
//if (bone.FlipX) {
|
||||
// cachedTransform.localRotation = Quaternion.Euler(0, 180, bone.rotationIK - flipCompensation);
|
||||
//} else {
|
||||
cachedTransform.localRotation = Quaternion.Euler(0, 0, bone.AppliedRotation);
|
||||
//}
|
||||
} else {
|
||||
Vector3 euler = skeletonTransform.rotation.eulerAngles;
|
||||
cachedTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation));
|
||||
}
|
||||
}
|
||||
|
||||
if (scale) {
|
||||
bone.scaleX = Mathf.Lerp(bone.scaleX, cachedTransform.localScale.x, overrideAlpha);
|
||||
bone.scaleY = Mathf.Lerp(bone.scaleY, cachedTransform.localScale.y, overrideAlpha);
|
||||
cachedTransform.localScale = new Vector3(bone.scaleX, bone.scaleY, bone.WorldSignX);
|
||||
// MITCH : nonuniform scale
|
||||
//nonUniformScaleWarning = (bone.scaleX != bone.scaleY);
|
||||
disableInheritScaleWarning = !bone.data.inheritScale;
|
||||
}
|
||||
|
||||
// MITCH : remove flipX
|
||||
//if (flip)
|
||||
// bone.flipX = flipX;
|
||||
} else {
|
||||
} else if (mode == Mode.Override) {
|
||||
if (transformLerpComplete)
|
||||
return;
|
||||
|
||||
if (position) {
|
||||
Vector3 pos = parentReference.InverseTransformPoint(cachedTransform.position);
|
||||
bone.x = Mathf.Lerp(bone.x, pos.x, overrideAlpha);
|
||||
bone.y = Mathf.Lerp(bone.y, pos.y, overrideAlpha);
|
||||
}
|
||||
if (parentReference == null) {
|
||||
if (position) {
|
||||
bone.x = Mathf.Lerp(bone.x, cachedTransform.localPosition.x, overrideAlpha);
|
||||
bone.y = Mathf.Lerp(bone.y, cachedTransform.localPosition.y, overrideAlpha);
|
||||
}
|
||||
|
||||
// MITCH
|
||||
if (rotation) {
|
||||
float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(flipX ? Vector3.forward * -1 : Vector3.forward, parentReference.InverseTransformDirection(cachedTransform.up)).eulerAngles.z, overrideAlpha);
|
||||
if (rotation) {
|
||||
float angle = Mathf.LerpAngle(bone.Rotation, cachedTransform.localRotation.eulerAngles.z, overrideAlpha);
|
||||
|
||||
// MITCH : remove flipX
|
||||
// float angle = Mathf.LerpAngle(bone.Rotation, cachedTransform.localRotation.eulerAngles.z, overrideAlpha) + flipCompensation;
|
||||
// if (flip) {
|
||||
//
|
||||
// if ((!flipX && bone.flipX)) {
|
||||
// angle -= flipCompensation;
|
||||
// }
|
||||
//
|
||||
// //TODO fix this...
|
||||
// if (angle >= 360)
|
||||
// angle -= 360;
|
||||
// else if (angle <= -360)
|
||||
// angle += 360;
|
||||
// }
|
||||
bone.Rotation = angle;
|
||||
bone.AppliedRotation = angle;
|
||||
}
|
||||
|
||||
if (scale) {
|
||||
bone.scaleX = Mathf.Lerp(bone.scaleX, cachedTransform.localScale.x, overrideAlpha);
|
||||
bone.scaleY = Mathf.Lerp(bone.scaleY, cachedTransform.localScale.y, overrideAlpha);
|
||||
// MITCH : nonuniform scale
|
||||
//nonUniformScaleWarning = (bone.scaleX != bone.scaleY);
|
||||
}
|
||||
|
||||
// MITCH : remove flipX
|
||||
// float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(flipX ? Vector3.forward * -1 : Vector3.forward, parentReference.InverseTransformDirection(cachedTransform.up)).eulerAngles.z, overrideAlpha) + flipCompensation;
|
||||
// if (flip) {
|
||||
//
|
||||
// if ((!flipX && bone.flipX)) {
|
||||
// angle -= flipCompensation;
|
||||
// }
|
||||
//
|
||||
// //TODO fix this...
|
||||
// if (angle >= 360)
|
||||
// angle -= 360;
|
||||
// else if (angle <= -360)
|
||||
// angle += 360;
|
||||
// }
|
||||
bone.Rotation = angle;
|
||||
bone.AppliedRotation = angle;
|
||||
}
|
||||
|
||||
if (scale) {
|
||||
bone.scaleX = Mathf.Lerp(bone.scaleX, cachedTransform.localScale.x, overrideAlpha);
|
||||
bone.scaleY = Mathf.Lerp(bone.scaleY, cachedTransform.localScale.y, overrideAlpha);
|
||||
// MITCH : nonuniform scale
|
||||
//nonUniformScaleWarning = (bone.scaleX != bone.scaleY);
|
||||
//if (flip)
|
||||
// bone.flipX = flipX;
|
||||
} else {
|
||||
if (transformLerpComplete)
|
||||
return;
|
||||
|
||||
if (position) {
|
||||
Vector3 pos = parentReference.InverseTransformPoint(cachedTransform.position);
|
||||
bone.x = Mathf.Lerp(bone.x, pos.x, overrideAlpha);
|
||||
bone.y = Mathf.Lerp(bone.y, pos.y, overrideAlpha);
|
||||
}
|
||||
|
||||
// MITCH
|
||||
if (rotation) {
|
||||
float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(flipX ? Vector3.forward * -1 : Vector3.forward, parentReference.InverseTransformDirection(cachedTransform.up)).eulerAngles.z, overrideAlpha);
|
||||
|
||||
// MITCH : remove flipX
|
||||
// float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(flipX ? Vector3.forward * -1 : Vector3.forward, parentReference.InverseTransformDirection(cachedTransform.up)).eulerAngles.z, overrideAlpha) + flipCompensation;
|
||||
// if (flip) {
|
||||
//
|
||||
// if ((!flipX && bone.flipX)) {
|
||||
// angle -= flipCompensation;
|
||||
// }
|
||||
//
|
||||
// //TODO fix this...
|
||||
// if (angle >= 360)
|
||||
// angle -= 360;
|
||||
// else if (angle <= -360)
|
||||
// angle += 360;
|
||||
// }
|
||||
bone.Rotation = angle;
|
||||
bone.AppliedRotation = angle;
|
||||
}
|
||||
|
||||
if (scale) {
|
||||
bone.scaleX = Mathf.Lerp(bone.scaleX, cachedTransform.localScale.x, overrideAlpha);
|
||||
bone.scaleY = Mathf.Lerp(bone.scaleY, cachedTransform.localScale.y, overrideAlpha);
|
||||
// MITCH : nonuniform scale
|
||||
//nonUniformScaleWarning = (bone.scaleX != bone.scaleY);
|
||||
}
|
||||
|
||||
disableInheritScaleWarning = !bone.data.inheritScale;
|
||||
|
||||
// MITCH : remove flipX
|
||||
//if (flip)
|
||||
// bone.flipX = flipX;
|
||||
}
|
||||
|
||||
disableInheritScaleWarning = !bone.data.inheritScale;
|
||||
|
||||
// MITCH : remove flipX
|
||||
//if (flip)
|
||||
// bone.flipX = flipX;
|
||||
transformLerpComplete = true;
|
||||
}
|
||||
|
||||
transformLerpComplete = true;
|
||||
}
|
||||
|
||||
// MITCH : remove flipX
|
||||
// public void FlipX (bool state) {
|
||||
// if (state != flipX) {
|
||||
// flipX = state;
|
||||
// if (flipX && Mathf.Abs(transform.localRotation.eulerAngles.y) > 90) {
|
||||
// skeletonUtility.skeletonAnimation.LateUpdate();
|
||||
// return;
|
||||
// } else if (!flipX && Mathf.Abs(transform.localRotation.eulerAngles.y) < 90) {
|
||||
// skeletonUtility.skeletonAnimation.LateUpdate();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// bone.FlipX = state;
|
||||
// transform.RotateAround(transform.position, skeletonUtility.transform.up, 180);
|
||||
// Vector3 euler = transform.localRotation.eulerAngles;
|
||||
// euler.x = 0;
|
||||
//
|
||||
// euler.y = bone.FlipX ? 180 : 0;
|
||||
// euler.y = 0;
|
||||
// transform.localRotation = Quaternion.Euler(euler);
|
||||
// }
|
||||
|
||||
public void AddBoundingBox (string skinName, string slotName, string attachmentName) {
|
||||
SkeletonUtility.AddBoundingBox(bone.skeleton, skinName, slotName, attachmentName, transform);
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnDrawGizmos () {
|
||||
// MITCH : nonuniform scale
|
||||
// if (NonUniformScaleWarning) {
|
||||
// Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning");
|
||||
// }
|
||||
|
||||
if (DisableInheritScaleWarning)
|
||||
Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// MITCH : remove flipX
|
||||
// public void FlipX (bool state) {
|
||||
// if (state != flipX) {
|
||||
// flipX = state;
|
||||
// if (flipX && Mathf.Abs(transform.localRotation.eulerAngles.y) > 90) {
|
||||
// skeletonUtility.skeletonAnimation.LateUpdate();
|
||||
// return;
|
||||
// } else if (!flipX && Mathf.Abs(transform.localRotation.eulerAngles.y) < 90) {
|
||||
// skeletonUtility.skeletonAnimation.LateUpdate();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// bone.FlipX = state;
|
||||
// transform.RotateAround(transform.position, skeletonUtility.transform.up, 180);
|
||||
// Vector3 euler = transform.localRotation.eulerAngles;
|
||||
// euler.x = 0;
|
||||
//
|
||||
// euler.y = bone.FlipX ? 180 : 0;
|
||||
// euler.y = 0;
|
||||
// transform.localRotation = Quaternion.Euler(euler);
|
||||
// }
|
||||
|
||||
public void AddBoundingBox (string skinName, string slotName, string attachmentName) {
|
||||
SkeletonUtility.AddBoundingBox(bone.skeleton, skinName, slotName, attachmentName, transform);
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnDrawGizmos () {
|
||||
// MITCH : nonuniform scale
|
||||
// if (NonUniformScaleWarning) {
|
||||
// Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning");
|
||||
// }
|
||||
|
||||
if (DisableInheritScaleWarning)
|
||||
Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,22 +32,23 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
[RequireComponent(typeof(SkeletonUtilityBone)), ExecuteInEditMode]
|
||||
namespace Spine.Unity {
|
||||
[RequireComponent(typeof(SkeletonUtilityBone)), ExecuteInEditMode]
|
||||
public abstract class SkeletonUtilityConstraint : MonoBehaviour {
|
||||
|
||||
public abstract class SkeletonUtilityConstraint : MonoBehaviour {
|
||||
protected SkeletonUtilityBone utilBone;
|
||||
protected SkeletonUtility skeletonUtility;
|
||||
|
||||
protected SkeletonUtilityBone utilBone;
|
||||
protected SkeletonUtility skeletonUtility;
|
||||
protected virtual void OnEnable () {
|
||||
utilBone = GetComponent<SkeletonUtilityBone>();
|
||||
skeletonUtility = SkeletonUtility.GetInParent<SkeletonUtility>(transform);
|
||||
skeletonUtility.RegisterConstraint(this);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable () {
|
||||
utilBone = GetComponent<SkeletonUtilityBone>();
|
||||
skeletonUtility = SkeletonUtility.GetInParent<SkeletonUtility>(transform);
|
||||
skeletonUtility.RegisterConstraint(this);
|
||||
protected virtual void OnDisable () {
|
||||
skeletonUtility.UnregisterConstraint(this);
|
||||
}
|
||||
|
||||
public abstract void DoUpdate ();
|
||||
}
|
||||
|
||||
protected virtual void OnDisable () {
|
||||
skeletonUtility.UnregisterConstraint(this);
|
||||
}
|
||||
|
||||
public abstract void DoUpdate ();
|
||||
}
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.3
|
||||
*
|
||||
* Copyright (c) 2013-2015, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
* non-transferable license to use, install, execute and perform the Spine
|
||||
* Runtimes Software (the "Software") and derivative works solely for personal
|
||||
* or internal use. Without the written permission of Esoteric Software (see
|
||||
* Section 2 of the Spine Software License Agreement), 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 SOFTWARE 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.
|
||||
*****************************************************************************/
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Modules {
|
||||
[ExecuteInEditMode]
|
||||
public class SkeletonUtilitySubmeshRenderer : MonoBehaviour {
|
||||
[System.NonSerialized]
|
||||
public Mesh mesh;
|
||||
public int submeshIndex = 0;
|
||||
public Material hiddenPassMaterial;
|
||||
Renderer cachedRenderer;
|
||||
MeshFilter filter;
|
||||
Material[] sharedMaterials;
|
||||
|
||||
void Awake () {
|
||||
cachedRenderer = GetComponent<Renderer>();
|
||||
filter = GetComponent<MeshFilter>();
|
||||
sharedMaterials = new Material[0];
|
||||
}
|
||||
|
||||
public void SetMesh (Renderer parentRenderer, Mesh mesh, Material mat) {
|
||||
if (cachedRenderer == null)
|
||||
return;
|
||||
|
||||
cachedRenderer.enabled = true;
|
||||
filter.sharedMesh = mesh;
|
||||
if (cachedRenderer.sharedMaterials.Length != parentRenderer.sharedMaterials.Length) {
|
||||
sharedMaterials = parentRenderer.sharedMaterials;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sharedMaterials.Length; i++) {
|
||||
if (i == submeshIndex)
|
||||
sharedMaterials[i] = mat;
|
||||
else
|
||||
sharedMaterials[i] = hiddenPassMaterial;
|
||||
}
|
||||
|
||||
cachedRenderer.sharedMaterials = sharedMaterials;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7820c1c2b0e52c6408de899d6939996e
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- parentRenderer: {instanceID: 0}
|
||||
- mesh: {instanceID: 0}
|
||||
- hiddenPassMaterial: {fileID: 2100000, guid: 43227e5adadc6f24bb4bf74b92a56fb4,
|
||||
type: 2}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -7,168 +7,171 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public abstract class SpineAttributeBase : PropertyAttribute {
|
||||
public string dataField = "";
|
||||
public string startsWith = "";
|
||||
}
|
||||
|
||||
public class SpineSlot : SpineAttributeBase {
|
||||
public bool containsBoundingBoxes = false;
|
||||
|
||||
/// <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>
|
||||
/// <param name="containsBoundingBoxes">Disables popup results that don't contain bounding box attachments when true.</param>
|
||||
public SpineSlot(string startsWith = "", string dataField = "", bool containsBoundingBoxes = false) {
|
||||
this.startsWith = startsWith;
|
||||
this.dataField = dataField;
|
||||
this.containsBoundingBoxes = containsBoundingBoxes;
|
||||
}
|
||||
}
|
||||
|
||||
public class SpineEvent : SpineAttributeBase {
|
||||
/// <summary>
|
||||
/// Smart popup menu for Spine Events (Spine.EventData)
|
||||
/// </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 SpineEvent(string startsWith = "", string dataField = "") {
|
||||
this.startsWith = startsWith;
|
||||
this.dataField = dataField;
|
||||
}
|
||||
}
|
||||
|
||||
public class SpineSkin : SpineAttributeBase {
|
||||
/// <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 : SpineAttributeBase {
|
||||
/// <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 : SpineAttributeBase {
|
||||
public bool returnAttachmentPath = false;
|
||||
public bool currentSkinOnly = false;
|
||||
public bool placeholdersOnly = false;
|
||||
public string slotField = "";
|
||||
|
||||
/// <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". This path format is only used by the SpineAttachment helper methods like SpineAttachment.GetAttachment and .GetHierarchy. Do not use full path anywhere else in Spine's system.</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;
|
||||
namespace Spine.Unity {
|
||||
public abstract class SpineAttributeBase : PropertyAttribute {
|
||||
public string dataField = "";
|
||||
public string startsWith = "";
|
||||
}
|
||||
|
||||
public static SpineAttachment.Hierarchy GetHierarchy (string fullPath) {
|
||||
return new SpineAttachment.Hierarchy(fullPath);
|
||||
public class SpineSlot : SpineAttributeBase {
|
||||
public bool containsBoundingBoxes = false;
|
||||
|
||||
/// <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>
|
||||
/// <param name="containsBoundingBoxes">Disables popup results that don't contain bounding box attachments when true.</param>
|
||||
public SpineSlot(string startsWith = "", string dataField = "", bool containsBoundingBoxes = false) {
|
||||
this.startsWith = startsWith;
|
||||
this.dataField = dataField;
|
||||
this.containsBoundingBoxes = containsBoundingBoxes;
|
||||
}
|
||||
}
|
||||
|
||||
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 class SpineEvent : SpineAttributeBase {
|
||||
/// <summary>
|
||||
/// Smart popup menu for Spine Events (Spine.EventData)
|
||||
/// </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 SpineEvent(string startsWith = "", string dataField = "") {
|
||||
this.startsWith = startsWith;
|
||||
this.dataField = dataField;
|
||||
}
|
||||
}
|
||||
|
||||
public static Spine.Attachment GetAttachment (string attachmentPath, SkeletonDataAsset skeletonDataAsset) {
|
||||
return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true));
|
||||
public class SpineSkin : SpineAttributeBase {
|
||||
/// <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 : SpineAttributeBase {
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A struct that represents 3 strings that help identify and locate an attachment in a skeleton.</summary>
|
||||
public struct Hierarchy {
|
||||
public string skin;
|
||||
public string slot;
|
||||
public string name;
|
||||
public class SpineAttachment : SpineAttributeBase {
|
||||
public bool returnAttachmentPath = false;
|
||||
public bool currentSkinOnly = false;
|
||||
public bool placeholdersOnly = false;
|
||||
public string slotField = "";
|
||||
|
||||
public Hierarchy (string fullPath) {
|
||||
string[] chunks = fullPath.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries);
|
||||
if (chunks.Length == 0) {
|
||||
skin = "";
|
||||
slot = "";
|
||||
/// <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". This path format is only used by the SpineAttachment helper methods like SpineAttachment.GetAttachment and .GetHierarchy. Do not use full path anywhere else in Spine's system.</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 SpineAttachment.Hierarchy GetHierarchy (string fullPath) {
|
||||
return new SpineAttachment.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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A struct that represents 3 strings that help identify and locate an attachment in a skeleton.</summary>
|
||||
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 = "";
|
||||
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];
|
||||
for (int i = 2; i < chunks.Length; i++) {
|
||||
name += chunks[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SpineBone : SpineAttributeBase {
|
||||
/// <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 class SpineBone : SpineAttributeBase {
|
||||
/// <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) {
|
||||
return renderer.skeleton == null ? null : renderer.skeleton.FindBone(boneName);
|
||||
}
|
||||
|
||||
public static Spine.BoneData GetBoneData(string boneName, SkeletonDataAsset skeletonDataAsset) {
|
||||
var data = skeletonDataAsset.GetSkeletonData(true);
|
||||
return data.FindBone(boneName);
|
||||
}
|
||||
}
|
||||
|
||||
public static Spine.Bone GetBone(string boneName, SkeletonRenderer renderer) {
|
||||
return renderer.skeleton == null ? null : renderer.skeleton.FindBone(boneName);
|
||||
public class SpineAtlasRegion : PropertyAttribute {
|
||||
//TODO: Standardize with Skeleton attributes
|
||||
//NOTE: For now, relies on locally scoped field named "atlasAsset" for source.
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user