mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-01 11:15:33 +00:00
94 lines
3.4 KiB
C#
94 lines
3.4 KiB
C#
|
|
using Godot;
|
|||
|
|
|
|||
|
|
namespace Cirno.Scripts.Interactables;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Animates a tiled texture by shifting the UV1 vertical offset on a mesh surface material.
|
|||
|
|
/// Designed for Blockbench-exported meshes whose texture is a vertical sprite sheet
|
|||
|
|
/// (e.g. 16×64 with 4 frames stacked top-to-bottom).
|
|||
|
|
///
|
|||
|
|
/// Usage:
|
|||
|
|
/// 1. Attach this node as a child of (or sibling to) the MeshInstance3D you want to animate.
|
|||
|
|
/// 2. Optionally assign <see cref="TargetMesh"/> in the inspector; if left empty the node
|
|||
|
|
/// will search its parent for the first MeshInstance3D automatically.
|
|||
|
|
/// 3. Set <see cref="FrameCount"/> to the number of frames in the sprite sheet.
|
|||
|
|
/// 4. Drive <see cref="CurrentFrame"/> from an AnimationPlayer track — the script handles
|
|||
|
|
/// the UV offset calculation.
|
|||
|
|
/// </summary>
|
|||
|
|
[Tool]
|
|||
|
|
public partial class FrameAnimator3D : Node3D
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// The mesh whose surface_0 material UV offset will be updated.
|
|||
|
|
/// If null, the node searches its parent for the first MeshInstance3D on _Ready.
|
|||
|
|
/// </summary>
|
|||
|
|
[Export] public MeshInstance3D TargetMesh { get; set; }
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Total number of animation frames stacked vertically in the texture.
|
|||
|
|
/// </summary>
|
|||
|
|
[Export] public int FrameCount { get; set; } = 4;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Surface index on the mesh to apply the UV offset to.
|
|||
|
|
/// Defaults to 0, matching Blockbench's typical single-surface export.
|
|||
|
|
/// </summary>
|
|||
|
|
[Export] public int SurfaceIndex { get; set; } = 0;
|
|||
|
|
|
|||
|
|
private int _currentFrame;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// The active frame index (0-based). Set this from an AnimationPlayer track to animate.
|
|||
|
|
/// Clamped to [0, <see cref="FrameCount"/> - 1].
|
|||
|
|
/// </summary>
|
|||
|
|
[Export]
|
|||
|
|
public int CurrentFrame
|
|||
|
|
{
|
|||
|
|
get => _currentFrame;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
_currentFrame = Mathf.Clamp(value, 0, Mathf.Max(0, FrameCount - 1));
|
|||
|
|
ApplyFrame();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public override void _Ready()
|
|||
|
|
{
|
|||
|
|
// Auto-discover the mesh from the parent if none was assigned in the inspector.
|
|||
|
|
if (TargetMesh is null)
|
|||
|
|
{
|
|||
|
|
TargetMesh = GetParent() as MeshInstance3D
|
|||
|
|
?? GetParentOrNull<Node>()?.FindChild("*", recursive: false, owned: false) as MeshInstance3D;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ApplyFrame();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Calculates the normalised V offset for <see cref="CurrentFrame"/> and writes it to the
|
|||
|
|
/// override material on <see cref="TargetMesh"/>'s surface.
|
|||
|
|
/// Each frame occupies an equal vertical slice: offset = frame / frameCount.
|
|||
|
|
/// </summary>
|
|||
|
|
private void ApplyFrame()
|
|||
|
|
{
|
|||
|
|
if (TargetMesh is null || FrameCount <= 0) return;
|
|||
|
|
|
|||
|
|
// Ensure we have an override material to write to so we don't mutate shared resources.
|
|||
|
|
if (TargetMesh.GetSurfaceOverrideMaterial(SurfaceIndex) is not StandardMaterial3D mat)
|
|||
|
|
{
|
|||
|
|
var baseMat = TargetMesh.GetActiveMaterial(SurfaceIndex);
|
|||
|
|
|
|||
|
|
// Duplicate the base material so sibling nodes keep their own state.
|
|||
|
|
mat = baseMat?.Duplicate() as StandardMaterial3D;
|
|||
|
|
if (mat is null) return;
|
|||
|
|
|
|||
|
|
TargetMesh.SetSurfaceOverrideMaterial(SurfaceIndex, mat);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Each frame is a 1/FrameCount slice; V offset moves down one slice per frame.
|
|||
|
|
float vOffset = (float)_currentFrame / FrameCount;
|
|||
|
|
mat.Uv1Offset = new Vector3(mat.Uv1Offset.X, vOffset, mat.Uv1Offset.Z);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|