Aetheria/Assets/Scripts/Gameplay/ImpactPresenter.cs
Meta Vens 97d94ecfae Present landed impacts: shield ripple, hull reaction and camera kick through one ImpactPresenter
ImpactPresenter (bound on every EntityInstance by SetEntity through the one CapabilityPresentationBinder) renders
feedback-1's absorbs: Feedback.Cue decides intensity and shake, DrawAhead.Carried the drawn point; a hull absorb
spawns ImpactFeedback's pooled effect under the instance, and a cue with shake raises a Cinemachine impulse.
ShieldManager ripples on a Shield-layer absorb. MainMenu gains a Screen Shake slider that persists on Back.
Editor: WireImpactFeedback (scene wiring, scene diff in the next commit) and ImpactPresenterSmoke.
Unity batchmode compile 0 error CS; ImpactPresenterSmoke exit 0. The scene is not yet wired in this commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:10:04 +02:00

47 lines
2.1 KiB
C#

using System.Collections.Generic;
using CultMath.UnityBridge;
using UnityEngine;
// How one entity's landed impacts look and feel: the hull reaction at the drawn impact point, and the player's camera
// kick. The simulation publishes each impact once on the entity's Capabilities (FireControl); Feedback.Cue decides how
// hard it reads and whether the camera feels it, DrawAhead.Carried decides where it is drawn; this decides nothing.
// The shield ripple is ShieldManager's. Everything here is display-only and runs in real time.
public class ImpactPresenter : MonoBehaviour, IAbsorbPresenter
{
public Entity Entity;
private readonly List<Prototype> _live = new List<Prototype>();
public void Absorb(AbsorbEvent e)
{
var feedback = ImpactFeedback.Instance;
if (feedback == null || Entity == null) return;
var manager = ActionGameManager.Instance;
var cue = Feedback.Cue(e, Entity.EquippedHull.Data.Durability,
manager != null && Entity == manager.CurrentEntity,
ActionGameManager.PlayerSettings.GraphicsSettings.ScreenShake, feedback.IntensityScale);
var point = DrawAhead.Carried(Entity, cue.Position, ActionGameManager.Clock.Lead).ToUnity();
if (e.Layer == AbsorbLayer.Hull && feedback.HullEffect != null)
{
_live.RemoveAll(live => live == null || !live.gameObject.activeSelf);
var effect = feedback.HullEffect.Instantiate<Prototype>();
var size = Mathf.Max(feedback.MinEffectScale, cue.Intensity);
effect.transform.SetParent(transform, false);
effect.transform.position = point;
effect.transform.localScale = Vector3.one * (size / Mathf.Max(transform.lossyScale.x, 1e-3f));
_live.Add(effect);
}
if (cue.Shake > 0f)
feedback.Kick(point, new Vector3(cue.Direction.x, 0f, cue.Direction.z), cue.Shake);
}
// A live effect is a pooled child of this instance; hand it back before the instance takes it down with it.
private void OnDestroy()
{
foreach (var live in _live)
if (live != null && live.gameObject.activeSelf) live.ReturnToPool();
}
}