using System;
using System.Collections.Generic;
using DG.Tweening;
using SLSFramework.General;
using UnityEngine;
namespace Continentis.MainGame.Character
{
///
/// Tier 1 动画驱动器:静态图片切换 + DOTween 位移。
/// 不使用 AnimationClip,通过切换 SpriteRenderer.sprite 表现不同动作。
/// 适合尚无帧动画资源的角色,或仅有立绘/静态图片的简易角色。
///
public class StaticSpriteAnimator : MonoBehaviour, ICharacterAnimator
{
[Header("Sprite 映射")]
[Tooltip("动作名称 → Sprite 映射表,Key 需与 CharacterData.animations 的 Key 对齐(如 Idle、Attack、Hit)")]
public SerializableDictionary actionSprites;
[Header("DOTween 参数")]
[Tooltip("攻击时前冲距离")]
[SerializeField] private float attackLungeDistance = 0.5f;
[Tooltip("攻击前冲时长")]
[SerializeField] private float attackLungeDuration = 0.15f;
[Tooltip("受击抖动强度")]
[SerializeField] private float hitShakeStrength = 0.3f;
[Tooltip("受击抖动时长")]
[SerializeField] private float hitShakeDuration = 0.2f;
private SpriteRenderer _mainSpriteRenderer;
private Sprite _idleSprite;
private Vector3 _originalLocalPosition;
private Tween _currentTween;
// ── ICharacterAnimator ──────────────────────────────────────────────
///
/// 初始化:缓存主 SpriteRenderer 和 Idle Sprite。
///
public void InitializeAnimator(CombatCharacterViewBase view)
{
_mainSpriteRenderer = view.mainView.GetComponent();
if (_mainSpriteRenderer == null)
_mainSpriteRenderer = view.mainView.GetComponentInChildren();
_originalLocalPosition = view.mainView.transform.localPosition;
// 尝试从映射表获取 Idle sprite,回退为当前显示的 sprite
_idleSprite = actionSprites != null && actionSprites.TryGetValue("Idle", out Sprite idle)
? idle
: _mainSpriteRenderer != null ? _mainSpriteRenderer.sprite : null;
// 应用 Idle Sprite
if (_mainSpriteRenderer != null && _idleSprite != null)
_mainSpriteRenderer.sprite = _idleSprite;
}
///
/// 播放动作:切换 Sprite + DOTween 动效,完毕后自动回 Idle。
///
public void PlayAction(string actionName, float speed = 1f, Action onComplete = null)
{
KillCurrentTween();
// 切换 Sprite
if (_mainSpriteRenderer != null && actionSprites != null &&
actionSprites.TryGetValue(actionName, out Sprite sprite))
{
_mainSpriteRenderer.sprite = sprite;
}
// 根据动作类型执行 DOTween 动效
float adjustedDuration = 1f / Mathf.Max(speed, 0.01f);
Transform mainTransform = _mainSpriteRenderer != null
? _mainSpriteRenderer.transform
: transform;
switch (actionName)
{
case "Attack":
case "Skill":
PlayLungeAnimation(mainTransform, adjustedDuration, onComplete);
break;
case "Hit":
PlayShakeAnimation(mainTransform, adjustedDuration, onComplete);
break;
default:
// 无特殊动效,短暂延迟后回 Idle
float holdDuration = DefaultActionHoldDuration * adjustedDuration;
_currentTween = DOVirtual.DelayedCall(holdDuration, () =>
{
ReturnToIdle();
onComplete?.Invoke();
});
break;
}
}
///
/// 立即切回 Idle Sprite 并复位位置。
///
public void ReturnToIdle()
{
KillCurrentTween();
if (_mainSpriteRenderer != null && _idleSprite != null)
_mainSpriteRenderer.sprite = _idleSprite;
if (_mainSpriteRenderer != null)
_mainSpriteRenderer.transform.localPosition = _originalLocalPosition;
}
///
/// 暂停或恢复当前 Tween。
///
public void SetPause(bool isPaused)
{
if (_currentTween != null && _currentTween.IsActive())
{
if (isPaused) _currentTween.Pause();
else _currentTween.Play();
}
}
// ── 内部动效 ────────────────────────────────────────────────────────
private const float DefaultActionHoldDuration = 0.3f;
private void PlayLungeAnimation(Transform target, float durationScale, Action onComplete)
{
float duration = attackLungeDuration * durationScale;
Vector3 lungeOffset = target.right * attackLungeDistance;
_currentTween = DOTween.Sequence()
.Append(target.DOLocalMove(_originalLocalPosition + lungeOffset, duration).SetEase(Ease.OutQuad))
.Append(target.DOLocalMove(_originalLocalPosition, duration).SetEase(Ease.InQuad))
.OnComplete(() =>
{
ReturnToIdle();
onComplete?.Invoke();
});
}
private void PlayShakeAnimation(Transform target, float durationScale, Action onComplete)
{
float duration = hitShakeDuration * durationScale;
_currentTween = target.DOShakePosition(duration, hitShakeStrength, vibrato: 10, randomness: 90, fadeOut: true)
.OnComplete(() =>
{
target.localPosition = _originalLocalPosition;
ReturnToIdle();
onComplete?.Invoke();
});
}
private void KillCurrentTween()
{
if (_currentTween != null && _currentTween.IsActive())
{
_currentTween.Kill();
_currentTween = null;
}
}
private void OnDestroy()
{
KillCurrentTween();
}
}
}