- ActionGameManager: the visible hostile/friendly indicators and MenuMap.Position read DrawAhead.Position at Clock.Lead. - DrawAhead.Round drops its lead parameter: the target's lead is the drawn time less its zone's step time, derived inside, so a caller cannot pass two times that disagree. Projectile and BallisticFlightTests updated; the hit tests derive their lead from the drawn time; two assertions that only varied the now-absent lead are removed. - Zone.RailAt logs "Requested orbit is not part of this zone!" for a missing parent again (DrawnPoseTests.AMissingParentOrbitIsLoggedAtADrawnTime). Builds: not yet compiled at this commit (verified by the run that follows). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
101 lines
3.7 KiB
C#
101 lines
3.7 KiB
C#
using System;
|
|
using System.Collections;
|
|
using UniRx;
|
|
using UnityEngine;
|
|
|
|
// A ballistic round, drawn by DrawAhead.Round from what the simulation publishes and deciding nothing. The simulation
|
|
// owns the round's facts (the PendingShot copy taken at spawn, replaced by the sim's record at ShotCommitted, and the
|
|
// outcome it publishes), so this object stores no motion state of its own and computes no position, time, range or
|
|
// impact point: each frame it asks DrawAhead.Round where the round is at sim time plus the clock's Lead (the same
|
|
// draw-ahead ships use). The round ends on the sim's word: ShotResolved ends one that hit or burst, and
|
|
// DrawAhead.RoundOver ends one the sim published as a miss. The barrel is only where the round is first seen: its
|
|
// offset from the drawn 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 CultMath.float3 _barrel;
|
|
private ShotOutcome _known;
|
|
private bool _alive;
|
|
private CompositeDisposable _binding;
|
|
|
|
// Takes the shot as the sim holds it, 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;
|
|
_barrel = new CultMath.float3(barrel.x, barrel.y, barrel.z);
|
|
_known = null;
|
|
_binding?.Dispose();
|
|
_binding = new CompositeDisposable(
|
|
zone.ShotCommitted.Where(o => o.ShotId == shot.ShotId).Subscribe(Committed),
|
|
zone.ShotResolved.Where(o => o.ShotId == shot.ShotId).Subscribe(Resolve));
|
|
_alive = true;
|
|
transform.forward = new Vector3(shot.TravelDirection.x, 0, shot.TravelDirection.y);
|
|
Draw();
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (_alive) Draw();
|
|
}
|
|
|
|
private void Draw()
|
|
{
|
|
var lead = ActionGameManager.Clock.Lead;
|
|
var drawn = DrawAhead.Time(_zone, lead);
|
|
transform.position = V(DrawAhead.Round(_shot, _known, _barrel, BlendTime, drawn));
|
|
if (DrawAhead.RoundOver(_shot, _known, drawn)) Finish();
|
|
}
|
|
|
|
// The sim's commit: the record it holds now (a contact burst's arrival is shortened here) and what it decided.
|
|
private void Committed(ShotOutcome outcome)
|
|
{
|
|
if (_zone.TryGetShot(outcome.ShotId, out var record)) _shot = record;
|
|
_known = outcome;
|
|
}
|
|
|
|
// The simulation's verdict. A round that hit or burst is drawn at its end and stops; a miss keeps flying.
|
|
private void Resolve(ShotOutcome outcome)
|
|
{
|
|
if (!_alive) return;
|
|
_known = outcome;
|
|
if (outcome.Result == ShotResult.Miss) return;
|
|
Draw();
|
|
if (HitEffect != null && ActionGameManager.DrawsAt(outcome.Target)) HitEffect.Instantiate<Transform>().position = transform.position;
|
|
Finish();
|
|
}
|
|
|
|
private static Vector3 V(CultMath.float3 p) => new Vector3(p.x, p.y, p.z);
|
|
|
|
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();
|
|
}
|
|
}
|