using UnityEngine; /// /// Swim animation for an inflated paper fish: a sideways wave that grows toward /// the tail and travels backwards, so tail and rear fins swish while the head /// stays steady. Deforms a per-instance copy of the shared mesh on the CPU. /// [RequireComponent(typeof(MeshFilter))] public class FishWiggle : MonoBehaviour { [Tooltip("True if the sprite's nose points +X (tail is at the left of the texture).")] public bool spriteFacesRight = true; [Tooltip("Sideways swing at the tail tip, as a fraction of fish height.")] public float amplitude = 0.10f; [Tooltip("Wave cycles per second when cruising.")] public float frequency = 1.6f; [Tooltip("How far along the body the wave is (0 = only the tail moves, 1 = whole body).")] [Range(0f, 1f)] public float bodyShare = 0.25f; [Tooltip("Speed multiplier from the controller; 1 = cruising.")] public float effort = 1f; Mesh mesh; Vector3[] baseVerts, verts; float[] tail; // 0 at the nose .. 1 at the tail tip float phase; void Start() { var mf = GetComponent(); var src = mf.sharedMesh; if (src == null) { enabled = false; return; } mesh = Object.Instantiate(src); mesh.name = src.name + " (wiggle)"; mesh.MarkDynamic(); mf.sharedMesh = mesh; baseVerts = mesh.vertices; verts = new Vector3[baseVerts.Length]; var uv = mesh.uv; tail = new float[baseVerts.Length]; for (int i = 0; i < tail.Length; i++) { float u = uv.Length == baseVerts.Length ? uv[i].x : baseVerts[i].x + 0.5f; float t = spriteFacesRight ? 1f - u : u; // ease in: head barely moves, last third of the body carries the swing tail[i] = Mathf.Pow(Mathf.Clamp01((t - (1f - bodyShare) * 0.6f) / (1f - (1f - bodyShare) * 0.6f)), 2.2f); } phase = Random.value * Mathf.PI * 2f; } void Update() { if (mesh == null) return; phase += Time.deltaTime * frequency * Mathf.Max(0.2f, effort) * Mathf.PI * 2f; float amp = amplitude * Mathf.Clamp(effort, 0.5f, 1.5f); for (int i = 0; i < verts.Length; i++) { var v = baseVerts[i]; float k = tail[i]; // wave travels head -> tail: phase lags with tailness v.z += Mathf.Sin(phase - k * 2.5f) * amp * k; verts[i] = v; } mesh.vertices = verts; mesh.RecalculateBounds(); } void OnDestroy() { if (mesh != null) Destroy(mesh); } }