Files
Continentis/Assets/Scripts/MainGame/Character/CharacterView/StaticSpriteAnimator.cs
SoulliesOfficial c3b1561375 更新
2026-04-01 12:23:27 -04:00

177 lines
6.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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();
}
}
}