Aetheria/Assets/Scripts/Gameplay/Weapons/Projectile.cs
Meta Vens aa9288d9de Projectile is drawn on FireControl.RoundAt and integrates nothing
Deletes the Time.deltaTime gravity/drag/position integration and the range-distance kill from Projectile and the
UnityEngine.Random jitter, barrel direction and inherited velocity from ProjectileManager. A round holds a copy of
its PendingShot and draws at RoundAt(Zone.Time + Clock.Lead) with a barrel offset decaying over BlendTime; ShotResolved
ends a hit or burst at the outcome's BurstPoint, a miss flies to the FireRange clamp. Unity compile not yet run at
this commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:19:23 +02:00

91 lines
3.6 KiB
C#

using System;
using System.Collections;
using UniRx;
using UnityEngine;
using static CultMath.math;
// A ballistic round, drawn on FireControl.RoundAt and deciding nothing. The simulation owns the round's whole flight
// (origin, direction, speed, fire time, arrival, range: the PendingShot copy taken at spawn), so this object stores no
// velocity or position of its own and integrates nothing: each frame it asks RoundAt where the round is at sim time
// plus the clock's Lead (the same draw-ahead ships use), and the simulation's ShotResolved ends a round that hit or
// burst. A round that missed flies on to the FireRange point RoundAt clamps at, and is done there. The barrel is only
// where the round is first seen: its offset from the sim line decays to zero over BlendTime sim seconds. The trail's
// fade after the end stays real time (pure presentation, ruling sim-speed-presentation).
public class Projectile : MonoBehaviour
{
public TrailRenderer Trail;
public Prototype HitEffect;
// Sim seconds over which the barrel's offset from the sim line decays to nothing.
public float BlendTime = .1f;
private PendingShot _shot;
private Zone _zone;
private Vector3 _barrelOffset;
private bool _alive;
private IDisposable _binding;
// Takes the frozen shot, the zone whose sim time it is drawn at, and where the barrel is as this round is first seen.
public void Launch(Zone zone, PendingShot shot, Vector3 barrel)
{
_zone = zone;
_shot = shot;
var origin = shot.FireOrigin;
_barrelOffset = barrel - new Vector3(origin.x, origin.y, origin.z);
_binding?.Dispose();
_binding = zone.ShotResolved.Where(o => o.ShotId == shot.ShotId).Subscribe(Resolve);
_alive = true;
transform.forward = new Vector3(shot.TravelDirection.x, 0, shot.TravelDirection.y);
transform.position = barrel;
}
private Vector3 At(CultMath.float2 planar, float time)
{
var blend = BlendTime > 0 ? saturate(1 - (time - _shot.FireTime) / BlendTime) : 0f;
return new Vector3(planar.x, _shot.FireOrigin.y, planar.y) + _barrelOffset * blend;
}
void Update()
{
if (!_alive) return;
var time = _zone.Time + ActionGameManager.Clock.Lead;
transform.position = At(FireControl.RoundAt(_shot, time), time);
var end = _shot.Speed > .01f ? _shot.FireTime + _shot.FireRange / _shot.Speed : _shot.FireTime;
if (time >= end) Finish();
}
// The simulation's verdict. A round that hit or burst stops where the sim put it, at the outcome's burst point,
// else where it arrives on its line; a miss keeps flying.
private void Resolve(ShotOutcome outcome)
{
if (!_alive || (outcome.Result != ShotResult.Hit && outcome.Result != ShotResult.Burst)) return;
var planar = outcome.HasBurstPoint ? outcome.BurstPoint : FireControl.RoundAt(_shot, _shot.ArrivalTime);
transform.position = At(planar, _shot.ArrivalTime);
if (HitEffect != null) HitEffect.Instantiate<Transform>().position = transform.position;
Finish();
}
private void Finish()
{
_alive = false;
_binding?.Dispose();
_binding = null;
StartCoroutine(Fade());
}
void OnDisable()
{
_alive = false;
_binding?.Dispose();
_binding = null;
}
IEnumerator Fade()
{
var startTime = Time.time;
var lifetime = Trail.time;
while (Time.time - startTime < lifetime)
yield return null;
GetComponent<Prototype>().ReturnToPool();
}
}