165 lines
5.0 KiB
C#
165 lines
5.0 KiB
C#
// RingGateEffect.cs
|
|
// Zweck: Erzeugt „Geister“-Kopien (Meshes), die aufsteigen + verblassen (Holo-Effekt um die Gates).
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class RingGateEffect : MonoBehaviour
|
|
{
|
|
[Header("Material/Color")]
|
|
[Tooltip("Transparent/Unlit empfohlen. Falls leer: benutze Material dieses Objekts.")]
|
|
public Material hologramMaterial;
|
|
[ColorUsage(true, true)] public Color baseColor = new Color(0.3f, 0.9f, 1f, 0.9f);
|
|
|
|
[Header("Emission")]
|
|
[Min(0f)] public float spawnRate = 0.8f;
|
|
[Range(1, 64)] public int maxGhosts = 12;
|
|
public bool emitting = true;
|
|
public bool burstOnEnable = true;
|
|
|
|
[Header("Motion & Life")]
|
|
[Min(0.1f)] public float lifetime = 2.2f;
|
|
public float riseDistance = 0.6f;
|
|
public float yawDegPerSec = 35f;
|
|
public float startScale = 1f;
|
|
public float endScale = 1.12f;
|
|
|
|
[Header("Curves")]
|
|
public AnimationCurve heightOverLife = AnimationCurve.EaseInOut(0, 0, 1, 1);
|
|
public AnimationCurve alphaOverLife = new AnimationCurve(new Keyframe(0, 0.9f), new Keyframe(1, 0f));
|
|
public AnimationCurve scaleOverLife = new AnimationCurve(new Keyframe(0, 0f), new Keyframe(1, 1f));
|
|
|
|
private struct Ghost
|
|
{
|
|
public Transform tf;
|
|
public float age;
|
|
public Renderer rend;
|
|
public MaterialPropertyBlock mpb;
|
|
}
|
|
|
|
private readonly List<Ghost> pool = new();
|
|
private readonly List<Ghost> alive = new();
|
|
private float spawnAccum;
|
|
private Mesh sourceMesh;
|
|
private Material sourceMat;
|
|
private Transform container;
|
|
|
|
private static readonly int _BaseColor = Shader.PropertyToID("_BaseColor");
|
|
private static readonly int _Color = Shader.PropertyToID("_Color");
|
|
|
|
private void Awake()
|
|
{
|
|
var mf = GetComponent<MeshFilter>();
|
|
var mr = GetComponent<MeshRenderer>();
|
|
if (!mf || !mr || !mf.sharedMesh)
|
|
{
|
|
Debug.LogError($"[{name}] Benötigt MeshFilter+MeshRenderer mit Mesh.");
|
|
enabled = false; return;
|
|
}
|
|
sourceMesh = mf.sharedMesh;
|
|
sourceMat = hologramMaterial ? hologramMaterial : mr.sharedMaterial;
|
|
|
|
container = new GameObject($"{name}_Ghosts").transform;
|
|
container.SetParent(transform, false);
|
|
|
|
// Pool vorwärmen
|
|
for (int i = 0; i < maxGhosts; i++)
|
|
{
|
|
var go = new GameObject($"Ghost_{i}");
|
|
go.transform.SetParent(container, false);
|
|
|
|
var ghostMF = go.AddComponent<MeshFilter>();
|
|
ghostMF.sharedMesh = sourceMesh;
|
|
|
|
var ghostMR = go.AddComponent<MeshRenderer>();
|
|
ghostMR.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
|
ghostMR.receiveShadows = false;
|
|
ghostMR.sharedMaterial = sourceMat;
|
|
|
|
var g = new Ghost
|
|
{
|
|
tf = go.transform,
|
|
age = lifetime + 1f,
|
|
rend = ghostMR,
|
|
mpb = new MaterialPropertyBlock()
|
|
};
|
|
go.SetActive(false);
|
|
pool.Add(g);
|
|
}
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
if (burstOnEnable)
|
|
{
|
|
for (int i = 0; i < Mathf.Min(4, maxGhosts); i++) Spawn();
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
float dt = Time.deltaTime;
|
|
|
|
if (emitting && spawnRate > 0f)
|
|
{
|
|
spawnAccum += spawnRate * dt;
|
|
while (spawnAccum >= 1f)
|
|
{
|
|
spawnAccum -= 1f;
|
|
Spawn();
|
|
}
|
|
}
|
|
|
|
// Simulation/Render
|
|
for (int i = alive.Count - 1; i >= 0; i--)
|
|
{
|
|
var g = alive[i];
|
|
g.age += dt;
|
|
if (g.age >= lifetime)
|
|
{
|
|
g.tf.gameObject.SetActive(false);
|
|
alive.RemoveAt(i);
|
|
pool.Add(g);
|
|
continue;
|
|
}
|
|
|
|
float t = Mathf.Clamp01(g.age / lifetime);
|
|
|
|
// Bewegung
|
|
float h01 = Mathf.Clamp01(heightOverLife.Evaluate(t));
|
|
float y = riseDistance * h01;
|
|
float s01 = Mathf.Clamp01(scaleOverLife.Evaluate(t));
|
|
float s = Mathf.Lerp(startScale, endScale, s01);
|
|
|
|
g.tf.localPosition = new Vector3(0f, y, 0f);
|
|
g.tf.localRotation = Quaternion.Euler(0f, yawDegPerSec * g.age, 0f);
|
|
g.tf.localScale = Vector3.one * s;
|
|
|
|
// Farbe/Alpha
|
|
float a = Mathf.Clamp01(alphaOverLife.Evaluate(t));
|
|
var c = baseColor; c.a *= a;
|
|
|
|
g.mpb.Clear();
|
|
g.mpb.SetColor(_BaseColor, c);
|
|
g.mpb.SetColor(_Color, c);
|
|
g.rend.SetPropertyBlock(g.mpb);
|
|
|
|
alive[i] = g;
|
|
}
|
|
}
|
|
|
|
private void Spawn()
|
|
{
|
|
if (pool.Count == 0) return;
|
|
var g = pool[pool.Count - 1];
|
|
pool.RemoveAt(pool.Count - 1);
|
|
|
|
g.age = 0f;
|
|
g.tf.localPosition = Vector3.zero;
|
|
g.tf.localRotation = Quaternion.identity;
|
|
g.tf.localScale = Vector3.one * startScale;
|
|
g.tf.gameObject.SetActive(true);
|
|
|
|
alive.Add(g);
|
|
}
|
|
}
|