- Carryable: generic pickup/carry component, cosmetic per-peer follow of the carrier's HandSocket instead of NGO reparenting (camera pitch isn't networked, so root-level reparenting wouldn't tilt with the view) - PlayerCarry: mutual exclusion with PlayerHands, server-timed hold-to-pour tick (client only signals start/stop, never per-unit requests) - IPourSource/IPourTarget: decouple PlayerInteractor from concrete Shop types, same pattern as IInteractable - Lieferkarton: gains product data (productId/quantity), two-stage Interact() (open, then pick up), IPourSource - ShelfSlot: implements IPourTarget, fills the previous RefreshVisuals TODO - PackageDisplay: renders quantity as a proper column x row grid that stacks into further layers as space runs out, reused by both containers - FlyingPackage: small non-networked cosmetic visual that arcs a package from the carried box to its exact landing slot in the shelf grid on every successful pour tick, broadcast via Rpc(SendTo.Everyone) - StorageCrate: rejects pickup while the player is carrying a box Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
55 lines
1.9 KiB
C#
55 lines
1.9 KiB
C#
using UnityEngine;
|
||
|
||
namespace AfterHours.Shop
|
||
{
|
||
/// <summary>
|
||
/// Rein kosmetisches, nicht genetzwertes Flug-Visual für ein Stück Ware,
|
||
/// das vom Karton ins Regal wandert. Jeder Peer erzeugt seine eigene
|
||
/// Instanz (ausgelöst über PlayerCarry.PlayFlightRpc) – keine
|
||
/// Server-Autorität nötig, der eigentliche Bestand ist längst per
|
||
/// NetworkVariable synchron.
|
||
/// </summary>
|
||
public sealed class FlyingPackage : MonoBehaviour
|
||
{
|
||
private const float ArcHeight = 0.35f;
|
||
|
||
private Vector3 _start;
|
||
private Vector3 _end;
|
||
private Quaternion _startRotation;
|
||
private Quaternion _endRotation;
|
||
private float _duration;
|
||
private float _elapsed;
|
||
|
||
public static void Spawn(GameObject prefab, Vector3 start, Vector3 end, Quaternion endRotation, float duration)
|
||
{
|
||
if (prefab == null) return;
|
||
|
||
var instance = Instantiate(prefab, start, Quaternion.identity);
|
||
var flight = instance.AddComponent<FlyingPackage>();
|
||
flight.Init(start, end, endRotation, duration);
|
||
}
|
||
|
||
private void Init(Vector3 start, Vector3 end, Quaternion endRotation, float duration)
|
||
{
|
||
_start = start;
|
||
_end = end;
|
||
_startRotation = transform.rotation;
|
||
_endRotation = endRotation;
|
||
_duration = Mathf.Max(0.05f, duration);
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
_elapsed += Time.deltaTime;
|
||
float t = Mathf.Clamp01(_elapsed / _duration);
|
||
|
||
Vector3 flat = Vector3.Lerp(_start, _end, t);
|
||
float arc = ArcHeight * Mathf.Sin(t * Mathf.PI);
|
||
transform.position = flat + Vector3.up * arc;
|
||
transform.rotation = Quaternion.Slerp(_startRotation, _endRotation, t);
|
||
|
||
if (t >= 1f) Destroy(gameObject);
|
||
}
|
||
}
|
||
}
|