BulletHell/Assets/BulletHellTemplate/Core/Misc/SpriteSheetRenderer.cs

229 lines
8.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace SpriteCaptureTool
{
/// <summary>
/// Captures an animation (optionally from four angles) and produces:
/// 1) A single ordered sprite sheet (lefttoright, toptobottom, maxColumns per row).
/// 2) A JSON file with weaponanchor pixel coordinates for each frame (inside its own cell).
/// </summary>
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<Texture2D> capturedFrames = new();
private readonly List<Vector2> anchorPixels = new(); // pixel coords per frame
private int totalFrameCount;
private void Start() => StartCoroutine(CaptureAllAngles());
#region Capture ---------------------------------------------------------
private IEnumerator CaptureAllAngles()
{
Vector3 originalRotation = transform.eulerAngles;
List<Vector3> 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 bottomleft)
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}).");
}
/// <summary>Returns the requested clip from the AnimatorController.</summary>
private AnimationClip GetAnimationClip(string clipName)
{
foreach (var clip in animator.runtimeAnimatorController.animationClips)
if (clip.name == clipName) return clip;
return null;
}
/// <summary>
/// Builds a JSON string with anchor pixel coordinates relative to each frame cell.
/// </summary>
private string BuildAnchorJson(int columns, int rows)
{
List<AnchorFrame> 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 bottomleft)
public float y;
}
[System.Serializable]
private struct AnchorWrapper { public AnchorFrame[] anchors; }
#endregion -------------------------------------------------------------
}
}