feat(Animation): 添加技能动画状态和过渡

- 在base人类型士控制器中添加skill动画状态
- 添加从默认状态到技能状态的过渡条件
- 配置技能动画的状态转换参数
- 在蓝色怪物override controller中添加技能动画覆盖

fix(Model): 修复模型导入设置和骨骼位置

- 将人类型士Idle模型材质导入模式从2改为1
- 调整蓝色怪物怒吼动画的最后一帧从85到65
- 修正模型骨骼位置数据,统一缩放比例
- 更新蓝色怪物模型的全局缩放从0.023到2.3

docs(Codely): 删除剪贴板图片文件
```
This commit is contained in:
huadexuan committed 2026-08-30 23:20:49 +08:00
1 parent 2728c1d2cc
commit 21aceb6c04
44 files changed
+23820 -173

No files matched your search

+91 -7
View File
@@ -85,6 +85,11 @@ public class UnitBase : MonoBehaviour
//当前攻击开始时间,用于在没有结束 Event 时释放攻击状态。
private float attackStartTime;
private bool isPlayingSkill;
private float attackSpeedMultiplier = 1f;
private UnitSkill unitSkill;
private System.Action skillFinishedCallback;
//状态枚举
public enum State
@@ -96,7 +101,10 @@ public class UnitBase : MonoBehaviour
Track,
//攻击状态
Attack
Attack,
//技能状态
Skill
}
@@ -111,6 +119,7 @@ public class UnitBase : MonoBehaviour
animator = GetComponent<Animator>();
controller = GetComponent<CharacterController>();
worldHealthBar = GetComponent<WorldHealthBar>();
unitSkill = GetComponent<UnitSkill>();
// 位移和朝向由 CharacterController 与 UnitBase 控制,避免攻击动画的根运动叠加旋转。
animator.applyRootMotion = false;
@@ -133,6 +142,14 @@ public class UnitBase : MonoBehaviour
return;
}
if (isPlayingSkill)
{
animator.SetInteger("speed", 0);
applyGravity();
CheckSkillAnimationFinished();
return;
}
//查找场景中距离自己最近的敌对目标
target = FindNearestOpponent();
if (target != null)
@@ -158,6 +175,12 @@ public class UnitBase : MonoBehaviour
applyGravity();
}
public GameObject Target => target;
public string OpponentTag => GetOpponentTag();
public float AttackSpeedMultiplier => attackSpeedMultiplier;
//根据自身Tag找到敌对Tag
private string GetOpponentTag()
{
@@ -287,11 +310,11 @@ public class UnitBase : MonoBehaviour
float animationDuration = GetAttackAnimationDuration();
if (animationDuration <= 0f || attackDuration <= 0f)
{
animator.speed = 1f;
animator.speed = attackSpeedMultiplier;
return;
}
animator.speed = animationDuration / attackDuration;
animator.speed = animationDuration / attackDuration * attackSpeedMultiplier;
}
//攻击
@@ -365,6 +388,25 @@ public class UnitBase : MonoBehaviour
}
}
public void FireSkillProjectile(GameObject opponent)
{
if (opponent == null || projectilePrefab == null || projectileSpawnPoint == null)
{
return;
}
GameObject projectileObject = Instantiate(
projectilePrefab,
projectileSpawnPoint.position,
projectileSpawnPoint.rotation);
Projectile projectile = projectileObject.GetComponent<Projectile>();
if (projectile != null)
{
// 技能箭不再次积攒能量,避免技能触发递归。
projectile.Initialize(opponent, attackPower, gameObject, projectileSpeed, projectileLifetime, false);
}
}
// 普通攻击命中后积攒能量,满能量时清空能量并调用技能占位方法。
private void AddAttackEnergy()
{
@@ -382,9 +424,51 @@ public class UnitBase : MonoBehaviour
ReleaseSkill();
}
// 技能占位方法,后续由具体单位脚本重写。
protected virtual void ReleaseSkill()
private void ReleaseSkill()
{
if (unitSkill != null)
{
unitSkill.ReleaseSkill();
}
}
public void PlaySkillAnimation(System.Action onFinished)
{
if (isPlayingSkill)
{
return;
}
skillFinishedCallback = onFinished;
isPlayingSkill = true;
state = State.Skill;
animator.speed = 1f;
animator.SetInteger("speed", 0);
animator.SetTrigger("skill");
attackStartTime = Time.time;
}
public void MultiplyAttackSpeed(float multiplier, float limit)
{
attackSpeedMultiplier = Mathf.Min(
attackSpeedMultiplier * multiplier,
limit);
animator.speed = attackSpeedMultiplier;
}
private void CheckSkillAnimationFinished()
{
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
if (!stateInfo.IsName("skill") || stateInfo.normalizedTime < 1f)
{
return;
}
isPlayingSkill = false;
state = State.Alert;
animator.speed = attackSpeedMultiplier;
skillFinishedCallback?.Invoke();
skillFinishedCallback = null;
}
@@ -429,8 +513,8 @@ public class UnitBase : MonoBehaviour
private void FinishAttack()
{
//攻击动画结束后恢复Animator速度。
animator.speed = 1f;
//攻击动画结束后恢复当前攻速倍率。
animator.speed = attackSpeedMultiplier;
//攻击动画结束后,目标仍在攻击范围内则立即进行下一次攻击。
target = FindNearestOpponent();