更新
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DG.Tweening;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Character
|
||||
{
|
||||
/// <summary>
|
||||
/// Tier 1 动画驱动器:静态图片切换 + DOTween 位移。
|
||||
/// 不使用 AnimationClip,通过切换 SpriteRenderer.sprite 表现不同动作。
|
||||
/// 适合尚无帧动画资源的角色,或仅有立绘/静态图片的简易角色。
|
||||
/// </summary>
|
||||
public class StaticSpriteAnimator : MonoBehaviour, ICharacterAnimator
|
||||
{
|
||||
[Header("Sprite 映射")]
|
||||
[Tooltip("动作名称 → Sprite 映射表,Key 需与 CharacterData.animations 的 Key 对齐(如 Idle、Attack、Hit)")]
|
||||
public SerializableDictionary<string, Sprite> 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 ──────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 初始化:缓存主 SpriteRenderer 和 Idle Sprite。
|
||||
/// </summary>
|
||||
public void InitializeAnimator(CombatCharacterViewBase view)
|
||||
{
|
||||
_mainSpriteRenderer = view.mainView.GetComponent<SpriteRenderer>();
|
||||
if (_mainSpriteRenderer == null)
|
||||
_mainSpriteRenderer = view.mainView.GetComponentInChildren<SpriteRenderer>();
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放动作:切换 Sprite + DOTween 动效,完毕后自动回 Idle。
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 立即切回 Idle Sprite 并复位位置。
|
||||
/// </summary>
|
||||
public void ReturnToIdle()
|
||||
{
|
||||
KillCurrentTween();
|
||||
|
||||
if (_mainSpriteRenderer != null && _idleSprite != null)
|
||||
_mainSpriteRenderer.sprite = _idleSprite;
|
||||
|
||||
if (_mainSpriteRenderer != null)
|
||||
_mainSpriteRenderer.transform.localPosition = _originalLocalPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 暂停或恢复当前 Tween。
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user