using OctoberStudio.Easing;
using OctoberStudio.Rendering;
using OctoberStudio.VAT;
using UnityEngine;
using UnityEngine.Events;
namespace OctoberStudio
{
public class CharacterBehavior : MonoBehaviour, ICharacterBehavior
{
protected static readonly int DEFEAT_TRIGGER = Animator.StringToHash("Defeat");
protected static readonly int REVIVE_TRIGGER = Animator.StringToHash("Revive");
protected static readonly int SPEED_FLOAT = Animator.StringToHash("Speed");
protected static readonly int _Overlay = Shader.PropertyToID("_Overlay");
// 把"角色逻辑事件"翻译成"VAT clip 名"。3D 模式下挂 controller 即可,2D 模式留空不影响。
protected const string VAT_CLIP_IDLE = "Idle";
protected const string VAT_CLIP_RUN = "Run";
protected const string VAT_CLIP_DIE = "Die";
protected const float VAT_SPEED_THRESHOLD = 0.01f;
[Tooltip("可选:挂载 RendererAdapter 切换 2D/3D 渲染。未挂时回退到下方 SpriteRenderer 字段。")]
[SerializeField] protected RendererAdapter rendererAdapter;
[SerializeField] protected SpriteRenderer playerSpriteRenderer;
[SerializeField] protected Animator animator;
[Tooltip("可选:3D VAT 模式下使用。SetSpeed/PlayDefeatAnimation/PlayReviveAnimation 会同步切换 VAT clip。")]
[SerializeField] protected VATAnimationController vatAnimationController;
[SerializeField] protected Color hitColor;
[SerializeField] protected Transform centerTransform;
public Transform CenterTransform => centerTransform;
public Transform Transform => transform;
protected IEasingCoroutine damageCoroutine;
///
/// 优先用 RendererAdapter 的 Material,否则回退到 SpriteRenderer.material。
///
protected virtual Material HitMaterial
{
get => rendererAdapter != null ? rendererAdapter.Material : playerSpriteRenderer.material;
set
{
if (rendererAdapter != null) rendererAdapter.Material = value;
else playerSpriteRenderer.material = value;
}
}
protected virtual void Reset()
{
if (rendererAdapter == null) rendererAdapter = GetComponent();
if (vatAnimationController == null) vatAnimationController = GetComponentInChildren(true);
}
protected virtual void Awake()
{
// 编辑器没拖时,运行时再尝试自动找(支持 nested prefab 中后挂上来的情况)
if (vatAnimationController == null) vatAnimationController = GetComponentInChildren(true);
}
public virtual void SetSpeed(float speed)
{
if (animator != null) animator.SetFloat(SPEED_FLOAT, speed);
if (vatAnimationController != null)
{
vatAnimationController.Play(speed > VAT_SPEED_THRESHOLD ? VAT_CLIP_RUN : VAT_CLIP_IDLE);
}
}
public virtual void SetLocalScale(Vector3 scale)
{
// VAT 3D 模式: PlayerBehavior 传过来的 scale.x=±1 是 2D 翻转语义,
// 3D 模型不能用 scale.x=-1 (会翻转法线 / culling),改用 VATAnimationController.SetFacing 旋转
if (vatAnimationController != null)
{
vatAnimationController.SetFacing(scale.x >= 0 ? 1 : -1);
return;
}
transform.localScale = scale;
}
public virtual void PlayReviveAnimation()
{
if (animator != null) animator.SetTrigger(REVIVE_TRIGGER);
if (vatAnimationController != null) vatAnimationController.Play(VAT_CLIP_IDLE);
}
public virtual void PlayDefeatAnimation()
{
if (animator != null) animator.SetTrigger(DEFEAT_TRIGGER);
if (vatAnimationController != null) vatAnimationController.Play(VAT_CLIP_DIE, loop: false);
}
public virtual void SetSortingOrder(int order)
{
if (rendererAdapter != null) rendererAdapter.SortingOrder = order;
else if (playerSpriteRenderer != null) playerSpriteRenderer.sortingOrder = order;
}
public virtual void FlashHit(UnityAction onFinish = null)
{
if (damageCoroutine.ExistsAndActive()) return;
var transparentColor = hitColor;
transparentColor.a = 0;
var mat = HitMaterial;
if (mat == null || !mat.HasProperty(_Overlay))
{
onFinish?.Invoke();
return;
}
mat.SetColor(_Overlay, transparentColor);
damageCoroutine = mat.DoColor(_Overlay, hitColor, 0.05f).SetOnFinish(() =>
{
damageCoroutine = mat.DoColor(_Overlay, transparentColor, 0.05f).SetOnFinish(onFinish);
});
}
}
}