IGrabPresenter.Grab(GrabEvent) only; the binder's Transform and velocity resolvers go. TractorBeam presents a running grab (aims at Started's TargetPosition, emits at Power, dark otherwise). FieldDriver draws its tendril from GrabEvents alone with a real-time phase clock over each event's PhaseDuration and writes no transform and destroys nothing; its grab times move to FieldTester, now the scene's stand-in sim that owns its pickup's timeline and pose. ItemPickup keeps its label lit while held. ImpactPresenterSmoke becomes CapabilityPresentationSmoke with a grab section; its two untracked-ship expectations follow DrawsAt since contact-hud. Builds (Unity 6000.3.24f1 batchmode: smoke ok, scene opens, no missing script). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
2.4 KiB
C#
68 lines
2.4 KiB
C#
using System;
|
|
using TMPro;
|
|
using CultMath;
|
|
using CultMath.UnityBridge;
|
|
using UnityEngine;
|
|
using static CultMath.math;
|
|
using Random = UnityEngine.Random;
|
|
|
|
// How one floating item looks. Zone owns the item's every fact (existence, position, expiry, pickup); once bound,
|
|
// this reads the latest sim body and draws, spun for show, and writes nothing back (rulings unity-presents-only-r3
|
|
// and sim-speed-presentation). An unbound pickup (FieldTester's test scene) leaves its transform alone.
|
|
public class ItemPickup : MonoBehaviour
|
|
{
|
|
public float RotationSpeed = 1f;
|
|
public float GridOffset;
|
|
public float LabelFadeDuration = .5f;
|
|
public float LabelPersistDuration = 3;
|
|
public float LabelDisplayAngle = 15;
|
|
public float LabelDisplayMaxDistance = 500;
|
|
public Transform ScanLabelContainer;
|
|
public TextMeshPro ScanLabel;
|
|
|
|
public ItemInstance Item { get; set; }
|
|
public float3 ViewOrigin { get; set; }
|
|
public float3 ViewDirection { get; set; }
|
|
|
|
private float _displayTime;
|
|
private float _timeOffset;
|
|
private Zone _zone;
|
|
private FloatingItem _floating;
|
|
|
|
public void Bind(Zone zone, FloatingItem floating)
|
|
{
|
|
_zone = zone;
|
|
_floating = floating;
|
|
Item = floating.Item;
|
|
_timeOffset = Random.value * 100;
|
|
Place();
|
|
}
|
|
|
|
private void Place()
|
|
{
|
|
var p = _floating.Body.Position;
|
|
transform.position = new Vector3(p.x, _zone.GetHeight(p) + GridOffset, p.y);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (_floating != null)
|
|
{
|
|
Place();
|
|
transform.localRotation = Quaternion.Euler(
|
|
sin(Time.time - _timeOffset * RotationSpeed) * 90, 0, cos(Time.time - _timeOffset * RotationSpeed) * 90);
|
|
}
|
|
|
|
var diff = transform.position.ToCultMath() - ViewOrigin;
|
|
var toThis = normalize(diff);
|
|
var viewAngle = acos(dot(toThis, ViewDirection)) * Mathf.Rad2Deg;
|
|
if (length(diff) < LabelDisplayMaxDistance && viewAngle < LabelDisplayAngle)
|
|
_displayTime = Time.time;
|
|
if (_floating?.Holder != null) _displayTime = Time.time;
|
|
var targetAlpha = Time.time - _displayTime < LabelPersistDuration ? 1 : 0;
|
|
var c = ScanLabel.color;
|
|
c.a = c.a + sign(targetAlpha - c.a) * (Time.deltaTime / LabelFadeDuration);
|
|
ScanLabel.color = c;
|
|
ScanLabelContainer.rotation = Quaternion.LookRotation((-toThis).ToUnity());
|
|
}
|
|
}
|