using UnityEngine; using OctoberStudio.VAT; namespace OctoberStudio { /// /// 把 VAT prefab 挂到原 EnemyBehavior 上,作为视觉替换层。 /// - 隐藏原 SpriteRenderer / Animator(留 Shadow) /// - Spawn VAT view 作为子物体 /// - 朝向: 把 EnemyBehavior.ExternalFacingOverride 设 true,EnemyBehavior 不再写 transform.localScale.x, /// sign 通过 OnFacingSignChanged event 推给本组件 → 调 VATAnimationController.SetFacing。 /// 一律走 Y 旋转,完全不依赖 localScale。 /// [DisallowMultipleComponent] public class EnemyVATAttachment : MonoBehaviour { [Header("VAT Source")] [Tooltip("烤好的 VAT prefab (e.g. mon1001_VAT.prefab)")] [SerializeField] private GameObject vatPrefab; [Header("View Transform")] [SerializeField] private Vector3 localOffset = new Vector3(0f, 0.3f, 0f); [SerializeField] private Vector3 localScale = new Vector3(0.5f, 0.5f, 0.5f); [Header("Hide Originals")] [Tooltip("不禁用名字含此关键字的 SpriteRenderer (默认 Shadow 阴影保留)")] [SerializeField] private string keepNameContains = "Shadow"; private GameObject _spawnedView; private VATAnimationController _vatCtrl; private EnemyBehavior _enemy; private void Awake() { _enemy = GetComponent(); HideOriginalSprites(); SpawnVATView(); if (_spawnedView != null) { _vatCtrl = _spawnedView.GetComponent(); } // 接管 EnemyBehavior 朝向:不再写 transform.localScale.x,sign 走 event if (_enemy != null) { _enemy.ExternalFacingOverride = true; _enemy.OnFacingSignChanged += HandleEnemyFacingChanged; } // 出生时同步一次初始朝向(VATAnimationController.OnEnable 默认 SetFacing(+1) 已先跑过, // 这里覆盖一次以应对对象池复用或 prefab 上 root.scale.x 非常规情况) if (_vatCtrl != null) _vatCtrl.SetFacing(1); } private void OnDestroy() { if (_enemy != null) _enemy.OnFacingSignChanged -= HandleEnemyFacingChanged; } private void HandleEnemyFacingChanged(int sign) { if (_vatCtrl != null) _vatCtrl.SetFacing(sign); } private void HideOriginalSprites() { var all = GetComponentsInChildren(true); foreach (var sr in all) { if (!string.IsNullOrEmpty(keepNameContains) && sr.gameObject.name.IndexOf(keepNameContains, System.StringComparison.OrdinalIgnoreCase) >= 0) continue; sr.enabled = false; } // 子物体上的 Animator 也禁用(主 Sprite 那个,Animator 切 Sprite 已无用,省 CPU) var animators = GetComponentsInChildren(true); foreach (var an in animators) { if (!string.IsNullOrEmpty(keepNameContains) && an.gameObject.name.IndexOf(keepNameContains, System.StringComparison.OrdinalIgnoreCase) >= 0) continue; an.enabled = false; } } private void SpawnVATView() { if (vatPrefab == null) return; _spawnedView = Instantiate(vatPrefab, transform); _spawnedView.name = "_VAT_View"; _spawnedView.transform.localPosition = localOffset; _spawnedView.transform.localScale = localScale; // 注意: 不在这里写 localRotation = identity! // VATAnimationController.OnEnable 已在 Instantiate 中调过 SetFacing(),把 Y 设成了 ±facing 值, // 这里 reset 会把它抹成 0,导致 LateUpdate 第一次跑之前 view 朝向是错的。 // VAT prefab 的 root rotation 本身就是 identity,无需重置。 } #if UNITY_EDITOR // 仅给 Editor 工具读 public GameObject VATPrefab { get => vatPrefab; set => vatPrefab = value; } public Vector3 LocalScale { get => localScale; set => localScale = value; } public Vector3 LocalOffset { get => localOffset; set => localOffset = value; } #endif } }