using UnityEngine; using System.Collections; using System.Collections.Generic; using System.IO; namespace SpriteCaptureTool { /// /// Captures an animation (optionally from four angles) and produces: /// 1) A single ordered sprite sheet (left‑to‑right, top‑to‑bottom, maxColumns per row). /// 2) A JSON file with weapon‑anchor pixel coordinates for each frame (inside its own cell). /// public class SpriteSheetRenderer : MonoBehaviour { #region Inspector ------------------------------------------------------- [Header("Render Settings")] [Tooltip("Camera used to render individual frames.")] public Camera renderCamera; [Tooltip("Square size, in pixels, of each frame.")] public int frameSize = 512; [Tooltip("Desired output framerate.")] public float frameRate = 12f; [Header("Animation Settings")] [Tooltip("Animator holding the clip to capture.")] public Animator animator; [Tooltip("Name of the clip/state to play.")] public string animationName = "Idle"; [Header("Capture Angles")] [Tooltip("Capture four directions: Front, Right, Back, Left.")] public bool captureMultiAngles = true; [Header("Sprite Sheet Settings")] [Tooltip("Maximum number of columns (frames per row).")] [Range(1, 20)] public int maxColumns = 10; [Header("Anchor Settings")] [Tooltip("Transform positioned on the hand (or desired weapon anchor).")] public Transform anchorTransform; #endregion -------------------------------------------------------------- private readonly List capturedFrames = new(); private readonly List anchorPixels = new(); // pixel coords per frame private int totalFrameCount; private void Start() => StartCoroutine(CaptureAllAngles()); #region Capture --------------------------------------------------------- private IEnumerator CaptureAllAngles() { Vector3 originalRotation = transform.eulerAngles; List angles = new() { new Vector3(0, 0, 0), // Front new Vector3(0, 90, 0), // Right new Vector3(0, 180, 0), // Back new Vector3(0, 270, 0) // Left }; int angleCount = captureMultiAngles ? angles.Count : 1; for (int i = 0; i < angleCount; i++) { transform.eulerAngles = angles[i]; yield return StartCoroutine(CaptureAnimation()); } transform.eulerAngles = originalRotation; SaveSpriteSheetAndJson(); } private IEnumerator CaptureAnimation() { AnimationClip clip = GetAnimationClip(animationName); if (clip == null) { Debug.LogError($"[SpriteSheetRenderer] Clip '{animationName}' not found."); yield break; } totalFrameCount = Mathf.CeilToInt(clip.length * frameRate); animator.Play(animationName, 0, 0f); yield return null; // wait 1 frame for proper pose for (int i = 0; i < totalFrameCount; i++) { yield return new WaitForEndOfFrame(); // --- Render texture -------------------------------------------------- RenderTexture rt = new RenderTexture(frameSize, frameSize, 24); renderCamera.targetTexture = rt; Texture2D frame = new Texture2D(frameSize, frameSize, TextureFormat.RGBA32, false) { filterMode = FilterMode.Point }; renderCamera.Render(); RenderTexture.active = rt; frame.ReadPixels(new Rect(0, 0, frameSize, frameSize), 0, 0); frame.Apply(); renderCamera.targetTexture = null; RenderTexture.active = null; Destroy(rt); capturedFrames.Add(frame); // --- Anchor position ------------------------------------------------- if (anchorTransform != null) { Vector3 vp = renderCamera.WorldToViewportPoint(anchorTransform.position); var pixel = new Vector2(vp.x * frameSize, vp.y * frameSize); anchorPixels.Add(pixel); } else { anchorPixels.Add(Vector2.zero); } yield return new WaitForSeconds(1f / frameRate); } Debug.Log($"[SpriteSheetRenderer] Captured {totalFrameCount} frames."); } #endregion -------------------------------------------------------------- #region Output ---------------------------------------------------------- private void SaveSpriteSheetAndJson() { if (capturedFrames.Count == 0) { Debug.LogWarning("[SpriteSheetRenderer] No frames to save."); return; } int totalFrames = capturedFrames.Count; int columns = Mathf.Min(maxColumns, totalFrameCount); int rows = Mathf.CeilToInt((float)totalFrames / columns); int sheetWidth = columns * frameSize; int sheetHeight = rows * frameSize; Texture2D sheet = new Texture2D(sheetWidth, sheetHeight, TextureFormat.RGBA32, false); for (int i = 0; i < totalFrames; i++) { int col = i % columns; int row = i / columns; // sprite placement (origin bottom‑left) int xPos = col * frameSize; int yPos = (rows - row - 1) * frameSize; // flip Y for texture coords sheet.SetPixels(xPos, yPos, frameSize, frameSize, capturedFrames[i].GetPixels()); } sheet.Apply(); string dir = "Assets/SpriteFrames"; if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); File.WriteAllBytes($"{dir}/SpriteSheet.png", sheet.EncodeToPNG()); File.WriteAllText($"{dir}/AnchorPoints.json", BuildAnchorJson(columns, rows)); Debug.Log($"[SpriteSheetRenderer] ✔ SpriteSheet.png & AnchorPoints.json gerados ({columns}x{rows})."); } /// Returns the requested clip from the AnimatorController. private AnimationClip GetAnimationClip(string clipName) { foreach (var clip in animator.runtimeAnimatorController.animationClips) if (clip.name == clipName) return clip; return null; } /// /// Builds a JSON string with anchor pixel coordinates relative to each frame cell. /// private string BuildAnchorJson(int columns, int rows) { List list = new(capturedFrames.Count); for (int i = 0; i < anchorPixels.Count; i++) { int col = i % columns; int row = i / columns; list.Add(new AnchorFrame { frame = i, column = col, row = row, x = anchorPixels[i].x, y = anchorPixels[i].y }); } AnchorWrapper wrapper = new() { anchors = list.ToArray() }; return JsonUtility.ToJson(wrapper, true); } [System.Serializable] private struct AnchorFrame { public int frame; // absolute index in spritesheet order public int column; // col 0..N public int row; // row 0..M public float x; // pixel inside the frame (origin bottom‑left) public float y; } [System.Serializable] private struct AnchorWrapper { public AnchorFrame[] anchors; } #endregion ------------------------------------------------------------- } }