Skip to content

Instantly share code, notes, and snippets.

@jmcguirk
Created March 22, 2013 02:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jmcguirk/5218443 to your computer and use it in GitHub Desktop.
Save jmcguirk/5218443 to your computer and use it in GitHub Desktop.
TK2D Spine Controller Runtime
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Spine;
/// <summary>
/// A Unity specific implementation of a skeleton controller.
/// </summary>
public class SkeletonController : MonoBehaviour
{
public TextAsset skeletonDataFile = null;
public TextAsset[] animationDataFiles;
/// <summary>
/// The current animation being played
/// </summary>
protected string m_currentAnimation;
protected Skeleton skeleton;
protected Dictionary<string, SpineAnimation> boneAnimations;
protected bool isDebugDrawOn = true;
private float animationTime = 0;
private bool isSpineAnimationReady = false;
#region Unity Functions
/// <summary>
/// Call down to the protected member functions,
/// so deriving classes can override.
/// </summary>
void Awake()
{
this.AwakeController();
}
/// <summary>
/// Call down to the protected member functions,
/// so deriving classes can override.
/// </summary>
void Update()
{
this.UpdateController();
}
#endregion
protected virtual void AwakeController()
{
if(skeletonDataFile == null)
throw new MissingComponentException("Skeleton data missing from sprite");
if(animationDataFiles.Length < 1)
throw new MissingComponentException("Animation data missing from sprite");
this.BuildSpineAnimations();
}
protected virtual void UpdateController()
{
if(this.isSpineAnimationReady)
{
//Stepped animation play
/*if(Input.anyKeyDown && this.boneAnimations.Length > 0)
{
Debug.Log(animationTime.ToString());
animationTime += 0.05f;
SpineAnimation animationToPlay = this.boneAnimations[0];
animationToPlay.Apply(this.skeleton, animationTime, true);
}*/
//Continuous Animation play
if (this.boneAnimations != null && !string.IsNullOrEmpty(m_currentAnimation))
{
animationTime += Time.deltaTime;
SpineAnimation animationToPlay = this.boneAnimations[m_currentAnimation];
animationToPlay.Apply(this.skeleton, animationTime, true);
}
this.skeleton.UpdateWorldTransform();
if(this.isDebugDrawOn)
this.Debug_Draw();
}
}
/// <summary>
/// Gets or sets the current animation.
/// </summary>
/// <value>
/// The current animation.
/// </value>
public string currentAnimation {
get {
return m_currentAnimation;
}
set {
animationTime = 0;
m_currentAnimation = value;
}
}
protected void Debug_Draw()
{
foreach(Slot slot in this.skeleton.drawOrder)
{
Bone bone = slot.bone;
Vector3 worldStartPos = this.gameObject.transform.position;
Vector3 worldEndPos = Vector3.zero;
worldStartPos.x += bone.worldX;
worldStartPos.y += bone.worldY;
worldEndPos.x = worldStartPos.x + bone.m00 * bone.length;
worldEndPos.y = worldStartPos.y + bone.m10 * bone.length;
Debug.DrawLine(worldStartPos, worldEndPos);
}
}
protected void BuildSpineAnimations()
{
IDictionary<string, object> jsonData = MiniJSON.Json.Deserialize(skeletonDataFile.text) as IDictionary<string, object>;
SkeletonData skeletonData = new SkeletonData(jsonData);
this.skeleton = new Skeleton(skeletonData);
this.skeleton.SetToBindPose();
this.skeleton.UpdateWorldTransform();
//Todo: Compress animation data into one file for better performance
this.boneAnimations = new Dictionary<string, SpineAnimation>();
for(int i = 0; i < animationDataFiles.Length; i ++)
{
TextAsset animationDataFile = animationDataFiles[i];
IDictionary<string, object> animationData = MiniJSON.Json.Deserialize(animationDataFile.text) as IDictionary<string, object>;
SpineAnimation newAnimation = new SpineAnimation(this.skeleton, animationData);
this.boneAnimations[animationDataFile.name.Split('.')[0]] = newAnimation;
}
this.isSpineAnimationReady = true;
}
}
using UnityEngine;
using System.Collections;
[AddComponentMenu("2D Toolkit/Backend/tk2dBaseSprite")]
/// <summary>
/// Sprite base class. Performs target agnostic functionality and manages state parameters.
/// </summary>
public abstract class tk2dBaseSprite : MonoBehaviour, tk2dRuntime.ISpriteCollectionForceBuild
{
/// <summary>
/// This is now private. You should use <see cref="tk2dBaseSprite.Collection">Collection</see> if you wish to read this value.
/// Use <see cref="tk2dBaseSprite.SwitchCollectionAndSprite">SwitchCollectionAndSprite</see> when you need to switch sprite collection.
/// </summary>
[SerializeField]
private tk2dSpriteCollectionData collection;
/// <summary>
/// Deprecation warning: the set accessor will be removed in a future version.
/// Use <see cref="tk2dBaseSprite.SwitchCollectionAndSprite">SwitchCollectionAndSprite</see> when you need to switch sprite collection.
/// </summary>
public tk2dSpriteCollectionData Collection
{
get { return collection; }
set { collection = value; collectionInst = collection.inst; }
}
// This is the active instance of the sprite collection
protected tk2dSpriteCollectionData collectionInst;
[SerializeField] protected Color _color = Color.white;
[SerializeField] protected Vector3 _scale = new Vector3(1.0f, 1.0f, 1.0f);
[SerializeField] protected int _spriteId = 0;
/// <summary>
/// Specifies if this sprite is kept pixel perfect
/// </summary>
public bool pixelPerfect = false;
/// <summary>
/// Internal cached version of the box collider created for this sprite, if present.
/// </summary>
public BoxCollider boxCollider = null;
/// <summary>
/// Internal cached version of the mesh collider created for this sprite, if present.
/// </summary>
public MeshCollider meshCollider = null;
public Vector3[] meshColliderPositions = null;
public Mesh meshColliderMesh = null;
// This is unfortunate, but required due to the unpredictable script execution order in Unity.
// The only problem happens in Awake(), where if another class is Awaken before this one, and tries to
// modify this instance before it is initialized, very bad things could happen.
void InitInstance()
{
if (collectionInst == null && collection != null)
collectionInst = collection.inst;
}
/// <summary>
/// Gets or sets the color.
/// </summary>
/// <value>
/// Please note the range for a Unity Color is 0..1 and not 0..255.
/// </value>
public Color color
{
get { return _color; }
set
{
if (value != _color)
{
_color = value;
InitInstance();
UpdateColors();
}
}
}
/// <summary>
/// Gets or sets the scale.
/// </summary>
/// <value>
/// Use the sprite scale method as opposed to transform.localScale to avoid breaking dynamic batching.
/// </value>
public Vector3 scale
{
get { return _scale; }
set
{
if (value != _scale)
{
_scale = value;
InitInstance();
UpdateVertices();
#if UNITY_EDITOR
EditMode__CreateCollider();
#else
UpdateCollider();
#endif
}
}
}
/// <summary>
/// Flips the sprite horizontally.
/// </summary>
public void FlipX()
{
scale = new Vector3(-_scale.x, _scale.y, _scale.z);
}
/// <summary>
/// Flips the sprite vertically.
/// </summary>
public void FlipY()
{
scale = new Vector3(_scale.x, -_scale.y, _scale.z);
}
/// <summary>
/// Gets or sets the sprite identifier.
/// </summary>
/// <value>
/// The spriteId is a unique number identifying each sprite.
/// Use <see cref="tk2dBaseSprite.GetSpriteIdByName">GetSpriteIdByName</see> to resolve an identifier from the current sprite collection.
/// </value>
public int spriteId
{
get { return _spriteId; }
set
{
if (value != _spriteId)
{
InitInstance();
value = Mathf.Clamp(value, 0, collectionInst.spriteDefinitions.Length - 1);
if (_spriteId < 0 || _spriteId >= collectionInst.spriteDefinitions.Length ||
GetCurrentVertexCount() != collectionInst.spriteDefinitions[value].positions.Length ||
collectionInst.spriteDefinitions[_spriteId].complexGeometry != collectionInst.spriteDefinitions[value].complexGeometry)
{
_spriteId = value;
UpdateGeometry();
}
else
{
_spriteId = value;
UpdateVertices();
}
UpdateMaterial();
UpdateCollider();
}
}
}
/// <summary>
/// Sets the sprite by identifier.
/// </summary>
public void SetSprite(int newSpriteId) {
this.spriteId = newSpriteId;
}
/// <summary>
/// Sets the sprite by name. The sprite will be selected from the current collection.
/// </summary>
public bool SetSprite(string spriteName) {
int spriteId = collection.GetSpriteIdByName(spriteName, -1);
if (spriteId != -1) {
SetSprite(spriteId);
}
return spriteId != -1;
}
/// <summary>
/// Sets sprite by identifier from the new collection.
/// </summary>
public void SetSprite(tk2dSpriteCollectionData newCollection, int spriteId) {
SwitchCollectionAndSprite(newCollection, spriteId);
}
/// <summary>
/// Sets sprite by name from the new collection.
/// </summary>
public bool SetSprite(tk2dSpriteCollectionData newCollection, string spriteName) {
return SwitchCollectionAndSprite(newCollection, spriteName);
}
/// <summary>
/// Switches the sprite collection and sprite.
/// Simply set the <see cref="tk2dBaseSprite.spriteId">spriteId</see> property when you don't need to switch the sprite collection.
/// This will be deprecated in a future release, use SetSprite instead.
/// </summary>
/// <param name='newCollection'>
/// A reference to the sprite collection to switch to.
/// </param>
/// <param name='newSpriteId'>
/// New sprite identifier.
/// </param>
public void SwitchCollectionAndSprite(tk2dSpriteCollectionData newCollection, int newSpriteId)
{
bool switchedCollection = false;
if (Collection != newCollection)
{
collection = newCollection;
collectionInst = collection.inst;
_spriteId = -1; // force an update, but only when the collection has changed
switchedCollection = true;
}
spriteId = newSpriteId;
if (switchedCollection)
{
UpdateMaterial();
}
}
/// <summary>
/// Switches the sprite collection and sprite.
/// Simply set the <see cref="tk2dBaseSprite.spriteId">spriteId</see> property when you don't need to switch the sprite collection.
/// This will be deprecated in a future release, use SetSprite instead.
/// </summary>
/// <param name='newCollection'>
/// A reference to the sprite collection to switch to.
/// </param>
/// <param name='spriteName'>
/// Sprite name.
/// </param>
public bool SwitchCollectionAndSprite(tk2dSpriteCollectionData newCollection, string spriteName)
{
int spriteId = newCollection.GetSpriteIdByName(spriteName, -1);
if (spriteId != -1) {
SwitchCollectionAndSprite(newCollection, spriteId);
}
return spriteId != -1;
}
/// <summary>
/// Makes the sprite pixel perfect to the active camera.
/// Automatically detects <see cref="tk2dCamera"/> if present
/// Otherwise uses Camera.main
/// </summary>
public void MakePixelPerfect()
{
float s = 1.0f;
tk2dPixelPerfectHelper pph = tk2dPixelPerfectHelper.inst;
if (pph)
{
if (pph.CameraIsOrtho)
{
s = pph.scaleK;
}
else
{
s = pph.scaleK + pph.scaleD * transform.position.z;
}
}
else if (tk2dCamera.inst)
{
if (Collection.version < 2)
{
Debug.LogError("Need to rebuild sprite collection.");
}
s = Collection.halfTargetHeight;
}
else if (Camera.main)
{
if (Camera.main.isOrthoGraphic)
{
s = Camera.main.orthographicSize;
}
else
{
float zdist = (transform.position.z - Camera.main.transform.position.z);
s = tk2dPixelPerfectHelper.CalculateScaleForPerspectiveCamera(Camera.main.fov, zdist);
}
}
else
{
Debug.LogError("Main camera not found.");
}
s *= Collection.invOrthoSize;
scale = new Vector3(Mathf.Sign(scale.x) * s * pixelPerfectScaleX, Mathf.Sign(scale.y) * s * pixelPerfectScaleY, Mathf.Sign(scale.z) * s);
}
public float pixelPerfectScaleX = 1;
public float pixelPerfectScaleY = 1;
protected abstract void UpdateMaterial(); // update material when switching spritecollection
protected abstract void UpdateColors(); // reupload color data only
protected abstract void UpdateVertices(); // reupload vertex data only
protected abstract void UpdateGeometry(); // update full geometry (including indices)
protected abstract int GetCurrentVertexCount(); // return current vertex count
/// <summary>
/// Rebuilds the mesh data for this sprite. Not usually necessary to call this, unless some internal states are modified.
/// </summary>
public abstract void Build();
/// <summary>
/// Resolves a sprite name and returns a unique id for the sprite.
/// Convenience alias of <see cref="tk2dSpriteCollectionData.GetSpriteIdByName"/>
/// </summary>
/// <returns>
/// Unique Sprite Id.
/// </returns>
/// <param name='name'>Case sensitive sprite name, as defined in the sprite collection. This is usually the source filename excluding the extension</param>
public int GetSpriteIdByName(string name)
{
InitInstance();
return collectionInst.GetSpriteIdByName(name);
}
/// <summary>
/// Adds a tk2dBaseSprite derived class as a component to the gameObject passed in, setting up necessary parameters
/// and building geometry.
/// </summary>
public static T AddComponent<T>(GameObject go, tk2dSpriteCollectionData spriteCollection, int spriteId) where T : tk2dBaseSprite
{
T sprite = go.AddComponent<T>();
sprite._spriteId = -1;
sprite.SetSprite(spriteCollection, spriteId);
sprite.Build();
return sprite;
}
/// <summary>
/// Adds a tk2dBaseSprite derived class as a component to the gameObject passed in, setting up necessary parameters
/// and building geometry. Shorthand using sprite name
/// </summary>
public static T AddComponent<T>(GameObject go, tk2dSpriteCollectionData spriteCollection, string spriteName) where T : tk2dBaseSprite
{
int spriteId = spriteCollection.GetSpriteIdByName(spriteName, -1);
if (spriteId == -1) {
Debug.LogError( string.Format("Unable to find sprite named {0} in sprite collection {1}", spriteName, spriteCollection.spriteCollectionName) );
return null;
}
else {
return AddComponent<T>(go, spriteCollection, spriteId);
}
}
protected int GetNumVertices()
{
InitInstance();
return collectionInst.spriteDefinitions[spriteId].positions.Length;
}
protected int GetNumIndices()
{
InitInstance();
return collectionInst.spriteDefinitions[spriteId].indices.Length;
}
protected void SetPositions(Vector3[] positions, Vector3[] normals, Vector4[] tangents)
{
var sprite = collectionInst.spriteDefinitions[spriteId];
int numVertices = GetNumVertices();
for (int i = 0; i < numVertices; ++i)
{
positions[i].x = sprite.positions[i].x * _scale.x;
positions[i].y = sprite.positions[i].y * _scale.y;
positions[i].z = sprite.positions[i].z * _scale.z;
}
// The secondary test sprite.normals != null must have been performed prior to this function call
if (normals.Length > 0)
{
for (int i = 0; i < numVertices; ++i)
{
normals[i] = sprite.normals[i];
}
}
// The secondary test sprite.tangents != null must have been performed prior to this function call
if (tangents.Length > 0)
{
for (int i = 0; i < numVertices; ++i)
{
tangents[i] = sprite.tangents[i];
}
}
}
protected void SetColors(Color[] dest)
{
Color c = _color;
if (collectionInst.premultipliedAlpha) { c.r *= c.a; c.g *= c.a; c.b *= c.a; }
int numVertices = GetNumVertices();
for (int i = 0; i < numVertices; ++i)
dest[i] = c;
}
/// <summary>
/// Gets the local space bounds of the sprite.
/// </summary>
/// <returns>
/// Local space bounds
/// </returns>
public Bounds GetBounds()
{
InitInstance();
var sprite = collectionInst.spriteDefinitions[_spriteId];
return new Bounds(new Vector3(sprite.boundsData[0].x * _scale.x, sprite.boundsData[0].y * _scale.y, sprite.boundsData[0].z * _scale.z),
new Vector3(sprite.boundsData[1].x * _scale.x, sprite.boundsData[1].y * _scale.y, sprite.boundsData[1].z * _scale.z));
}
/// <summary>
/// Gets untrimmed local space bounds of the sprite. This is the size of the sprite before 2D Toolkit trims away empty space in the sprite.
/// Use this when you need to position sprites in a grid, etc, when the trimmed bounds is not sufficient.
/// </summary>
/// <returns>
/// Local space untrimmed bounds
/// </returns>
public Bounds GetUntrimmedBounds()
{
InitInstance();
var sprite = collectionInst.spriteDefinitions[_spriteId];
return new Bounds(new Vector3(sprite.untrimmedBoundsData[0].x * _scale.x, sprite.untrimmedBoundsData[0].y * _scale.y, sprite.untrimmedBoundsData[0].z * _scale.z),
new Vector3(sprite.untrimmedBoundsData[1].x * _scale.x, sprite.untrimmedBoundsData[1].y * _scale.y, sprite.untrimmedBoundsData[1].z * _scale.z));
}
/// <summary>
/// Gets the current sprite definition.
/// </summary>
/// <returns>
/// <see cref="tk2dSpriteDefinition"/> for the currently active sprite.
/// </returns>
public tk2dSpriteDefinition GetCurrentSpriteDef()
{
InitInstance();
return collectionInst.spriteDefinitions[_spriteId];
}
/// <summary>
/// Gets the current sprite definition.
/// </summary>
/// <returns>
/// <see cref="tk2dSpriteDefinition"/> for the currently active sprite.
/// </returns>
public tk2dSpriteDefinition CurrentSprite {
get {
InitInstance();
return collectionInst.spriteDefinitions[_spriteId];
}
}
// Unity functions
public void Start()
{
if (pixelPerfect)
MakePixelPerfect();
}
// Collider setup
protected virtual bool NeedBoxCollider() { return false; }
protected void UpdateCollider()
{
var sprite = collectionInst.spriteDefinitions[_spriteId];
if (sprite.colliderType == tk2dSpriteDefinition.ColliderType.Box && boxCollider == null)
{
// Has the user created a box collider?
boxCollider = gameObject.GetComponent<BoxCollider>();
if (boxCollider == null)
{
// create box collider at runtime. this won't get removed from the object
boxCollider = gameObject.AddComponent<BoxCollider>();
}
}
if (boxCollider != null)
{
if (sprite.colliderType == tk2dSpriteDefinition.ColliderType.Box)
{
boxCollider.center = new Vector3(sprite.colliderVertices[0].x * _scale.x, sprite.colliderVertices[0].y * _scale.y, sprite.colliderVertices[0].z * _scale.z);
boxCollider.extents = new Vector3(sprite.colliderVertices[1].x * _scale.x, sprite.colliderVertices[1].y * _scale.y, sprite.colliderVertices[1].z * _scale.z);
}
else if (sprite.colliderType == tk2dSpriteDefinition.ColliderType.Unset)
{
// Don't do anything here, for backwards compatibility
}
else // in all cases, if the collider doesn't match is requested, null it out
{
if (boxCollider != null)
{
// move the box far far away, boxes with zero extents still collide
boxCollider.center = new Vector3(0, 0, -100000.0f);
boxCollider.extents = Vector3.zero;
}
}
}
}
// This is separate to UpdateCollider, as UpdateCollider can only work with BoxColliders, and will NOT create colliders
protected void CreateCollider()
{
var sprite = collectionInst.spriteDefinitions[_spriteId];
if (sprite.colliderType == tk2dSpriteDefinition.ColliderType.Unset)
{
// do not attempt to create or modify anything if it is Unset
return;
}
// User has created a collider
if (collider != null)
{
boxCollider = GetComponent<BoxCollider>();
meshCollider = GetComponent<MeshCollider>();
}
if ((NeedBoxCollider() || sprite.colliderType == tk2dSpriteDefinition.ColliderType.Box) && meshCollider == null)
{
if (boxCollider == null)
{
boxCollider = gameObject.AddComponent<BoxCollider>();
}
}
else if (sprite.colliderType == tk2dSpriteDefinition.ColliderType.Mesh && boxCollider == null)
{
// this should not be updated again (apart from scale changes in the editor, where we force regeneration of colliders)
if (meshCollider == null)
meshCollider = gameObject.AddComponent<MeshCollider>();
if (meshColliderMesh == null)
meshColliderMesh = new Mesh();
meshColliderMesh.Clear();
meshColliderPositions = new Vector3[sprite.colliderVertices.Length];
for (int i = 0; i < meshColliderPositions.Length; ++i)
meshColliderPositions[i] = new Vector3(sprite.colliderVertices[i].x * _scale.x, sprite.colliderVertices[i].y * _scale.y, sprite.colliderVertices[i].z * _scale.z);
meshColliderMesh.vertices = meshColliderPositions;
float s = _scale.x * _scale.y * _scale.z;
meshColliderMesh.triangles = (s >= 0.0f)?sprite.colliderIndicesFwd:sprite.colliderIndicesBack;
meshCollider.sharedMesh = meshColliderMesh;
meshCollider.convex = sprite.colliderConvex;
// this is required so our mesh pivot is at the right point
if (rigidbody) rigidbody.centerOfMass = Vector3.zero;
}
else if (sprite.colliderType != tk2dSpriteDefinition.ColliderType.None)
{
// This warning is not applicable in the editor
if (Application.isPlaying)
{
Debug.LogError("Invalid mesh collider on sprite, please remove and try again.");
}
}
UpdateCollider();
}
#if UNITY_EDITOR
public void EditMode__CreateCollider()
{
// Revert to runtime behaviour when the game is running
if (Application.isPlaying)
{
UpdateCollider();
return;
}
var sprite = collectionInst.spriteDefinitions[_spriteId];
if (sprite.colliderType == tk2dSpriteDefinition.ColliderType.Unset)
return;
PhysicMaterial physicsMaterial = collider?collider.sharedMaterial:null;
bool isTrigger = collider?collider.isTrigger:false;
if (boxCollider)
{
DestroyImmediate(boxCollider, true);
}
if (meshCollider)
{
DestroyImmediate(meshCollider, true);
if (meshColliderMesh)
DestroyImmediate(meshColliderMesh, true);
}
CreateCollider();
if (collider)
{
collider.isTrigger = isTrigger;
collider.material = physicsMaterial;
}
}
#endif
protected void Awake()
{
if (collection != null)
{
collectionInst = collection.inst;
}
}
// tk2dRuntime.ISpriteCollectionEditor
public bool UsesSpriteCollection(tk2dSpriteCollectionData spriteCollection)
{
return Collection == spriteCollection;
}
public virtual void ForceBuild()
{
collectionInst = collection.inst;
if (spriteId < 0 || spriteId >= collectionInst.spriteDefinitions.Length)
spriteId = 0;
Build();
#if UNITY_EDITOR
EditMode__CreateCollider();
#endif
}
/// <summary>
/// Create a sprite (and gameObject) displaying the region of the texture specified.
/// Use <see cref="tk2dSpriteCollectionData.CreateFromTexture"/> if you need to create a sprite collection
/// with multiple sprites.
/// </summary>
public static GameObject CreateFromTexture<T>(Texture2D texture, tk2dRuntime.SpriteCollectionSize size, Rect region, Vector2 anchor) where T : tk2dBaseSprite
{
tk2dSpriteCollectionData data = tk2dRuntime.SpriteCollectionGenerator.CreateFromTexture(texture, size, region, anchor);
if (data == null)
return null;
GameObject spriteGo = new GameObject();
tk2dBaseSprite.AddComponent<T>(spriteGo, data, 0);
return spriteGo;
}
}
using UnityEngine;
using System.Collections;
using Spine;
/// <summary>
/// TK2D version of spine skeleton renderer
/// </summary>
public class TK2DSkeletonController : SkeletonController
{
/// <summary>
/// references to a sprite collection data file
/// </summary>
public GameObject spriteAtlas;
/// <summary>
/// How much to scale the rig by
/// </summary>
public float rigScale = 1.0f;
/**
* Which direction this rig should be moving initially
*/
public int rigDirection = 1;
/// <summary>
/// The initial animation the rig should be playing
/// </summary>
public string initialAnimation;
/// <summary>
/// A collection of spawned attachment sprites
/// </summary>
private tk2dSprite[] attachmentSprites = null;
/// <summary>
/// The skin name to use for this rig
/// </summary>
public string SkinName;
/// <summary>
/// The is atlas parsed
/// </summary>
private bool isAtlasParsed = false;
/// <summary>
/// Have the bones for this rig been rendered
/// </summary>
private bool isBonesRendered = false;
/// <summary>
/// Awakes the controller.
/// </summary>
/// <exception cref="UnityEngine.MissingComponentException">
/// Skeleton data missing from sprite
/// or
/// Animation data missing from sprite
/// or
/// OT - missing componenet
/// </exception>
protected override void AwakeController()
{
if(skeletonDataFile == null)
throw new MissingComponentException("Skeleton data missing from sprite");
if(animationDataFiles.Length < 1)
throw new MissingComponentException("Animation data missing from sprite");
if(spriteAtlas == null)
throw new MissingComponentException("OT - missing componenet");
}
/// <summary>
/// Plays the given animation.
/// </summary>
/// <param name="animationName">Name of the animation.</param>
public void playAnimation(string animationName) {
this.currentAnimation = animationName;
}
/// <summary>
/// Updates the controller.
/// </summary>
protected override void UpdateController()
{
if (!this.isBonesRendered) {
this.isBonesRendered = true;
this.OnAtlasReady();
}
base.UpdateController();
if(this.isAtlasParsed)
{
for(int i=0; i <this.skeleton.drawOrder.Count; i++)
{
Vector2 worldPos = Vector2.zero;
Vector2 worldScale = Vector2.zero;
float worldRotation = 0;
Slot slot = this.skeleton.slots[i];
Attachment attachment = slot.attachment;
Bone bone = slot.bone;
tk2dSprite sprite = this.attachmentSprites[i];
if(sprite != null)
{
if (attachment != null) {
worldPos = this.ResolveWorldCoordinates(slot);
worldRotation = bone.worldRotation + attachment.rotation;
sprite.gameObject.SetActive(true);
} else {
sprite.gameObject.SetActive(false);
}
sprite.transform.localPosition = new Vector3(worldPos.x * rigScale * rigDirection, worldPos.y * rigScale, (float)(i * -0.1));
sprite.transform.eulerAngles = new Vector3(0, 0, rigDirection * worldRotation);
}
}
}
}
/// <summary>
/// Resolves the world coordinates.
/// </summary>
/// <param name="slot">The slot.</param>
/// <returns></returns>
private Vector2 ResolveWorldCoordinates(Slot slot)
{
Vector2 worldPosition = Vector2.zero;
Attachment attachment = slot.attachment;
Bone bone = slot.bone;
worldPosition.x = attachment.x * bone.m00 + attachment.y * bone.m01 + bone.worldX;
worldPosition.y = attachment.x * bone.m10 + attachment.y * bone.m11 + bone.worldY;
return worldPosition;
}
/// <summary>
/// Called when [atlas ready].
/// </summary>
void OnAtlasReady()
{
base.BuildSpineAnimations();
if (!string.IsNullOrEmpty(this.SkinName)) {
Skin skin = this.skeleton.data.FindSkin(this.SkinName);
skin.AttachAll(this.skeleton, this.skeleton.data.defaultSkin);
}
this.attachmentSprites = new tk2dSprite[this.skeleton.drawOrder.Count];
for(int i=0; i <this.skeleton.drawOrder.Count; i++)
{
Slot slot = this.skeleton.slots[i];
Attachment attachment = slot.attachment;
Bone bone = slot.bone;
GameObject obj = new GameObject();
obj.transform.parent = this.transform;
obj.name = bone.name;
if (attachment != null) {
if (!string.IsNullOrEmpty(attachment.name)) {
string atlasSpriteName = attachment.name;
tk2dSpriteCollectionData dat = spriteAtlas.GetComponent<tk2dSpriteCollectionData>();
int id = dat.GetSpriteIdByName(attachment.name, -1);
if (id >= 0) {
tk2dSprite sprite = obj.AddComponent<tk2dSprite>();
sprite.pixelPerfect = true;
sprite.pixelPerfectScaleX = rigDirection * rigScale;
sprite.pixelPerfectScaleY = rigScale;
sprite.name = bone.name;
sprite.SetSprite(dat, attachment.name);
this.attachmentSprites[i] = sprite;
}
}
}
}
this.currentAnimation = this.initialAnimation;
this.isAtlasParsed = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment