(client) feat:添加临时动画组件,添加逃跑逻辑
This commit is contained in:
@ -9,6 +9,7 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using Utils;
|
||||
|
||||
|
||||
namespace Entity
|
||||
@ -37,7 +38,8 @@ namespace Entity
|
||||
/// </summary>
|
||||
public GameObject weaponAnimator;
|
||||
|
||||
public ITick[] weaponAnimatorNodeList;
|
||||
public SpriteAnimator weaponItem;
|
||||
public SpriteAnimator weaponAttackAnimation;
|
||||
|
||||
/// <summary>
|
||||
/// 人工智能行为树,定义实体的行为逻辑。
|
||||
@ -49,15 +51,24 @@ namespace Entity
|
||||
/// </summary>
|
||||
public JobBase currentJob;
|
||||
|
||||
private Attributes _attribute;
|
||||
|
||||
/// <summary>
|
||||
/// 实体的属性定义,包括生命值、攻击力、防御力等。
|
||||
/// </summary>
|
||||
public virtual Attributes attributes { get; protected set; }
|
||||
public virtual Attributes attributes
|
||||
{
|
||||
get { return _attribute ??= new Attributes(baseAttributes); }
|
||||
protected set => _attribute = value;
|
||||
}
|
||||
|
||||
private Attributes _baseAttributes;
|
||||
public virtual Attributes baseAttributes
|
||||
{
|
||||
get { return _baseAttributes ??= new Attributes(entityDef.attributes); }
|
||||
get
|
||||
{
|
||||
return _baseAttributes ??= entityDef == null ? new Attributes() : new Attributes(entityDef.attributes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -79,11 +90,7 @@ namespace Entity
|
||||
/// 表示实体是否可以被选择。
|
||||
/// </summary>
|
||||
public bool canSelect = true;
|
||||
|
||||
/// <summary>
|
||||
/// 表示实体是否处于追逐状态(影响移动速度)。
|
||||
/// </summary>
|
||||
public bool IsChase { set; get; } = true;
|
||||
|
||||
|
||||
public string currentDimensionId = null;
|
||||
|
||||
@ -98,7 +105,6 @@ namespace Entity
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
IsChase = true;
|
||||
currentJob = null;
|
||||
// 逻辑修改:只有当存在一个不同的焦点实体时,才将其PlayerControlled设为false
|
||||
if (Program.Instance.FocusedEntity && Program.Instance.FocusedEntity != this)
|
||||
@ -146,16 +152,9 @@ namespace Entity
|
||||
public event Action<Entity> OnEntityDied;
|
||||
|
||||
private bool _warning = false;
|
||||
|
||||
/// <summary>
|
||||
/// 存储不同朝向下的动画节点集合。
|
||||
/// </summary>
|
||||
public Dictionary<EntityState, Dictionary<Orientation, ITick[]>> bodyAnimationNode = new();
|
||||
|
||||
private ITick[] _currentAnimatorCache;
|
||||
|
||||
|
||||
|
||||
private GameObject wearponAttackAnimationNodeRoot = null;
|
||||
private ITick[] wearponAttackAnimationNodeList;
|
||||
|
||||
/// <summary>
|
||||
/// 存储不同朝向下的身体节点对象。
|
||||
@ -199,22 +198,24 @@ namespace Entity
|
||||
|
||||
protected virtual void InitWeaponAnimator()
|
||||
{
|
||||
if (!weaponAnimator)
|
||||
return;
|
||||
for (var i = 0; i < weaponAnimator.transform.childCount; i++)
|
||||
if (weaponAnimator && weaponAnimator.transform.childCount > 0)
|
||||
{
|
||||
Destroy(weaponAnimator.transform.GetChild(i).gameObject);
|
||||
weaponAnimatorNodeList = null;
|
||||
foreach (GameObject child in weaponAnimator.transform)
|
||||
{
|
||||
Destroy(child);
|
||||
}
|
||||
}
|
||||
var weapon = GetCurrentWeapon();
|
||||
if (weapon == null)
|
||||
{
|
||||
weaponAnimator.SetActive(false);
|
||||
|
||||
var currentWeapon = GetCurrentWeapon();
|
||||
if (currentWeapon?.AttackAnimationDef == null)
|
||||
return;
|
||||
}
|
||||
weaponItem=GameObjectCreate.SpriteAnimator(currentWeapon.Icon.ToArray(), weaponAnimator.transform);
|
||||
weaponItem.SetFPS(currentWeapon.FPS);
|
||||
|
||||
var weaponAnimation = weapon.InstantiateAttackAnimation(weaponAnimator.transform);
|
||||
weaponAnimatorNodeList = weaponAnimation.animationComponents;
|
||||
weaponAttackAnimation = GameObjectCreate.SpriteAnimator(currentWeapon.AttackAnimationDef.ToArray(),
|
||||
weaponAnimator.transform);
|
||||
weaponAttackAnimation.SetFPS(currentWeapon.FPS);
|
||||
weaponAttackAnimation.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -230,30 +231,26 @@ namespace Entity
|
||||
foreach (var state in states)
|
||||
{
|
||||
bodyNodes.TryAdd(state, new Dictionary<Orientation, GameObject>());
|
||||
bodyAnimationNode.TryAdd(state, new Dictionary<Orientation, ITick[]>());
|
||||
}
|
||||
|
||||
// 主初始化逻辑
|
||||
foreach (var state in states)
|
||||
{
|
||||
var stateBodyNodes = bodyNodes[state];
|
||||
var stateAnimNodes = bodyAnimationNode[state];
|
||||
foreach (var orientation in orientations)
|
||||
{
|
||||
// 获取节点定义(避免重复调用)
|
||||
var nodeDef = drawingOrder.GetDrawNodeDef(state, orientation, out var original);
|
||||
GameObject targetObj = null;
|
||||
// --- 修改点一:处理空节点定义(增加默认精灵显示) ---
|
||||
GameObject targetObj;
|
||||
if (nodeDef == null)
|
||||
{
|
||||
if (imagePrefab != null && Managers.PackagesImageManager.Instance.defaultSprite != null)
|
||||
if (imagePrefab && Managers.PackagesImageManager.Instance.defaultSprite != null)
|
||||
{
|
||||
// 实例化imagePrefab作为默认占位符
|
||||
targetObj = Instantiate(imagePrefab.gameObject, body.transform);
|
||||
targetObj.name = $"{state}_{orientation}_Default";
|
||||
targetObj.transform.localPosition = Vector3.zero;
|
||||
var imagePrefabCom = targetObj.GetComponent<ImagePrefab>();
|
||||
if (imagePrefabCom != null)
|
||||
if (imagePrefabCom)
|
||||
{
|
||||
imagePrefabCom.SetSprite(Managers.PackagesImageManager.Instance.defaultSprite);
|
||||
}
|
||||
@ -261,14 +258,12 @@ namespace Entity
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"InitBody: 默认ImagePrefab中无法获取ImagePrefab组件!状态: {state}, 朝向: {orientation}");
|
||||
// 降级为普通GameObject
|
||||
targetObj = new GameObject { name = $"{state}_{orientation}_Empty" };
|
||||
targetObj.transform.SetParent(body.transform, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有imagePrefab或defaultSprite,则创建空GameObject
|
||||
targetObj = new GameObject { name = $"{state}_{orientation}_Empty" };
|
||||
targetObj.transform.SetParent(body.transform, false);
|
||||
}
|
||||
@ -282,29 +277,19 @@ namespace Entity
|
||||
}
|
||||
else
|
||||
{
|
||||
targetObj = InitBodyPart(nodeDef, body); // 创建新对象
|
||||
targetObj = GameObjectCreate.InitBodyPart(nodeDef, body); // 创建新对象
|
||||
}
|
||||
}
|
||||
|
||||
if (targetObj != null)
|
||||
if (targetObj)
|
||||
{
|
||||
stateBodyNodes[orientation] = targetObj;
|
||||
// 逻辑说明:确保 stateAnimNodes[orientation] 总是被初始化为一个列表
|
||||
var animatorsForOrientation = new List<ITick>(); // 总是创建一个新的列表
|
||||
var animators = targetObj.GetComponentsInChildren<SpriteAnimator>();
|
||||
if (animators.Length > 0)
|
||||
{
|
||||
animatorsForOrientation.AddRange(animators);
|
||||
}
|
||||
|
||||
stateAnimNodes[orientation] = animatorsForOrientation.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"InitBody: 无法为状态 {state}, 朝向 {orientation} 创建或找到有效的GameObject。");
|
||||
stateBodyNodes[orientation] = new GameObject($"ErrorNode_{state}_{orientation}"); // 提供一个错误占位符
|
||||
stateBodyNodes[orientation].transform.SetParent(body.transform, false);
|
||||
stateAnimNodes[orientation] = Array.Empty<ITick>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -321,147 +306,7 @@ namespace Entity
|
||||
SetBodyTexture(EntityState.Idle, Orientation.Down); // 激活默认朝向
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 递归初始化单个绘图节点及其子节点,具有更强的健壮性和错误处理。
|
||||
/// </summary>
|
||||
/// <param name="drawNode">绘图节点定义。</param>
|
||||
/// <param name="parent">父节点对象。</param>
|
||||
/// <returns>创建的GameObject,如果失败则返回null</returns>
|
||||
protected virtual GameObject InitBodyPart(DrawNodeDef drawNode, GameObject parent)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 参数验证
|
||||
if (drawNode == null)
|
||||
{
|
||||
Debug.LogWarning("InitBodyPart: drawNode参数为null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parent == null)
|
||||
{
|
||||
Debug.LogWarning($"InitBodyPart: 父节点为null (节点名: {drawNode.nodeName})");
|
||||
return null;
|
||||
}
|
||||
|
||||
GameObject nodeObject = null;
|
||||
// 根据纹理数量创建不同类型的节点
|
||||
switch (drawNode.textures?.Count ?? 0)
|
||||
{
|
||||
case 0:
|
||||
// 无纹理节点
|
||||
nodeObject = new GameObject(drawNode.nodeName);
|
||||
nodeObject.transform.SetParent(parent.transform, false);
|
||||
break;
|
||||
case 1:
|
||||
// 单纹理节点
|
||||
if (imagePrefab == null)
|
||||
{
|
||||
Debug.LogError($"InitBodyPart: imagePrefab未设置 (节点名: {drawNode.nodeName})");
|
||||
return null;
|
||||
}
|
||||
|
||||
nodeObject = Instantiate(imagePrefab.gameObject, parent.transform);
|
||||
var texture =
|
||||
Managers.PackagesImageManager.Instance.GetSprite(drawNode.packID,
|
||||
drawNode.textures[0]); // --- 修改点二:移除 ?. ---
|
||||
if (!texture)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"InitBodyPart: 无法获取纹理 (节点名: {drawNode.nodeName}, 纹理ID: {drawNode.textures[0]})");
|
||||
}
|
||||
|
||||
var imagePrefabCom = nodeObject.GetComponent<ImagePrefab>();
|
||||
if (imagePrefabCom != null)
|
||||
{
|
||||
imagePrefabCom.SetSprite(texture);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"InitBodyPart: 无法获取ImagePrefab组件 (节点名: {drawNode.nodeName})");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
// 多纹理动画节点
|
||||
if (!animatorPrefab)
|
||||
{
|
||||
Debug.LogError($"InitBodyPart: animatorPrefab未设置 (节点名: {drawNode.nodeName})");
|
||||
return null;
|
||||
}
|
||||
|
||||
nodeObject = Instantiate(animatorPrefab.gameObject, parent.transform);
|
||||
var animator = nodeObject.GetComponent<SpriteAnimator>();
|
||||
if (animator == null)
|
||||
{
|
||||
Debug.LogWarning($"InitBodyPart: 无法获取SpriteAnimator组件 (节点名: {drawNode.nodeName})");
|
||||
break;
|
||||
}
|
||||
|
||||
animator.SetFPS(drawNode.FPS);
|
||||
var animatedSprites = new List<Sprite>();
|
||||
foreach (var textureId in drawNode.textures)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sprite =
|
||||
Managers.PackagesImageManager.Instance.GetSprite(drawNode.packID,
|
||||
textureId); // --- 修改点二:移除 ?. ---
|
||||
if (sprite != null)
|
||||
{
|
||||
animatedSprites.Add(sprite);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"InitBodyPart: 无法获取动画纹理 (节点名: {drawNode.nodeName}, 纹理ID: {textureId})");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"InitBodyPart: 加载动画纹理时出错 (节点名: {drawNode.nodeName}, 纹理ID: {textureId}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
if (animatedSprites.Count > 0)
|
||||
{
|
||||
animator.SetSprites(animatedSprites.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"InitBodyPart: 没有有效的动画纹理 (节点名: {drawNode.nodeName})");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// 设置节点属性
|
||||
if (!nodeObject) return nodeObject;
|
||||
nodeObject.transform.localPosition = drawNode.position;
|
||||
nodeObject.name = drawNode.nodeName ?? "UnnamedNode";
|
||||
// 递归初始化子节点
|
||||
if (drawNode.nodes == null) return nodeObject;
|
||||
foreach (var child in drawNode.nodes)
|
||||
{
|
||||
try
|
||||
{
|
||||
InitBodyPart(child, nodeObject);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"InitBodyPart: 初始化子节点失败 (父节点: {drawNode.nodeName}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return nodeObject;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"InitBodyPart: 初始化节点时发生未处理的异常 (节点名: {drawNode?.nodeName}): {ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新实体的逻辑,包括玩家控制和自动行为。
|
||||
@ -485,30 +330,7 @@ namespace Entity
|
||||
{
|
||||
AutoBehave();
|
||||
}
|
||||
|
||||
if (_currentAnimatorCache != null)
|
||||
{
|
||||
foreach (var animator in _currentAnimatorCache)
|
||||
{
|
||||
animator.Tick();
|
||||
}
|
||||
}
|
||||
|
||||
if (wearponAttackAnimationNodeList != null)
|
||||
{
|
||||
foreach (var tick in wearponAttackAnimationNodeList)
|
||||
{
|
||||
tick.Tick();
|
||||
}
|
||||
}
|
||||
|
||||
if (weaponAnimatorNodeList != null)
|
||||
{
|
||||
foreach (var tick in weaponAnimatorNodeList)
|
||||
{
|
||||
tick.Tick();
|
||||
}
|
||||
}
|
||||
|
||||
if (IsShowingHealthBarUI)
|
||||
{
|
||||
_hitBarUIShowTimer -= Time.deltaTime;
|
||||
@ -561,16 +383,6 @@ namespace Entity
|
||||
|
||||
_currentState = state;
|
||||
_currentOrientation = orientation;
|
||||
|
||||
if (bodyAnimationNode.TryGetValue(_currentState, out var animationNode) &&
|
||||
animationNode.TryGetValue(_currentOrientation, out var value))
|
||||
{
|
||||
_currentAnimatorCache = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentAnimatorCache = new ITick[] { }; // 如果没有找到动画,则使用空列表
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -580,7 +392,7 @@ namespace Entity
|
||||
{
|
||||
if (IsAttacking)
|
||||
return;
|
||||
transform.position += direction * (attributes.moveSpeed * Time.deltaTime * (IsChase ? 1 : 0.5f));
|
||||
transform.position += direction * (attributes.moveSpeed * Time.deltaTime);
|
||||
SetBodyTexture(EntityState.Walking, _currentOrientation);
|
||||
_walkingTimer = 2;
|
||||
}
|
||||
@ -617,6 +429,7 @@ namespace Entity
|
||||
}
|
||||
|
||||
ShowHealthBar(); // 无论是否死亡,都更新血条UI
|
||||
TemporaryAnimationManager.Instance.GenerateTemporaryAnimation(hit.ToString(), Position);
|
||||
}
|
||||
|
||||
public void ShowHealthBar()
|
||||
@ -710,9 +523,6 @@ namespace Entity
|
||||
/// </summary>
|
||||
protected virtual void UpdatePlayerControls()
|
||||
{
|
||||
// 检测 Shift 键状态
|
||||
var isHoldingShift = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
|
||||
IsChase = !isHoldingShift; // 按住 Shift 时 IsChase = false,否则 true
|
||||
// 获取当前键盘输入状态(2D 移动,只使用 X 和 Y 轴)
|
||||
var inputDirection = Vector2.zero;
|
||||
|
||||
@ -772,17 +582,10 @@ namespace Entity
|
||||
// NEW: AttackFlow 现在接收 WeaponResource 参数
|
||||
protected IEnumerator AttackFlow(WeaponResource weapon) // 将可见性改为 protected,允许子类访问
|
||||
{
|
||||
// STEP 1: 激活武器动画节点
|
||||
if (weapon.AttackAnimationDef != null)
|
||||
{
|
||||
var animation = weapon.InstantiateAttackAnimation(body.transform);
|
||||
wearponAttackAnimationNodeRoot = animation.root;
|
||||
wearponAttackAnimationNodeList = animation.animationComponents;
|
||||
}
|
||||
|
||||
|
||||
// STEP 4: 等待到攻击判定时间
|
||||
var elapsedTime = 0f;
|
||||
while (elapsedTime < weapon.AttackDetectionTime)
|
||||
while (elapsedTime < weapon.AttackCooldown)
|
||||
{
|
||||
if (IsDead)
|
||||
{
|
||||
@ -824,8 +627,7 @@ namespace Entity
|
||||
Destroy(wearponAttackAnimationNodeRoot);
|
||||
wearponAttackAnimationNodeRoot = null;
|
||||
}
|
||||
|
||||
wearponAttackAnimationNodeList = null;
|
||||
|
||||
_attackCoroutine = null;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user