Refactored spine-unity, separated SkeletonComponent and SkeletonAnimation.

Added animation:<No Change> to SkeletonAnimation. See:
http://www.esotericsoftware.com/forum/viewtopic.php?f=3&t=606
This commit is contained in:
NathanSweet 2013-05-06 15:40:46 +02:00
parent 64fd34ba94
commit 6bbce812f8
14 changed files with 296 additions and 92 deletions

View File

@ -32,12 +32,12 @@ using Spine;
public class SpineEditor {
[MenuItem("Assets/Create/Spine Atlas")]
static public void CreateAtlas () {
CreateAsset<AtlasAsset>("New Spine Atlas");
CreateAsset<AtlasAsset>("New Atlas");
}
[MenuItem("Assets/Create/Spine Skeleton Data")]
[MenuItem("Assets/Create/Spine SkeletonData")]
static public void CreateSkeletonData () {
CreateAsset<SkeletonDataAsset>("New Spine Skeleton Data");
CreateAsset<SkeletonDataAsset>("New SkeletonData");
}
static private void CreateAsset <T> (String path) where T : ScriptableObject {
@ -53,20 +53,39 @@ public class SpineEditor {
Selection.activeObject = asset;
}
[MenuItem("GameObject/Create Other/Spine Skeleton")]
static public void CreateSkeletonGameObject () {
GameObject gameObject = new GameObject("New Spine Skeleton", typeof(SkeletonComponent));
[MenuItem("GameObject/Create Other/Spine SkeletonComponent")]
static public void CreateSkeletonComponentGameObject () {
GameObject gameObject = new GameObject("New SkeletonComponent", typeof(SkeletonComponent));
EditorUtility.FocusProjectWindow();
Selection.activeObject = gameObject;
}
[MenuItem("GameObject/Create Other/Spine SkeletonAnimation")]
static public void CreateSkeletonAnimationGameObject () {
GameObject gameObject = new GameObject("New SkeletonAnimation", typeof(SkeletonAnimation));
EditorUtility.FocusProjectWindow();
Selection.activeObject = gameObject;
}
[MenuItem("Component/Spine Skeleton")]
[MenuItem("Component/Spine SkeletonComponent")]
static public void CreateSkeletonComponent () {
Selection.activeGameObject.AddComponent(typeof(SkeletonComponent));
}
[MenuItem("Component/Spine Skeleton", true)]
[MenuItem("Component/Spine SkeletonAnimation")]
static public void CreateSkeletonAnimation () {
Selection.activeGameObject.AddComponent(typeof(SkeletonAnimation));
}
[MenuItem("Component/Spine SkeletonComponent", true)]
static public bool ValidateCreateSkeletonComponent () {
return Selection.activeGameObject != null && Selection.activeGameObject.GetComponent(typeof(SkeletonComponent)) == null;
return Selection.activeGameObject != null
&& Selection.activeGameObject.GetComponent(typeof(SkeletonComponent)) == null
&& Selection.activeGameObject.GetComponent(typeof(SkeletonAnimation)) == null;
}
[MenuItem("Component/Spine SkeletonAnimation", true)]
static public bool ValidateCreateSkeletonAnimation () {
return ValidateCreateSkeletonComponent();
}
}

View File

@ -0,0 +1,116 @@
/*******************************************************************************
* Copyright (c) 2013, Esoteric Software
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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 System;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(SkeletonAnimation))]
public class SkeletonAnimationInspector : Editor {
private SerializedProperty skeletonDataAsset, animationName, loop, useAnimationName, initialSkinName, timeScale;
void OnEnable () {
skeletonDataAsset = serializedObject.FindProperty("skeletonDataAsset");
animationName = serializedObject.FindProperty("animationName");
loop = serializedObject.FindProperty("loop");
useAnimationName = serializedObject.FindProperty("useAnimationName");
initialSkinName = serializedObject.FindProperty("initialSkinName");
timeScale = serializedObject.FindProperty("timeScale");
}
override public void OnInspectorGUI () {
serializedObject.Update();
SkeletonComponent component = (SkeletonComponent)target;
EditorGUIUtility.LookLikeInspector();
EditorGUILayout.PropertyField(skeletonDataAsset);
if (component.skeleton != null) {
// Initial skin name.
String[] skins = new String[component.skeleton.Data.Skins.Count + 1];
int skinIndex = 0;
for (int i = 0; i < skins.Length - 1; i++) {
String name = component.skeleton.Data.Skins[i].Name;
skins[i] = name;
if (name == initialSkinName.stringValue) skinIndex = i;
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Initial Skin");
EditorGUIUtility.LookLikeControls();
skinIndex = EditorGUILayout.Popup(skinIndex, skins);
EditorGUIUtility.LookLikeInspector();
EditorGUILayout.EndHorizontal();
initialSkinName.stringValue = skinIndex == 0 ? null : skins[skinIndex];
// Animation name.
String[] animations = new String[component.skeleton.Data.Animations.Count + 2];
animations[0] = "<No Change>";
animations[1] = "<None>";
int animationIndex = useAnimationName.boolValue ? 1 : 0;
for (int i = 0; i < animations.Length - 2; i++) {
String name = component.skeleton.Data.Animations[i].Name;
animations[i + 2] = name;
if (name == animationName.stringValue) animationIndex = i + 2;
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Animation");
EditorGUIUtility.LookLikeControls();
animationIndex = EditorGUILayout.Popup(animationIndex, animations);
EditorGUIUtility.LookLikeInspector();
EditorGUILayout.EndHorizontal();
if (animationIndex == 0) {
animationName.stringValue = null;
useAnimationName.boolValue = false;
} else if (animationIndex == 1) {
animationName.stringValue = null;
useAnimationName.boolValue = true;
} else {
animationName.stringValue = animations[animationIndex];
useAnimationName.boolValue = true;
}
}
// Animation loop.
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Loop");
loop.boolValue = EditorGUILayout.Toggle(loop.boolValue);
EditorGUILayout.EndHorizontal();
EditorGUILayout.PropertyField(timeScale);
if (serializedObject.ApplyModifiedProperties() ||
(Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed")
) {
if (!Application.isPlaying) {
component.Clear();
component.Update();
}
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 5fe5e7b61b19ccf46b700c982173cb7e
guid: 39fbfef61034ca045b5aa80088e1e8a4
MonoImporter:
serializedVersion: 2
defaultReferences: []

View File

@ -29,13 +29,11 @@ using UnityEngine;
[CustomEditor(typeof(SkeletonComponent))]
public class SkeletonComponentInspector : Editor {
private SerializedProperty skeletonDataAsset, animationName, skinName, loop, timeScale;
private SerializedProperty skeletonDataAsset, initialSkinName, timeScale;
void OnEnable () {
skeletonDataAsset = serializedObject.FindProperty("skeletonDataAsset");
skinName = serializedObject.FindProperty("skinName");
animationName = serializedObject.FindProperty("animationName");
loop = serializedObject.FindProperty("loop");
initialSkinName = serializedObject.FindProperty("initialSkinName");
timeScale = serializedObject.FindProperty("timeScale");
}
@ -47,50 +45,25 @@ public class SkeletonComponentInspector : Editor {
EditorGUILayout.PropertyField(skeletonDataAsset);
if (component.skeleton != null) {
// Skin name.
// Initial skin name.
String[] skins = new String[component.skeleton.Data.Skins.Count + 1];
int skinIndex = 0;
for (int i = 0; i < skins.Length - 1; i++) {
String name = component.skeleton.Data.Skins[i].Name;
skins[i] = name;
if (name == skinName.stringValue) skinIndex = i;
if (name == initialSkinName.stringValue) skinIndex = i;
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Skin");
EditorGUILayout.LabelField("Initial Skin");
EditorGUIUtility.LookLikeControls();
skinIndex = EditorGUILayout.Popup(skinIndex, skins);
EditorGUIUtility.LookLikeInspector();
EditorGUILayout.EndHorizontal();
skinName.stringValue = skinIndex == 0 ? null : skins[skinIndex];
// Animation name.
String[] animations = new String[component.skeleton.Data.Animations.Count + 1];
animations[0] = "<None>";
int animationIndex = 0;
for (int i = 0; i < animations.Length - 1; i++) {
String name = component.skeleton.Data.Animations[i].Name;
animations[i + 1] = name;
if (name == animationName.stringValue) animationIndex = i + 1;
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Animation");
EditorGUIUtility.LookLikeControls();
animationIndex = EditorGUILayout.Popup(animationIndex, animations);
EditorGUIUtility.LookLikeInspector();
EditorGUILayout.EndHorizontal();
animationName.stringValue = animationIndex == 0 ? null : animations[animationIndex];
initialSkinName.stringValue = skinIndex == 0 ? null : skins[skinIndex];
}
// Animation loop.
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Loop");
loop.boolValue = EditorGUILayout.Toggle(loop.boolValue);
EditorGUILayout.EndHorizontal();
EditorGUILayout.PropertyField(timeScale);
if (serializedObject.ApplyModifiedProperties() ||

View File

@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright (c) 2013, Esoteric Software
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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 System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
using Spine;
/** Extends SkeletonComponent to apply an animation. */
[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class SkeletonAnimation : SkeletonComponent {
public bool useAnimationName;
public String animationName;
public bool loop;
public Spine.AnimationState state;
override public void Initialize () {
base.Initialize(); // Call overridden method to initialize the skeleton.
state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
}
override public void UpdateSkeleton () {
if (useAnimationName) {
// Keep AnimationState in sync with animationName and loop fields.
if (animationName == null || animationName.Length == 0) {
if (state.Animation != null) state.ClearAnimation();
} else if (state.Animation == null || animationName != state.Animation.Name) {
Spine.Animation animation = skeleton.Data.FindAnimation(animationName);
if (animation != null)
state.SetAnimation(animation, loop);
}
state.Loop = loop;
}
// Apply the animation.
state.Update(Time.deltaTime * timeScale);
state.Apply(skeleton);
// Call overridden method to call skeleton Update and UpdateWorldTransform.
base.UpdateSkeleton();
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d247ba06193faa74d9335f5481b2b56c
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -28,15 +28,13 @@ using System.Collections.Generic;
using UnityEngine;
using Spine;
/** Renders a skeleton. Extend to apply animations, get bones and manipulate them, etc. */
[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class SkeletonComponent : MonoBehaviour {
public SkeletonDataAsset skeletonDataAsset;
public Skeleton skeleton;
public String skinName;
public String animationName;
public bool loop;
public String initialSkinName;
public float timeScale = 1;
public Spine.AnimationState state;
private Mesh mesh;
private Vector3[] vertices;
private Color[] colors;
@ -45,7 +43,7 @@ public class SkeletonComponent : MonoBehaviour {
private int quadCount;
private float[] vertexPositions = new float[8];
public void Clear () {
public virtual void Clear () {
GetComponent<MeshFilter>().mesh = null;
DestroyImmediate(mesh);
mesh = null;
@ -53,25 +51,26 @@ public class SkeletonComponent : MonoBehaviour {
skeleton = null;
}
public void Initialize () {
public virtual void Initialize () {
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
mesh.name = "Skeleton Mesh";
mesh.hideFlags = HideFlags.HideAndDontSave;
state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
skeleton = new Skeleton(skeletonDataAsset.GetSkeletonData(false));
if (initialSkinName != null && initialSkinName.Length > 0) {
skeleton.SetSkin(initialSkinName);
skeleton.SetSlotsToSetupPose();
}
}
public void UpdateAnimation () {
public virtual void UpdateSkeleton () {
skeleton.Update(Time.deltaTime * timeScale);
state.Update(Time.deltaTime * timeScale);
state.Apply(skeleton);
skeleton.UpdateWorldTransform();
}
public void Update () {
public virtual void Update () {
// Clear fields if missing information to render.
if (skeletonDataAsset == null || skeletonDataAsset.GetSkeletonData(false) == null) {
Clear();
@ -82,28 +81,7 @@ public class SkeletonComponent : MonoBehaviour {
if (skeleton == null || skeleton.Data != skeletonDataAsset.GetSkeletonData(false))
Initialize();
// Keep AnimationState in sync with animationName and loop fields.
if (animationName == null || animationName.Length == 0) {
if (state.Animation != null) state.ClearAnimation();
} else if (state.Animation == null || animationName != state.Animation.Name) {
Spine.Animation animation = skeleton.Data.FindAnimation(animationName);
if (animation != null)
state.SetAnimation(animation, loop);
}
state.Loop = loop;
// Keep Skeleton in sync with skinName.
if (skinName == null || skinName.Length == 0) {
if (skeleton.Skin != null) {
skeleton.SetSkin((Skin)null);
skeleton.SetSlotsToSetupPose();
}
} else if (skeleton.Skin == null || skinName != skeleton.Skin.Name) {
skeleton.SetSkin(skinName);
skeleton.SetSlotsToSetupPose();
}
UpdateAnimation();
UpdateSkeleton();
// Count quads.
int quadCount = 0;
@ -176,16 +154,16 @@ public class SkeletonComponent : MonoBehaviour {
renderer.sharedMaterial = skeletonDataAsset.atlasAsset.material;
}
void OnEnable () {
public virtual void OnEnable () {
Update();
}
void OnDisable () {
public virtual void OnDisable () {
if (Application.isEditor)
Clear();
}
void Reset () {
public virtual void Reset () {
Update();
}
}

View File

@ -26,18 +26,15 @@
using UnityEngine;
using System.Collections;
public class SpineboyComponent : MonoBehaviour {
public void OnMouseDown () {
SkeletonComponent skeletonComponent = GetComponent<SkeletonComponent>();
skeletonComponent.animationName = "jump";
skeletonComponent.loop = false;
public class Spineboy : MonoBehaviour {
public void Start () {
SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
skeletonAnimation.state.SetAnimation("walk", true);
}
public void Update () {
SkeletonComponent skeletonComponent = GetComponent<SkeletonComponent>();
if (!skeletonComponent.loop && skeletonComponent.state.Time >= skeletonComponent.state.Animation.Duration - 0.25) {
skeletonComponent.animationName = "walk";
skeletonComponent.loop = true;
}
public void OnMouseDown () {
SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
skeletonAnimation.state.SetAnimation("jump", false);
skeletonAnimation.state.AddAnimation("walk", true);
}
}

View File

@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (c) 2013, Esoteric Software
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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;
public class Spineboy : MonoBehaviour {
public void Start () {
SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
skeletonAnimation.state.SetAnimation("walk", true);
}
public void OnMouseDown () {
SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
skeletonAnimation.state.SetAnimation("jump", false);
skeletonAnimation.state.AddAnimation("walk", true);
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f73aeaeed67fd5446ae964e07ef7e7e7
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData: