wuxianshengcong/Library/PackageCache/com.unity.2d.aseprite@1.1.6/Editor/AssetGeneration/AnimationClipGeneration.cs

428 lines
17 KiB
C#
Raw Permalink Normal View History

2025-01-02 14:49:00 +08:00
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.U2D.Aseprite
{
internal static class AnimationClipGeneration
{
const string k_RootName = "Root";
public static AnimationClip[] Generate(string assetName,
IReadOnlyList<Sprite> sprites,
AsepriteFile file,
IReadOnlyList<Layer> layers,
IReadOnlyList<Frame> frames,
List<Tag> tags,
Dictionary<int, GameObject> layerIdToGameObject)
{
var noOfFrames = file.noOfFrames;
if (tags.Count == 0)
{
var tag = new Tag();
tag.name = assetName + "_Clip";
tag.fromFrame = 0;
tag.toFrame = noOfFrames;
tags.Add(tag);
}
var layersWithDisabledRenderer = new HashSet<Layer>();
for (var i = 0; i < layers.Count; ++i)
{
if (DoesLayerDisableRenderer(layers[i], tags))
layersWithDisabledRenderer.Add(layers[i]);
}
var layersWithCustomSortingOrder = new HashSet<Layer>();
for (var i = 0; i < layers.Count; ++i)
{
if (DoesLayerHaveCustomSorting(layers[i]))
layersWithCustomSortingOrder.Add(layers[i]);
}
var clips = new List<AnimationClip>(tags.Count);
var animationNames = new HashSet<string>(tags.Count);
for (var i = 0; i < tags.Count; ++i)
{
var clipName = tags[i].name;
if (animationNames.Contains(clipName))
{
var nameIndex = 0;
while (animationNames.Contains(clipName))
clipName = $"{tags[i].name}_{nameIndex++}";
Debug.LogWarning($"The animation clip name {tags[i].name} is already in use. Renaming to {clipName}.");
}
var clip = CreateClip(tags[i], clipName, layers, layersWithDisabledRenderer, layersWithCustomSortingOrder, frames, sprites, layerIdToGameObject);
clips.Add(clip);
animationNames.Add(clipName);
}
return clips.ToArray();
}
static bool DoesLayerDisableRenderer(Layer layer, IReadOnlyList<Tag> tags)
{
if (layer.layerType != LayerTypes.Normal)
return false;
var cells = layer.cells;
var linkedCells = layer.linkedCells;
for (var i = 0; i < tags.Count; ++i)
{
var tag = tags[i];
for (var frameIndex = tag.fromFrame; frameIndex < tag.toFrame; ++frameIndex)
{
var foundCell = false;
foreach (var cell in cells)
{
if (cell.frameIndex != frameIndex)
continue;
foundCell = true;
break;
}
if (foundCell)
continue;
foreach (var cell in linkedCells)
{
if (cell.frameIndex != frameIndex)
continue;
foundCell = true;
break;
}
if (!foundCell)
return true;
}
}
return false;
}
static bool DoesLayerHaveCustomSorting(Layer layer)
{
if (layer.layerType != LayerTypes.Normal)
return false;
var cells = layer.cells;
var linkedCells = layer.linkedCells;
foreach(var cell in cells)
{
if (cell.additiveSortOrder != 0)
return true;
}
foreach (var linkedCell in linkedCells)
{
var cellIndex = cells.FindIndex(x => x.frameIndex == linkedCell.linkedToFrame);
if (cellIndex == -1)
continue;
var cell = cells[cellIndex];
if (cell.additiveSortOrder != 0)
return true;
}
return false;
}
static AnimationClip CreateClip(
Tag tag,
string clipName,
IReadOnlyList<Layer> layers,
IReadOnlyCollection<Layer> layersWithDisabledRenderer,
IReadOnlyCollection<Layer> layersWithCustomSorting,
IReadOnlyList<Frame> frames,
IReadOnlyList<Sprite> sprites,
IReadOnlyDictionary<int, GameObject> layerIdToGameObject)
{
var animationClip = new AnimationClip()
{
name = clipName,
frameRate = 100f
};
var clipSettings = new AnimationClipSettings();
clipSettings.loopTime = tag.isRepeating;
AnimationUtility.SetAnimationClipSettings(animationClip, clipSettings);
for (var i = 0; i < layers.Count; ++i)
{
var layer = layers[i];
if (layer.layerType != LayerTypes.Normal)
continue;
var layerGo = layerIdToGameObject[layer.index];
if (layerGo.GetComponent<SpriteRenderer>() == null)
continue;
var doesLayerDisableRenderer = layersWithDisabledRenderer.Contains(layer);
var doesLayerHaveCustomSorting = layersWithCustomSorting.Contains(layer);
var layerTransform = layerGo.transform;
var spriteKeyframes = new List<ObjectReferenceKeyframe>();
var cells = layer.cells;
var activeFrames = AddCellsToClip(cells, tag, sprites, frames, spriteKeyframes);
var linkedCells = layer.linkedCells;
activeFrames.UnionWith(AddLinkedCellsToClip(linkedCells, cells, tag, sprites, frames, spriteKeyframes));
spriteKeyframes.Sort((x, y) => x.time.CompareTo(y.time));
DuplicateLastFrame(spriteKeyframes, frames[tag.toFrame - 1], animationClip.frameRate);
var path = GetTransformPath(layerTransform);
var spriteBinding = EditorCurveBinding.PPtrCurve(path, typeof(SpriteRenderer), "m_Sprite");
AnimationUtility.SetObjectReferenceCurve(animationClip, spriteBinding, spriteKeyframes.ToArray());
AddEnabledKeyframes(layerTransform, tag, frames, doesLayerDisableRenderer, activeFrames, animationClip);
AddSortOrderKeyframes(layerTransform, layer, tag, frames, cells, doesLayerHaveCustomSorting, animationClip);
AddAnimationEvents(tag, frames, animationClip);
}
return animationClip;
}
static HashSet<int> AddCellsToClip(IReadOnlyList<Cell> cells, Tag tag, IReadOnlyList<Sprite> sprites, IReadOnlyList<Frame> frames, List<ObjectReferenceKeyframe> keyFrames)
{
var activeFrames = new HashSet<int>();
var startTime = GetTimeFromFrame(frames, tag.fromFrame);
for (var i = 0; i < cells.Count; ++i)
{
var cell = cells[i];
if (cell.frameIndex < tag.fromFrame ||
cell.frameIndex >= tag.toFrame)
continue;
var sprite = sprites.Find(x => x.GetSpriteID() == cell.spriteId);
if (sprite == null)
continue;
var keyframe = new ObjectReferenceKeyframe();
var time = GetTimeFromFrame(frames, cell.frameIndex);
keyframe.time = time - startTime;
keyframe.value = sprite;
keyFrames.Add(keyframe);
activeFrames.Add(cell.frameIndex);
}
return activeFrames;
}
static HashSet<int> AddLinkedCellsToClip(IReadOnlyList<LinkedCell> linkedCells, IReadOnlyList<Cell> cells, Tag tag, IReadOnlyList<Sprite> sprites, IReadOnlyList<Frame> frames, List<ObjectReferenceKeyframe> keyFrames)
{
var activeFrames = new HashSet<int>();
var startTime = GetTimeFromFrame(frames, tag.fromFrame);
for (var i = 0; i < linkedCells.Count; ++i)
{
var linkedCell = linkedCells[i];
if (linkedCell.frameIndex < tag.fromFrame ||
linkedCell.frameIndex >= tag.toFrame)
continue;
var cellIndex = cells.FindIndex(x => x.frameIndex == linkedCell.linkedToFrame);
if (cellIndex == -1)
continue;
var cell = cells[cellIndex];
var sprite = sprites.Find(x => x.GetSpriteID() == cell.spriteId);
if (sprite == null)
continue;
var keyframe = new ObjectReferenceKeyframe();
var time = GetTimeFromFrame(frames, linkedCell.frameIndex);
keyframe.time = time - startTime;
keyframe.value = sprite;
keyFrames.Add(keyframe);
activeFrames.Add(linkedCell.frameIndex);
}
return activeFrames;
}
static void DuplicateLastFrame(List<ObjectReferenceKeyframe> keyFrames, Frame lastFrame, float frameRate)
{
if (keyFrames.Count == 0)
return;
var frameTime = 1f / frameRate;
var lastKeyFrame = keyFrames[^1];
var duplicatedFrame = new ObjectReferenceKeyframe();
var time = lastKeyFrame.time + MsToSeconds(lastFrame.duration);
// We remove one AnimationClip frame, since the animation system will automatically add one frame at the end.
time -= frameTime;
duplicatedFrame.time = time;
duplicatedFrame.value = lastKeyFrame.value;
keyFrames.Add(duplicatedFrame);
}
static string GetTransformPath(Transform transform)
{
var path = transform.name;
if (transform.name == k_RootName)
return "";
if (transform.parent.name == k_RootName)
return path;
var parentPath = GetTransformPath(transform.parent) + "/";
path = path.Insert(0, parentPath);
return path;
}
static void AddEnabledKeyframes(Transform layerTransform, Tag tag, IReadOnlyList<Frame> frames, bool doesLayerDisableRenderer, IReadOnlyCollection<int> activeFrames, AnimationClip animationClip)
{
if (activeFrames.Count == tag.noOfFrames && !doesLayerDisableRenderer)
return;
var path = GetTransformPath(layerTransform);
var enabledBinding = EditorCurveBinding.FloatCurve(path, typeof(SpriteRenderer), "m_Enabled");
var enabledKeyframes = new List<Keyframe>();
var disabledPrevFrame = false;
var startTime = GetTimeFromFrame(frames, tag.fromFrame);
for (var frameIndex = tag.fromFrame; frameIndex < tag.toFrame; ++frameIndex)
{
var time = GetTimeFromFrame(frames, frameIndex);
time -= startTime;
if (!activeFrames.Contains(frameIndex) && !disabledPrevFrame)
{
var keyframe = GetBoolKeyFrame(false, time);
enabledKeyframes.Add(keyframe);
disabledPrevFrame = true;
}
else if (activeFrames.Contains(frameIndex) && disabledPrevFrame)
{
var keyframe = GetBoolKeyFrame(true, time);
enabledKeyframes.Add(keyframe);
disabledPrevFrame = false;
}
}
if (enabledKeyframes.Count == 0 && !doesLayerDisableRenderer)
return;
// Make sure there is an enable keyframe on the first frame, if the first frame is active.
if (activeFrames.Contains(tag.fromFrame))
{
var keyframe = GetBoolKeyFrame(true, 0f);
enabledKeyframes.Add(keyframe);
}
var animCurve = new AnimationCurve(enabledKeyframes.ToArray());
AnimationUtility.SetEditorCurve(animationClip, enabledBinding, animCurve);
}
static void AddSortOrderKeyframes(Transform layerTransform, Layer layer, Tag tag, IReadOnlyList<Frame> frames, IReadOnlyList<Cell> cells, bool doesLayerHaveCustomSorting, AnimationClip animationClip)
{
var layerGo = layerTransform.gameObject;
var spriteRenderer = layerGo.GetComponent<SpriteRenderer>();
if (spriteRenderer == null)
return;
var sortOrderKeyframes = new List<Keyframe>();
var path = GetTransformPath(layerTransform);
var sortOrderBinding = EditorCurveBinding.FloatCurve(path, typeof(SpriteRenderer), "m_SortingOrder");
var startTime = GetTimeFromFrame(frames, tag.fromFrame);
var hasKeyOnFirstFrame = false;
for (var i = 0; i < cells.Count; ++i)
{
var previousCell = i > 0 ? cells[i - 1] : default;
var cell = cells[i];
if (cell.frameIndex < tag.fromFrame || cell.frameIndex >= tag.toFrame)
continue;
var additiveSortOrder = cell.additiveSortOrder;
// We want to add a keyframe if the current cell has additive sorting, or if the previous cell had additive sorting.
// This is to ensure that we reset the sort order to 0 if the previous cell had additive sorting.
if (additiveSortOrder == 0 && previousCell.additiveSortOrder == 0)
continue;
if (cell.frameIndex == tag.fromFrame)
hasKeyOnFirstFrame = true;
var time = GetTimeFromFrame(frames, cell.frameIndex) - startTime;
var keyframe = GetIntKeyFrame(layer.index + additiveSortOrder, time);
sortOrderKeyframes.Add(keyframe);
}
if (sortOrderKeyframes.Count == 0 && !doesLayerHaveCustomSorting)
return;
if (!hasKeyOnFirstFrame)
{
var firstFrame = GetIntKeyFrame(layer.index, 0f);
sortOrderKeyframes.Add(firstFrame);
}
var animCurve = new AnimationCurve(sortOrderKeyframes.ToArray());
AnimationUtility.SetEditorCurve(animationClip, sortOrderBinding, animCurve);
}
static float GetTimeFromFrame(IReadOnlyList<Frame> frames, int frameIndex)
{
var totalMs = 0;
for (var i = 0; i < frameIndex; ++i)
totalMs += frames[i].duration;
return MsToSeconds(totalMs);
}
static float MsToSeconds(int ms) => ms / 1000f;
static Keyframe GetBoolKeyFrame(bool value, float time)
{
var keyframe = new Keyframe();
keyframe.value = value ? 1f : 0f;
keyframe.time = time;
keyframe.inTangent = float.PositiveInfinity;
keyframe.outTangent = float.PositiveInfinity;
return keyframe;
}
static Keyframe GetIntKeyFrame(int value, float time)
{
var keyframe = new Keyframe();
keyframe.value = value;
keyframe.time = time;
keyframe.inTangent = float.PositiveInfinity;
keyframe.outTangent = float.PositiveInfinity;
return keyframe;
}
static void AddAnimationEvents(Tag tag, IReadOnlyList<Frame> frames, AnimationClip animationClip)
{
var events = new List<AnimationEvent>();
var startTime = GetTimeFromFrame(frames, tag.fromFrame);
for (var frameIndex = tag.fromFrame; frameIndex < tag.toFrame; ++frameIndex)
{
var frame = frames[frameIndex];
if (frame.eventStrings.Length == 0)
continue;
var frameTime = GetTimeFromFrame(frames, frameIndex);
var eventStrings = frame.eventStrings;
for (var m = 0; m < eventStrings.Length; ++m)
{
events.Add(new AnimationEvent()
{
time = frameTime - startTime,
functionName = eventStrings[m]
});
}
}
if (events.Count > 0)
AnimationUtility.SetAnimationEvents(animationClip, events.ToArray());
}
}
}