Reputation: 41
Currently I am working on a 2D game in Unity. I am working on a EditorWindow to slice an imported spritesheet and create animations from these sprites.
Currently, I have the code to slice the spreadsheet functioning, detailed below for those interested in referencing:
public void Slice()
{
var textures = Selection.GetFiltered<Texture2D>(SelectionMode.Assets);
foreach (var texture in textures)
{
ProcessTexture(texture, pixelPerUnit, spriteSize, pivot, alignment);
}
}
static void ProcessTexture(Texture2D texture, int pixelPerUnit,
Vector2Int spriteSize, Vector2 pivot, Alignment alignment)
{
string path = AssetDatabase.GetAssetPath(texture);
{
TextureImporter textureImporter =
TextureImporter.GetAtPath(path) as TextureImporter;
//Set characteristics for spritesheet
textureImporter.textureType = TextureImporterType.Sprite;
textureImporter.spriteImportMode = SpriteImportMode.Multiple;
textureImporter.spritePixelsPerUnit = pixelPerUnit;
textureImporter.filterMode = FilterMode.Point;
textureImporter.textureCompression = TextureImporterCompression.Uncompressed;
int colCount = texture.width / spriteSize.x;
int rowCount = texture.height / spriteSize.y;
//Create Spritesheet Metadata based on characteristics
List<SpriteMetaData> metas = new List<SpriteMetaData>();
for (int c = 0; c < colCount; c++)
{
for (int r = 0; r < rowCount; r++)
{
SpriteMetaData meta = new SpriteMetaData();
meta.rect = new Rect(c * spriteSize.x,
r * spriteSize.y,
spriteSize.x, spriteSize.y);
meta.name = (rowCount - r - 1) + "-" + c;
meta.alignment = (int)alignment;
meta.pivot = pivot;
metas.Add(meta);
}
}
//Apply the metadata to the spritesheet
textureImporter.spritesheet = metas.ToArray();
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
}
}
The portion that is currently giving me grief is converting this newly sliced spreadsheet into animations through script.
Currently I can create an empty animation clip in the directory of the spritesheet using the code snippet below:
string path = AssetDatabase.GetAssetPath(texture);
string newPath = Path.GetDirectoryName(path);
AnimationClip clip = new AnimationClip();
AssetDatabase.CreateAsset(clip, newPath + "\\" + spriteName ".anim");
I am having difficulty finding out how to add sprites to this newly created animation. I have looked into AnimationCurves and AnimationEvents but I cannot seem to find the step to link a sprite to the animation through the editor.
If anyone has experience or knowledge regarding the creation of unity animationclips through script, any insight would be greatly appreciated. If any more information is needed on my end, please let me know. This is my first time using this service. Thank you all for your help!
Upvotes: 2
Views: 1388
Reputation: 26
Thanks to BirdWorks for this solution! I adapted it for my needs. If Unity already has parsed your spritesheet into sprites due to using TexturePacker or by using the auto slicer tool builtin, its a bit different for implementation.
I also wanted to verify that it was all being parsed correctly so I put in extra work to make it show in the inspector. (this caused more headache than it was worth, but it works!)
Here is how I did it (again credit to BirdWorks for a lot of it):
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
[ExecuteInEditMode]
public class SpriteImporter : MonoBehaviour
{
public static SpriteImporter instance;
[Serializable]
public class AnimationAction {
public string key;
public List<ActionDirection> direction;
}
[Serializable]
public class ActionDirection {
public string key;
public List<FrameSprite> frames;
}
[Serializable]
public class FrameSprite {
public string key;
public Sprite sprite;
}
public string savePath = "Assets/Art/Spritesheets/anim/";
public List<AnimationAction> frameSprites = new List<AnimationAction>();
void Start()
{
if (instance == null)
{
Debug.Log("Creating Instance of SpriteImporter");
instance = this;
}
}
static void CreateInstance()
{
// Get spriteImporter Object
GameObject spriteImporter = GameObject.FindWithTag("SpriteImporter");
// Delete all components
foreach(var comp in spriteImporter.GetComponents<Component>())
{
if(!(comp is Transform))
{
DestroyImmediate(comp);
}
}
// Add SpriteImporter Component
instance = spriteImporter.AddComponent(typeof(SpriteImporter)) as SpriteImporter;
}
[MenuItem("AssetDatabase/ImportSprite")]
static void ImportSprite()
{
if(instance == null)
CreateInstance();
ResetData();
Texture2D t = (Texture2D)AssetDatabase.LoadAssetAtPath("Assets/Art/Spritesheets/Prince.png", typeof(Texture2D));
string spriteSheet = AssetDatabase.GetAssetPath( t );
Sprite[] sprites = AssetDatabase.LoadAllAssetsAtPath( spriteSheet ).OfType<Sprite>().ToArray();
foreach(Sprite s in sprites)
{
string[] parts = s.name.Split("-");
// Get or Create Animation Action
AnimationAction action = GetAction(parts[0]);
if(action == null)
{
action = new AnimationAction();
action.key = parts[0];
action.direction = new List<ActionDirection>();
AddAction(action);
}
// Get or Create ActionDirection
ActionDirection direction = GetDirection(action, parts[1]);
if(direction == null)
{
direction = new ActionDirection();
direction.key = parts[1];
direction.frames = new List<FrameSprite>();
AddDirection(action, direction);
}
// Add Frames to Direction
FrameSprite fs = new FrameSprite();
fs.key = parts[2];
fs.sprite = s;
direction.frames.Add(fs);
}
// Process Animations
CreateAnimations();
}
static void ResetData()
{
instance.frameSprites = new List<AnimationAction>();
}
static AnimationAction GetAction(string key)
{
foreach(var item in instance.frameSprites)
{
if(item.key == key)
return item;
}
return null;
}
static void AddAction(AnimationAction action)
{
instance.frameSprites.Add(action);
}
static ActionDirection GetDirection(AnimationAction action, string dir)
{
foreach(var item in action.direction)
{
if(item.key == dir)
return item;
}
return null;
}
static void AddDirection(AnimationAction action, ActionDirection direction)
{
action.direction.Add(direction);
}
static void CreateAnimations()
{
string newPath = Path.GetDirectoryName(instance.savePath);
foreach(AnimationAction action in instance.frameSprites)
{
foreach(ActionDirection direction in action.direction)
{
Debug.Log("Creating "+ newPath + "\\" +action.key + "-" + direction.key + ".anim");
//Create the base AnimationClip
AnimationClip clip = new AnimationClip();
clip.frameRate = 12f;
//Create the CurveBinding
EditorCurveBinding spriteBinding = new EditorCurveBinding();
spriteBinding.type = typeof(SpriteRenderer);
spriteBinding.path = "";
spriteBinding.propertyName = "m_Sprite";
//Create the KeyFrames
ObjectReferenceKeyframe[] spriteKeyFrames = new ObjectReferenceKeyframe[direction.frames.Count];
int j = 0;
foreach(FrameSprite sprite in direction.frames)
{
spriteKeyFrames[j] = new ObjectReferenceKeyframe();
spriteKeyFrames[j].time = j/clip.frameRate;
spriteKeyFrames[j].value = sprite.sprite;
j++;
}
AnimationUtility.SetObjectReferenceCurve(clip, spriteBinding, spriteKeyFrames);
//Set Loop Time to True
AnimationClipSettings settings = AnimationUtility.GetAnimationClipSettings(clip);
settings.loopTime = true;
AnimationUtility.SetAnimationClipSettings(clip, settings);
//Save the clip
AssetDatabase.CreateAsset(clip, newPath + "\\" + action.key + "-" + direction.key + ".anim");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
}
}
Upvotes: 0
Reputation: 41
I am posting my resolution here in the event that anyone else would like to reference it! I ultimately ended up referencing the articles Create Animation Clip from Sprite[] (Programmatically) and How to get child sprites from a 'Multiple Sprite' Texture.
The resultant code for creating animations from a selected spritesheet through editor window is pasted below:
public void Animate()
{
var textures = Selection.GetFiltered<Texture2D>(SelectionMode.Assets);
foreach (var texture in textures)
{
GenerateAnimations(texture, spriteSize, spriteName);
}
}
static void GenerateAnimations(Texture2D texture, Vector2Int spriteSize, string spriteNameGlobal)
{
//Create an Array of all sprites in the selected texture
string path = AssetDatabase.GetAssetPath(texture);
Sprite[] allSprites = AssetDatabase.LoadAllAssetsAtPath(path).OfType<Sprite>().ToArray();
string newPath = Path.GetDirectoryName(path);
//Determine the number of rows & columns based on preset "spriteSize" Vector2
int colCount = texture.width / spriteSize.x;
int rowCount = texture.height / spriteSize.y;
//Loop through each row of the spritesheet to make an animation
for (int i = 0; i < rowCount; i++)
{
//Create a sprite subsection for a row of sprites
Sprite[] batchSprites = new Sprite[colCount];
for (int j = 0; j < batchSprites.Length; j++)
{
batchSprites[j] = allSprites[i * colCount + j];
}
//Create the base AnimationClip
AnimationClip clip = new AnimationClip();
clip.frameRate = 12f;
//Create the CurveBinding
EditorCurveBinding spriteBinding = new EditorCurveBinding();
spriteBinding.type = typeof(SpriteRenderer);
spriteBinding.path = "";
spriteBinding.propertyName = "m_Sprite";
//Create the KeyFrames
ObjectReferenceKeyframe[] spriteKeyFrames = new ObjectReferenceKeyframe[colCount];
for (int j = 0; j < spriteKeyFrames.Length; j++)
{
spriteKeyFrames[j] = new ObjectReferenceKeyframe();
spriteKeyFrames[j].time = j/clip.frameRate;
spriteKeyFrames[j].value = batchSprites[j];
}
AnimationUtility.SetObjectReferenceCurve(clip, spriteBinding, spriteKeyFrames);
//Set Loop Time to True
AnimationClipSettings settings = AnimationUtility.GetAnimationClipSettings(clip);
settings.loopTime = true;
AnimationUtility.SetAnimationClipSettings(clip, settings);
//Save the clip
AssetDatabase.CreateAsset(clip, newPath + "\\" + spriteNameGlobal + i + ".anim");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
In working with this code, there were some interesting issues that would pop up if not careful. A major one was not setting spriteBinding.propertyname to "m_Sprite". Be careful with this if you decide to modify this code.
I hope this proves useful! If anything was not clear, or there are further suggestions for clarity, please respond and I can try to to improve this response.
Upvotes: 2