Floater fixtures: the grab-point test and the wiring verify run over a moving well
PickupTests builds the zone over a sun and a planet on a rail, asserts the field differs between the ship's point and the item's and between start and end of the run, and checks each grab event at the zone time it was published. DrawnPoseWiringVerify gets a sun and a planet near the pickup and mine, requires the field under each to move with the drawn time, and checks the pickup's y as well as the mine's. Not compiled or run yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
0c8b15f6c1
commit
c631e09fe8
2 changed files with 65 additions and 7 deletions
|
|
@ -116,7 +116,23 @@ namespace Aetheria.EditorTools
|
|||
cache.FlushAsync().Wait();
|
||||
var ledger = new ProvenanceLedger();
|
||||
var items = new ItemManager(cache, ledger, settings, _ => { });
|
||||
var zone = new Zone(items, new PlanetSettings(), new ZonePack(), null, null);
|
||||
|
||||
// A sun and a planet on a moving rail round it, both near the bodies below: the field under a floater changes with the
|
||||
// drawn time, so a presenter that samples it at the step's time, drops it, or reads lead 0 for the height draws a different y.
|
||||
var sunOrbit = cache.Upsert(new OrbitData { FixedPosition = new float2(-60, -40) }).Key;
|
||||
var planetOrbit = cache.Upsert(new OrbitData { Parent = new CultRecordRef<OrbitData>(sunOrbit), Distance = 40f, Phase = .3f }).Key;
|
||||
var pack = new ZonePack { Radius = 5000f, Mass = 10000f };
|
||||
pack.Orbits.Add(new CultRecordRef<OrbitData>(sunOrbit));
|
||||
pack.Orbits.Add(new CultRecordRef<OrbitData>(planetOrbit));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(cache.Upsert(new PlanetData { Name = "Wiring Sun", Orbit = new CultRecordRef<OrbitData>(sunOrbit), Mass = 400f }).Key));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(cache.Upsert(new PlanetData { Name = "Wiring Planet", Orbit = new CultRecordRef<OrbitData>(planetOrbit), Mass = 100f }).Key));
|
||||
var curves = new PlanetSettings
|
||||
{
|
||||
BodyRadius = new ExponentialCurve(),
|
||||
WellStrength = new ExponentialCurve { Multiplier = 1f, Exponent = 1f },
|
||||
RocheLimit = new ExponentialCurve { Constant = 2f }
|
||||
};
|
||||
var zone = new Zone(items, curves, pack, null, null);
|
||||
|
||||
var rootObject = new GameObject("Wiring Zone Root");
|
||||
var rendererObject = new GameObject("Wiring Zone Renderer");
|
||||
|
|
@ -162,6 +178,12 @@ namespace Aetheria.EditorTools
|
|||
var mineVelocity = mine.Body.TotalVelocity;
|
||||
Require(Mathf.Abs(pickupVelocity.x) > 1f && Mathf.Abs(mineVelocity.y) > 1f, "The fixture's bodies stopped: a lead would not move them.");
|
||||
|
||||
// The field under each floater really is a function of the drawn time here, by more than the tolerance at the largest lead.
|
||||
const float LargestLead = .015f;
|
||||
foreach (var at in new[] { floating.Body.Position + pickupVelocity * LargestLead, mine.Body.Position + mineVelocity * LargestLead })
|
||||
Require(Mathf.Abs(DrawAhead.Height(zone, at, LargestLead) - zone.GetHeight(at, zone.ExactTime)) > 5 * Tolerance,
|
||||
"The fixture's field is flat under a floater: a drawn time would not move its height.");
|
||||
|
||||
Vector2 baseline = default;
|
||||
foreach (var leadSeconds in new[] { 0f, .008f, .015f })
|
||||
{
|
||||
|
|
@ -176,6 +198,9 @@ namespace Aetheria.EditorTools
|
|||
Require(Mathf.Abs(seen.x - want.x) < Tolerance && Mathf.Abs(seen.z - want.y) < Tolerance,
|
||||
$"ItemPickup at lead {lead}: drawn x/z {seen.x},{seen.z}, the step plus velocity times lead {want.x},{want.y}.");
|
||||
|
||||
Require(Mathf.Abs(seen.y - DrawAhead.FloaterHeight(zone, settings, new float2(want.x, want.y), lead)) < Tolerance,
|
||||
$"ItemPickup y {seen.y} is not on the floater layer.");
|
||||
|
||||
seen = mineInstance.transform.position;
|
||||
want = mine.Body.Position + mineVelocity * lead;
|
||||
Require(Mathf.Abs(seen.x - want.x) < Tolerance && Mathf.Abs(seen.z - want.y) < Tolerance,
|
||||
|
|
|
|||
|
|
@ -64,7 +64,9 @@ public sealed class PickupTests : IDisposable
|
|||
};
|
||||
}
|
||||
|
||||
private Lab Build(PickupData beam, float lifetime = 30f)
|
||||
// With `wells`, the zone has a sun (a parentless rail off the ship's side, strength 400) and a planet on a moving rail round
|
||||
// it (strength 25), the field a floater layer is read against; without, the zone is flat and nothing pulls on anything.
|
||||
private Lab Build(PickupData beam, float lifetime = 30f, bool wells = false)
|
||||
{
|
||||
var settings = new GameplaySettings
|
||||
{
|
||||
|
|
@ -104,7 +106,26 @@ public sealed class PickupTests : IDisposable
|
|||
var ledger = new ProvenanceLedger();
|
||||
for (var i = 1; i <= 9000; i++) ledger.Lots[i] = new Lot { Origin = new Attributed(), Quality = 1 };
|
||||
var items = new ItemManager(cache, ledger, settings, _ => { });
|
||||
var zone = new Zone(items, new PlanetSettings(), new ZonePack { Radius = 0 }, new GalaxyZone { Name = "Pickup", Owner = null }, null);
|
||||
var pack = new ZonePack { Radius = 0 };
|
||||
var curves = new PlanetSettings();
|
||||
if (wells)
|
||||
{
|
||||
curves = new PlanetSettings
|
||||
{
|
||||
BodyRadius = new ExponentialCurve(),
|
||||
WellStrength = new ExponentialCurve { Multiplier = 1f, Exponent = 1f },
|
||||
RocheLimit = new ExponentialCurve { Constant = 2f }
|
||||
};
|
||||
var sunOrbit = cache.Upsert(new OrbitData { FixedPosition = float2(-50, 0) }).Key;
|
||||
var planetOrbit = cache.Upsert(new OrbitData { Parent = new CultRecordRef<OrbitData>(sunOrbit), Distance = 60f, Phase = .3f }).Key;
|
||||
pack.Radius = 5000f;
|
||||
pack.Mass = 10000f;
|
||||
pack.Orbits.Add(new CultRecordRef<OrbitData>(sunOrbit));
|
||||
pack.Orbits.Add(new CultRecordRef<OrbitData>(planetOrbit));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(cache.Upsert(new PlanetData { Name = "Sun", Orbit = new CultRecordRef<OrbitData>(sunOrbit), Mass = 400f }).Key));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(cache.Upsert(new PlanetData { Name = "Planet", Orbit = new CultRecordRef<OrbitData>(planetOrbit), Mass = 25f }).Key));
|
||||
}
|
||||
var zone = new Zone(items, curves, pack, new GalaxyZone { Name = "Pickup", Owner = null }, null);
|
||||
return new Lab { Items = items, Zone = zone };
|
||||
}
|
||||
|
||||
|
|
@ -251,14 +272,26 @@ public sealed class PickupTests : IDisposable
|
|||
public void AGrabPointRidesTheFloaterLayerNotTheShips()
|
||||
{
|
||||
using var bag = new CompositeDisposable();
|
||||
var lab = Build(Beam());
|
||||
var lab = Build(Beam(), wells: true);
|
||||
var ship = Spawn(lab, float3(0, 0, 0));
|
||||
var item = Drift(lab, 40, 0);
|
||||
var events = Watch(ship, bag);
|
||||
|
||||
// The fixture has a field to ride: it differs between the ship's point and the item's, and between the start and
|
||||
// the end of the run, so a grab point read at the ship's xz or at the wrong time cannot agree with it.
|
||||
var atShip = lab.Zone.GetHeight(float2(0, 0), lab.Zone.ExactTime);
|
||||
var atItem = lab.Zone.GetHeight(float2(40, 0), lab.Zone.ExactTime);
|
||||
Assert.True(abs(atItem - atShip) > 1f, $"the field is flat under the grab: ship {atShip}, item {atItem}");
|
||||
var start = lab.Zone.ExactTime;
|
||||
|
||||
// Each event is checked at the zone time it was published, where the sim read the field.
|
||||
var seen = new List<(GrabEvent Event, double Time)>();
|
||||
ship.Capabilities.Grab.Subscribe(e => seen.Add((e, lab.Zone.ExactTime))).AddTo(bag);
|
||||
ship.TractorCommand = 1;
|
||||
for (var tick = 1; tick <= 150; tick++) lab.Zone.Update(1 / 60f);
|
||||
Assert.NotEmpty(events);
|
||||
Assert.All(events, e => Assert.Equal(lab.Zone.GetHeight(e.TargetPosition.xz, lab.Zone.ExactTime) - 1.5f, e.TargetPosition.y, 3));
|
||||
Assert.NotEmpty(seen);
|
||||
Assert.True(abs(lab.Zone.GetHeight(float2(40, 0), lab.Zone.ExactTime) - lab.Zone.GetHeight(float2(40, 0), start)) > .01f, "the wells did not move");
|
||||
Assert.All(seen, s => Assert.Equal(lab.Zone.GetHeight(s.Event.TargetPosition.xz, s.Time) - 1.5f, s.Event.TargetPosition.y, 3));
|
||||
Assert.All(seen, s => Assert.True(abs(s.Event.TargetPosition.y - (-1.5f)) > 1f, "the grab point sits on the bare layer, the field dropped"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Reference in a new issue