RoguelikeKit/Assets/Common/Scripts/Enemies/EnemyVATAttachment.cs

122 lines
4.5 KiB
C#

using UnityEngine;
using OctoberStudio.VAT;
namespace OctoberStudio
{
/// <summary>
/// 把 VAT prefab 挂到原 EnemyBehavior 上,作为视觉替换层。
/// - 隐藏原 SpriteRenderer / Animator(留 Shadow)
/// - Spawn VAT view 作为子物体
/// - 朝向: 把 EnemyBehavior.ExternalFacingOverride 设 true,EnemyBehavior 不再写 transform.localScale.x,
/// sign 通过 OnFacingSignChanged event 推给本组件 → 调 VATAnimationController.SetFacing。
/// 一律走 Y 旋转,完全不依赖 localScale。
/// </summary>
[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<EnemyBehavior>();
HideOriginalSprites();
SpawnVATView();
if (_spawnedView != null)
{
_vatCtrl = _spawnedView.GetComponent<VATAnimationController>();
}
// 接管 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<SpriteRenderer>(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<Animator>(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
}
}