using ECSAnimator.Runtime.Product.Data; using ECSAnimator.Runtime.Product.ECS; using Unity.Mathematics; using UnityEngine; namespace ECSAnimator.Runtime.Product.Samples.Newcomer { /// Sample bridge from a committed ECS attachment pose to this GameObject hierarchy. public sealed class CharacterAttachmentFollower : MonoBehaviour { [SerializeField] private string _attachmentKey = "attachment.weapon"; [SerializeField] private Vector3 _localPosition; [SerializeField] private Vector3 _localEulerAngles; private CharacterRuntime _runtime; private CharacterInstanceHandle _instance; private ResolvedAttachmentHandle _attachment; private float4x4 _localOffset; public bool TryFollow(CharacterRuntime runtime, CharacterPreparedAsset prepared, CharacterSpawnResult spawn) => TryFollow(runtime, prepared, spawn.SpawnId); public void StopFollowing() { _runtime = null; _instance = default; _attachment = default; } private bool TryFollow(CharacterRuntime runtime, CharacterPreparedAsset prepared, CharacterInstanceHandle instance) { StopFollowing(); if (runtime == null || prepared == null || !instance.IsValid || !runtime.TryResolveAttachment(prepared.Bindings, _attachmentKey, out var attachment)) return false; _runtime = runtime; _instance = instance; _attachment = attachment; _localOffset = float4x4.TRS(_localPosition, quaternion.EulerXYZ(math.radians((float3)_localEulerAngles)), new float3(1f)); return true; } private void LateUpdate() { if (_runtime == null || !_runtime.TryGetAttachmentPose(_instance, _attachment, _localOffset, out var pose)) return; transform.SetPositionAndRotation(pose.LocalToWorld.c3.xyz, quaternion.LookRotationSafe(pose.LocalToWorld.c2.xyz, pose.LocalToWorld.c1.xyz)); } } }