private GeneratedClip[] FinalizeAnimationClips(AsepriteFileInfo aseInfo, List <AnimationClip> clips) { m_clipCount = clips.Count; var previousGeneratedClips = new Dictionary <string, GeneratedClip>(); foreach (var generatedClip in m_generatedClips) { previousGeneratedClips.Add(generatedClip.name, generatedClip); } var newGeneratedClips = new List <GeneratedClip>(m_clipCount); for (int i = 0; i < m_clipCount; i++) { AnimationClip clip = clips[i]; string tagName = aseInfo.spriteSheetData.meta.frameTags[i].name; bool clipDoesExist = previousGeneratedClips.TryGetValue(tagName, out GeneratedClip clipData); if (clipDoesExist) { clipData.name = tagName; clipData.clip = clipData.createMode != GeneratedClip.CreateMode.Merge ? clip : null; } else { clipData = GeneratedClip.Create(tagName, clip); } newGeneratedClips.Add(clipData); } return(newGeneratedClips.ToArray()); }
private void GenerateAssets(Settings settings, AsepriteFileInfo aseInfo, AssetImportContext context) { string atlasPath = $"{aseInfo.absoluteDirectoryPath}/{aseInfo.title}_aseprite.png"; string dataPath = $"{aseInfo.absoluteDirectoryPath}/{aseInfo.title}_aseprite.json"; // Create a temporary valid JSON file so Aseprite has something to write into // The file will be deleted after the json data is transferred to the meta file of the generated atlas File.WriteAllText(dataPath, "{}"); // Use Aseprite to build an atlas from the file and generate accompanying JSON data AsepriteCLI.Run( "--batch", "--debug", $"\"{aseInfo.absolutePath}\"", "--filename-format {title}_{tag}-{tagframe}", "--sheet-type packed", "--inner-padding 1", "--trim", $"--sheet \"{atlasPath}\"", "--list-tags", "--format json-array", $"--data \"{dataPath}\"" ); // Get the JSON data generated by Aseprite, then delete the temp file aseInfo.spriteSheetData = JsonUtility.FromJson <SpriteSheetData>(File.ReadAllText(dataPath)); File.Delete(dataPath); // Create Texture2D from Aseprite atlas, then delete the temp file m_atlas = CreateSpriteAtlasTexture( aseInfo.spriteSheetData.meta.size.w, aseInfo.spriteSheetData.meta.size.h, File.ReadAllBytes(atlasPath) ); File.Delete(atlasPath); // Create Sprites from sheet data List <Sprite> sprites = CreateSpritesForAtlas( m_atlas, aseInfo.spriteSheetData ); m_spriteCount = sprites.Count; // Create AnimationClip subassets, if specified m_generatedClips = generateAnimationClips ? CreateAnimationClips(settings, aseInfo, sprites) : new GeneratedClip[0]; m_clipCount = m_generatedClips.Length; // Add all subassets to the import context context.AddObjectToAsset(m_atlas.name, m_atlas); foreach (var sprite in sprites) { context.AddObjectToAsset(sprite.name, sprite); } foreach (var clipData in m_generatedClips) { if (clipData.createMode != GeneratedClip.CreateMode.Merge) { context.AddObjectToAsset(clipData.name, clipData.clip); } } }
private GeneratedClip[] CreateAnimationClips(Settings settings, AsepriteFileInfo aseInfo, List <Sprite> sprites) { SpriteSheetData sheetData = aseInfo.spriteSheetData; var clipInfoLookup = new Dictionary <string, List <(Sprite sprite, int duration)> >(); foreach (SpriteSheetData.FrameTag frameTag in sheetData.meta.frameTags) { var framesInfo = new List <(Sprite sprite, int duration)>(); for (int i = frameTag.from; i <= frameTag.to; i++) { SpriteSheetData.Frame frame = sheetData.frames[i]; Sprite sprite = sprites.Find((s) => { return(frame.filename.Equals(s.name)); }); if (ReferenceEquals(sprite, null)) { continue; } int duration = frame.duration; framesInfo.Add((sprite, duration)); } string clipName = $"aseprite_{aseInfo.title}_{frameTag.name}"; clipInfoLookup.Add(clipName, framesInfo); } var clips = new List <AnimationClip>(clipInfoLookup.Count); foreach (var kvp in clipInfoLookup) { string clipName = kvp.Key; List <(Sprite sprite, int duration)> clipInfo = kvp.Value; AnimationClip clip = new AnimationClip(); clip.wrapMode = WrapMode.Loop; clip.name = clipName; int[] frameTimesInMilliseconds = new int[clipInfo.Count]; for (int iFrame = 0; iFrame < frameTimesInMilliseconds.Length; iFrame++) { frameTimesInMilliseconds[iFrame] = clipInfo[iFrame].duration; } clip.frameRate = ClipSettings.CalculateAutoFrameRate(settings.MaxSampleRate, frameTimesInMilliseconds); AnimationClipSettings currentClipSettings = AnimationUtility.GetAnimationClipSettings(clip); currentClipSettings.loopTime = true; AnimationUtility.SetAnimationClipSettings(clip, currentClipSettings); var spriteBinding = new EditorCurveBinding(); spriteBinding.type = typeof(SpriteRenderer); spriteBinding.path = clipSettings.spriteRendererPath; spriteBinding.propertyName = "m_Sprite"; int clipLength = clipInfo.Count; var keyframes = new List <ObjectReferenceKeyframe>(clipLength + 1); float currentDuration = 0f; for (int i = 0; i < clipLength; i++) { var keyframe = new ObjectReferenceKeyframe(); keyframe.value = clipInfo[i].sprite; keyframe.time = currentDuration; keyframes.Add(keyframe); // Divide frame duration by 1000 because it is specified by Aseprite in milliseconds. float keyDuration = clipInfo[i].duration / 1000f; currentDuration += keyDuration; // TODO: Miscreant: Do these calculations before any sec/msec conversions for more precision // Tack on a duplicate of the last keyframe to ensure the last frame gets its full duration if (i == clipLength - 1 && keyDuration > (1.0f / clip.frameRate)) { keyframe = new ObjectReferenceKeyframe(); // The last frame will persist for one full sample, so subtract that from the current time keyframe.time = currentDuration - (1.0f / clip.frameRate); keyframe.value = clipInfo[i].sprite; keyframes.Add(keyframe); } } AnimationUtility.SetObjectReferenceCurve( clip, spriteBinding, keyframes.ToArray() ); clips.Add(clip); } return(FinalizeAnimationClips(aseInfo, clips)); }