using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using Assets.HeroEditor4D.Common.Scripts.CharacterScripts; using Assets.HeroEditor4D.Common.Scripts.Common; using UnityEngine; using UnityEngine.UI; namespace Assets.HeroEditor4D.Common.Scripts.EditorScripts { /// /// Used for creating sprite sheets for frame-by-frame animation. /// public class SpriteSequenceCapture : MonoBehaviour { public Character4D Character4D; public GameObject Canvas; public GameObject Camera; public string UpperBodyAnimationFolder; public string LowerBodyAnimationFolder; public string ComplexAnimationFolder; public List UpperBodyClipNames; public List LowerBodyClipNames; public List ComplexClipNames; public Dropdown DirectionDropdown; public Dropdown PrimaryAnimationDropdown; public Dropdown SecondaryAnimationDropdown; public Slider CameraSizeSlider; public Dropdown FrameSizeDropdown; public Dropdown FrameRatioDropdown; public Dropdown ScreenshotIntervalDropdown; public Dropdown ShadowDropdown; public GameObject Shadow; public ScreenshotTransparent ScreenshotTransparent; #if UNITY_EDITOR /// /// Called only in Editor. /// public void OnValidate() { UpperBodyClipNames = Directory.GetFiles(UpperBodyAnimationFolder, "*.anim", SearchOption.AllDirectories).Select(Path.GetFileNameWithoutExtension).ToList(); LowerBodyClipNames = Directory.GetFiles(LowerBodyAnimationFolder, "*.anim", SearchOption.AllDirectories).Select(Path.GetFileNameWithoutExtension).ToList(); ComplexClipNames = Directory.GetFiles(ComplexAnimationFolder, "*.anim", SearchOption.AllDirectories).Select(Path.GetFileNameWithoutExtension).ToList(); UpperBodyClipNames.AddRange(ComplexClipNames); for (var i = 0; i < UpperBodyClipNames.Count; i++) { if (UpperBodyClipNames[i].EndsWith("U")) UpperBodyClipNames[i] = UpperBodyClipNames[i].Substring(0, UpperBodyClipNames[i].Length - 1); } for (var i = 0; i < LowerBodyClipNames.Count; i++) { if (LowerBodyClipNames[i].EndsWith("L")) LowerBodyClipNames[i] = LowerBodyClipNames[i].Substring(0, LowerBodyClipNames[i].Length - 1); } PrimaryAnimationDropdown.options = new List(); SecondaryAnimationDropdown.options = new List(); foreach (var clipName in UpperBodyClipNames) { PrimaryAnimationDropdown.options.Add(new Dropdown.OptionData(clipName)); } foreach (var clipName in LowerBodyClipNames) { SecondaryAnimationDropdown.options.Add(new Dropdown.OptionData(clipName)); } PrimaryAnimationDropdown.value = UpperBodyClipNames.IndexOf("Slash1H"); SecondaryAnimationDropdown.value = LowerBodyClipNames.IndexOf("Idle"); } /// /// Called on start. /// public void Start() { if (Character4D) Character4D.SetDirection(Vector2.left); foreach (var dropdown in new[] { PrimaryAnimationDropdown, SecondaryAnimationDropdown, FrameSizeDropdown, FrameRatioDropdown, ScreenshotIntervalDropdown, ShadowDropdown }) { dropdown.RefreshShownValue(); } if (UpperBodyClipNames.Count == 0) OnValidate(); } /// /// Called when direction dropdown changed. /// /// public void OnDirectionChanged(int value) { switch (value) { case 0: Character4D.SetDirection(Vector2.left); break; case 1: Character4D.SetDirection(Vector2.right); break; case 2: Character4D.SetDirection(Vector2.down); break; case 3: Character4D.SetDirection(Vector2.up); break; } } /// /// Load character from prefab. /// public void Load() { var path = UnityEditor.EditorUtility.OpenFilePanel("Open character prefab", "", "prefab"); if (path.Length > 0) { path = "Assets" + path.Replace(Application.dataPath, null); Load(path); } } /// /// Load character from prefab by given path. /// public void Load(string path) { var character = UnityEditor.AssetDatabase.LoadAssetAtPath(path); if (character == null) throw new Exception("Error loading character, please make sure you are loading correct prefab!"); if (Character4D != null) Destroy(Character4D.gameObject); Character4D = Instantiate(character, transform); Character4D.transform.localPosition = Vector3.zero; Shadow = Character4D.transform.Find("Shadow").gameObject; OnDirectionChanged(DirectionDropdown.value); var mat = new Material(Shader.Find("Sprites/Default")); foreach (var spriteRenderer in Character4D.GetComponentsInChildren()) { if (spriteRenderer.name != "Eyes") { spriteRenderer.material = mat; } } Debug.LogWarning("All materials were replaced by [Sprites/Default] to avoid outline artefacts."); } /// /// Create sprite sheet. /// public void Capture() { var frameSize = new[] { 256, 512, 1024 }[FrameSizeDropdown.value]; var frameRatio = FrameRatioDropdown.value + 4; var interval = new[] { 0.1f, 0.25f, 0.5f, 1f }[ScreenshotIntervalDropdown.value]; Camera.GetComponent().orthographicSize = CameraSizeSlider.value; Shadow.SetActive(ShadowDropdown.value == 0); var upperClips = new List { UpperBodyClipNames[PrimaryAnimationDropdown.value] }; var lowerClips = new List { LowerBodyClipNames[SecondaryAnimationDropdown.value] }; StartCoroutine(CaptureFrames(upperClips, lowerClips, frameSize, frameRatio, interval)); } private void ShowFrame(string upperClip, string lowerClip, float normalizedTime) { if (ComplexClipNames.Contains(upperClip)) { Character4D.Animator.Play(upperClip, 2, normalizedTime); } else { Character4D.Animator.Play(upperClip, 1, normalizedTime); Character4D.Animator.Play(lowerClip, 0, normalizedTime); } Character4D.Animator.speed = 0; if (Character4D.Animator.IsInTransition(1)) { Debug.Log("IsInTransition"); } } private IEnumerator CaptureFrames(List upperClips, List lowerClips, int frameSize, int frameRatio, float interval) { Canvas.SetActive(false); var death = upperClips.Any(i => i.Contains("Death")); foreach (var upperClip in upperClips) { foreach (var lowerClip in lowerClips) { for (var i = 0; i < frameRatio; i++) { ShowFrame(upperClip, lowerClip, (float) i / (frameRatio - 1)); yield return new WaitForSeconds(interval); string direction; switch (DirectionDropdown.value) { case 0: direction = "Left"; break; case 1: direction = "Right"; break; case 2: direction = "Front"; break; case 3: direction = "Back"; break; default: throw new NotImplementedException(); } var path = death ? $"{Application.dataPath.Replace("/Assets", null)}/SpriteSheets/{direction}/{upperClip}/{i}.png" : $"{Application.dataPath.Replace("/Assets", null)}/SpriteSheets/{direction}/{upperClip}-{lowerClip}/{i}.png"; ScreenshotTransparent.Width = ScreenshotTransparent.Height = frameSize; ScreenshotTransparent.Capture(path); } } if (death) break; } Canvas.SetActive(true); } #endif } }