using System; using System.Collections; using System.Collections.Generic; using System.Linq; using AI; using Base; using Data; using Item; using Managers; using Prefab; using UnityEngine; namespace Entity { /// /// 表示游戏中的实体类,继承自 MonoBehaviour 并实现 ITick 接口。 /// public class Entity : MonoBehaviour, ITick { /// /// 动画预制体,用于管理实体的动画逻辑。 /// public SpriteAnimator animatorPrefab; /// /// 图像预制体,用于管理实体的静态图像显示。 /// public ImagePrefab imagePrefab; public ProgressBarPrefab healthBarPrefab; public EntityPrefab entityPrefab; public EntityDef entityDef; /// /// 手上拿着显示的贴图 /// public GameObject weaponAnimator; public ITick[] weaponAnimatorNodeList; /// /// 人工智能行为树,定义实体的行为逻辑。 /// public AIBase aiTree; /// /// 当前实体正在执行的任务。 /// public JobBase currentJob; /// /// 实体的属性定义,包括生命值、攻击力、防御力等。 /// public Attributes attributes = new(); /// /// 实体当前的移动方向。 /// public Vector3 direction; /// /// 实体的身体部分,用于挂载动画和图像节点。 /// public GameObject body; /// /// 实体所属的阵营或派系。 /// public string affiliation; /// /// 表示实体是否可以被选择。 /// public bool canSelect = true; /// /// 表示实体是否处于追逐状态(影响移动速度)。 /// public bool IsChase { set; get; } = true; public string currentDimensionId = null; /// /// 表示实体是否由玩家控制。 /// public bool PlayerControlled { set { if (value) { IsChase = true; currentJob = null; // 逻辑修改:只有当存在一个不同的焦点实体时,才将其PlayerControlled设为false if (Program.Instance.FocusedEntity && Program.Instance.FocusedEntity != this) { Program.Instance.FocusedEntity.PlayerControlled = false; } Program.Instance.SetFocusedEntity(this); } // 逻辑修改:确保只有当自身是焦点实体时才取消焦点,避免不必要的逻辑执行 else if (Program.Instance.FocusedEntity == this) { Program.Instance.SetFocusedEntity(null); } } get => Program.Instance.FocusedEntity == this; } public bool IsWalking => _walkingTimer > 0; /// /// 获取实体当前位置。 /// public Vector3 Position => transform.position; /// /// 表示实体是否已经死亡(生命值小于等于零)。 /// public bool IsDead => attributes.health <= 0; public bool IsShowingHealthBarUI => _hitBarUIShowTimer > 0; public bool IsAttacking => _attackCoroutine != null; /// /// 当实体受到伤害时触发的事件。 /// 可以订阅此事件来响应实体的生命值变化,例如更新UI或播放受击特效。 /// public event Action OnEntityHit; /// /// 当实体死亡时触发的事件。 /// 只在实体首次进入死亡状态时触发一次。 /// public event Action OnEntityDied; private bool _warning = false; /// /// 存储不同朝向下的动画节点集合。 /// public Dictionary> bodyAnimationNode = new(); private ITick[] _currentAnimatorCache; private GameObject wearponAttackAnimationNodeRoot = null; private ITick[] wearponAttackAnimationNodeList; /// /// 存储不同朝向下的身体节点对象。 /// protected Dictionary> bodyNodes = new(); /// /// 当前实体的朝向。 /// private Orientation _currentOrientation = Orientation.Down; /// /// 当前实体的状态 /// private EntityState _currentState = EntityState.Idle; // 协程引用 private Coroutine _attackCoroutine; protected List hediffs; [SerializeField] private float _hitBarUIShowTime = 5; private float _hitBarUIShowTimer = 0; private int _walkingTimer = 0; /// /// 初始化实体的基本属性和行为树。 /// /// 实体的定义数据。 public virtual void Init(EntityDef entityDef) { attributes = new Attributes(entityDef.attributes); aiTree = Utils.BehaviorTree.ConvertToAIBase(entityDef.behaviorTree); affiliation = entityDef.affiliation?.defName; InitBody(entityDef.drawingOrder); this.entityDef = entityDef; HideHealthBar(); InitWeaponAnimator(); } protected virtual void InitWeaponAnimator() { for (var i = 0; i < weaponAnimator.transform.childCount; i++) { Destroy(weaponAnimator.transform.GetChild(i).gameObject); weaponAnimatorNodeList = null; } var weapon = GetCurrentWeapon(); if (weapon == null) { weaponAnimator.SetActive(false); return; } var weaponAnimation = weapon.InstantiateAttackAnimation(weaponAnimator.transform); weaponAnimatorNodeList = weaponAnimation.animationComponents; } /// /// 初始化实体的身体部分,包括不同朝向下的绘图节点。 /// /// 绘制顺序定义。 protected virtual void InitBody(DrawingOrderDef drawingOrder) { // 预缓存枚举值(避免每次循环重复调用 Enum.GetValues) var states = Enum.GetValues(typeof(EntityState)).Cast().ToArray(); var orientations = Enum.GetValues(typeof(Orientation)).Cast().ToArray(); // 预初始化字典结构(减少内层循环的字典检查) foreach (var state in states) { bodyNodes.TryAdd(state, new Dictionary()); bodyAnimationNode.TryAdd(state, new Dictionary()); } // 主初始化逻辑 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; // --- 修改点一:处理空节点定义(增加默认精灵显示) --- if (nodeDef == null) { if (imagePrefab != null && 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(); if (imagePrefabCom != null) { imagePrefabCom.SetSprite(Managers.PackagesImageManager.Instance.defaultSprite); } else { 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); } } else { // 处理有效节点定义 if (original.HasValue && stateBodyNodes.TryGetValue(original.Value, out var reusedObj)) { targetObj = reusedObj; // 复用已有对象 } else { targetObj = InitBodyPart(nodeDef, body); // 创建新对象 } } if (targetObj != null) { stateBodyNodes[orientation] = targetObj; // 逻辑说明:确保 stateAnimNodes[orientation] 总是被初始化为一个列表 var animatorsForOrientation = new List(); // 总是创建一个新的列表 var animators = targetObj.GetComponentsInChildren(); 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(); } } } // 批量隐藏所有节点(使用字典值集合直接操作) foreach (var nodeDict in bodyNodes.Values) { foreach (var obj in nodeDict.Values) { obj.SetActive(false); } } SetBodyTexture(EntityState.Idle, Orientation.Down); // 激活默认朝向 } /// /// 递归初始化单个绘图节点及其子节点,具有更强的健壮性和错误处理。 /// /// 绘图节点定义。 /// 父节点对象。 /// 创建的GameObject,如果失败则返回null 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(); 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(); if (animator == null) { Debug.LogWarning($"InitBodyPart: 无法获取SpriteAnimator组件 (节点名: {drawNode.nodeName})"); break; } animator.SetFPS(drawNode.FPS); var animatedSprites = new List(); 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; } } /// /// 更新实体的逻辑,包括玩家控制和自动行为。 /// public void Tick() { if (_walkingTimer > 0) { _walkingTimer -= 1; if (_walkingTimer <= 0) { SetBodyTexture(EntityState.Idle, _currentOrientation); } } if (PlayerControlled) { UpdatePlayerControls(); } else { 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; if (_hitBarUIShowTimer <= 0) { HideHealthBar(); } } } /// /// 尝试攻击目标实体。 /// public virtual void TryAttack() // 使用override允许子类重写 { if (IsAttacking || IsDead) return; // 死亡时无法攻击 // 尝试获取当前武器 WeaponResource currentWeapon = GetCurrentWeapon(); // 如果没有武器,可以选择进行徒手攻击或者直接返回 // 暂时设定为:如果没有武器,则不进行攻击 if (currentWeapon == null) { // 可以在这里添加一个默认的徒手攻击逻辑,或者播放一个“不能攻击”的提示 Debug.Log($"{name} 没有装备武器,无法攻击。"); return; } // 启动基于武器的攻击协程 _attackCoroutine = StartCoroutine(AttackFlow(currentWeapon)); } public virtual void SetBodyTexture(EntityState state, Orientation orientation) { if (bodyNodes.TryGetValue(_currentState, out var stateNode)) { if (stateNode.TryGetValue(_currentOrientation, out var node)) { node.SetActive(false); } } if (bodyNodes.TryGetValue(state, out var showStateNode)) { if (showStateNode.TryGetValue(orientation, out var showNode)) { showNode.SetActive(true); } } _currentState = state; _currentOrientation = orientation; if (bodyAnimationNode.TryGetValue(_currentState, out var animationNode) && animationNode.TryGetValue(_currentOrientation, out var value)) { _currentAnimatorCache = value; } else { _currentAnimatorCache = new ITick[] { }; // 如果没有找到动画,则使用空列表 } } /// /// 根据方向尝试移动实体。 /// public virtual void TryMove() { if (IsAttacking) return; transform.position += direction * (attributes.moveSpeed * Time.deltaTime * (IsChase ? 1 : 0.5f)); SetBodyTexture(EntityState.Walking, _currentOrientation); _walkingTimer = 2; } /// /// 处理实体受到攻击的逻辑。 /// /// 攻击来源实体。 public virtual void OnHit(Entity from) { // MODIFIED: 收到攻击时触发回调 if (IsDead) // 如果已经死亡,则不再处理伤害 nor 触发事件 { return; } var hit = from.attributes.attack - attributes.defense; if (hit < 0) hit = from.attributes.attack / 100; // 确保伤害不为负,最小为0 hit = Mathf.Max(0, hit); attributes.health -= hit; var wasFatal = IsDead; // 检查这次攻击是否导致实体死亡 // 触发 OnEntityHit 事件 OnEntityHit?.Invoke(new EntityHitEventArgs( this, from, hit, attributes.health, entityDef.attributes.health, wasFatal)); currentJob?.StopJob(); if (wasFatal) { // 如果是首次死亡,则触发 OnEntityDied 事件 // MODIFIED: 停止所有活动,包括当前工作 currentJob = null; // 清除当前工作 OnEntityDied?.Invoke(this); } ShowHealthBar(); // 无论是否死亡,都更新血条UI } public void ShowHealthBar() { if (!healthBarPrefab) return; healthBarPrefab.gameObject.SetActive(true); healthBarPrefab.Progress = (float)attributes.health / entityDef.attributes.health; _hitBarUIShowTimer = _hitBarUIShowTime; } public void HideHealthBar() { if (!healthBarPrefab) return; healthBarPrefab.gameObject.SetActive(false); } /// /// 杀死实体,设置生命值为零。 /// public virtual void Kill() { if (IsDead) { return; } attributes.health = 0; // 直接设置生命值为0 // MODIFIED: 停止所有活动,包括当前工作 currentJob?.StopJob(); currentJob = null; // 清除当前工作 // 触发 OnEntityDied 事件 OnEntityDied?.Invoke(this); ShowHealthBar(); } /// /// 设置实体的目标位置。 /// /// 目标位置。 public virtual void SetTarget(Vector3 pos) { direction = (pos - transform.position).normalized; Orientation ori; // 判断方向向量最接近哪个朝向 if (Mathf.Abs(direction.y) > Mathf.Abs(direction.x)) { // 垂直方向优先 ori = direction.y > 0 ? Orientation.Up : Orientation.Down; } else { // 水平方向优先 ori = direction.x > 0 ? Orientation.Right : Orientation.Left; } SetBodyTexture(_currentState, ori); } /// /// 自动行为逻辑,根据行为树执行任务。 /// protected virtual void AutoBehave() { if (aiTree == null) return; if (currentJob == null || !currentJob.Running) { currentJob = aiTree.GetJob(this); if (currentJob == null) { if (!_warning) { Debug.LogWarning($"{GetType().Name}类型的{name}没有分配到任何工作,给行为树末尾添加等待行为,避免由于没有工作导致无意义的反复查找工作导致性能问题"); _warning = true; } return; } currentJob.StartJob(this); } currentJob.Update(); } /// /// 更新玩家控制的逻辑,处理输入和移动。 /// 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; // 检测 WASD 或方向键输入 if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) { inputDirection += Vector2.up; // 向上移动(Y 轴正方向) } if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) { inputDirection += Vector2.down; // 向下移动(Y 轴负方向) } if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) { inputDirection += Vector2.left; // 向左移动(X 轴负方向) } if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) { inputDirection += Vector2.right; // 向右移动(X 轴正方向) } if (Input.GetMouseButton(0)) { TryAttack(); } // 如果有输入方向,则设置目标位置并尝试移动 if (inputDirection == Vector2.zero) return; // 归一化方向向量,确保对角线移动速度一致 inputDirection = inputDirection.normalized; // 设置目标位置(2D 移动,Z 轴保持不变) var targetPosition = transform.position + new Vector3(inputDirection.x, inputDirection.y, 0); // 调用 SetTarget 方法设置目标位置 SetTarget(targetPosition); // 调用 TryMove 方法处理实际移动逻辑 TryMove(); } // NEW: ShakeInDirectionCoroutine 签名修改以接收持续时间 private IEnumerator ShakeInDirectionCoroutine(float duration) { var originalPosition = transform.position; // 记录原始位置 // 在攻击动画持续时间内进行抖动效果 transform.position += direction * 0.5f; yield return new WaitForSeconds(duration); transform.position = originalPosition; } // 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: 等待到攻击判定时间 float elapsedTime = 0f; while (elapsedTime < weapon.AttackDetectionTime) { if (IsDead) { /* 如果实体在此期间死亡,立刻中断 */ break; } elapsedTime += Time.deltaTime; yield return null; // 等待一帧 } // 如果实体在等待期间死亡,清理并退出 if (IsDead) { CleanupAttack(weapon); yield break; } ExecuteWeaponAction(weapon); float remainingAnimationTime = weapon.AttackAnimationTime - elapsedTime; if (remainingAnimationTime > 0) { yield return new WaitForSeconds(remainingAnimationTime); } else if (weapon.AttackAnimationTime > 0) { yield return new WaitForSeconds(weapon.AttackAnimationTime - elapsedTime); } // STEP 7: 清理攻击状态 CleanupAttack(weapon); } private void CleanupAttack(WeaponResource weapon) { if (wearponAttackAnimationNodeRoot) { Destroy(wearponAttackAnimationNodeRoot); wearponAttackAnimationNodeRoot = null; } wearponAttackAnimationNodeList = null; _attackCoroutine = null; } protected virtual void ExecuteWeaponAction(WeaponResource weapon) // 将可见性改为 protected,允许子类重写 { if (weapon == null) return; // 安全检查 switch (weapon.Type) { case WeaponType.Melee: ExecuteMeleeAttack(weapon); break; case WeaponType.Ranged: ExecuteRangedAttack(weapon); break; default: Debug.LogWarning($"未知武器类型: {weapon.Type} for {name}"); break; } } private void ExecuteMeleeAttack(WeaponResource weapon) { if (weapon.Attributes == null) { Debug.LogWarning($"武器 {weapon.DefName} 没有定义Attributes,无法执行近战攻击。"); return; } var attackRange = weapon.Attributes.attackRange; var attackTargetCount = weapon.Attributes.attackTargetCount; var hits = Physics2D.OverlapCircleAll( transform.position, attackRange, LayerMask.GetMask("Entity")); foreach (var hit in hits) { if (attackTargetCount <= 0) break; // 已达到最大攻击目标数 if (hit.gameObject == gameObject) continue; // 不攻击自己 var entity = hit.GetComponent(); if (entity != null && entity.affiliation != affiliation) // 确保是敌对实体 { entity.OnHit(this); // 攻击时将自身作为攻击来源 attackTargetCount--; } } } // NEW: 辅助方法用于执行远程攻击 private void ExecuteRangedAttack(WeaponResource weapon) { if (weapon.Bullet == null) { Debug.LogWarning($"远程武器 {weapon.DefName} 没有定义Bullet,无法发射子弹。"); return; } // 获取子弹方向。这里使用实体当前的移动方向作为子弹发射方向 // 更复杂的逻辑可能根据鼠标位置、目标位置等确定 Vector3 bulletDirection = direction; // 实体当前的朝向 if (PlayerControlled && Input.GetMouseButton(0)) // 玩家控制时,如果鼠标按下,尝试朝鼠标方向发射 { Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); mouseWorldPos.z = transform.position.z; // 保持Z轴一致 bulletDirection = (mouseWorldPos - transform.position).normalized; } // 如果没有明确的方向,给一个默认值以防万一 if (bulletDirection == Vector3.zero) bulletDirection = Vector3.down; // 假设 EntityManage.Instance.GenerateBulletEntity 方法存在 // (需要一个 EntityManage 单例来实现子弹生成) if (EntityManage.Instance != null && Program.Instance != null) { EntityManage.Instance.GenerateBulletEntity( Program.Instance.FocusedDimensionId, weapon.Bullet, transform.position, // 子弹的生成位置 bulletDirection, // 子弹的初始方向 this); // 子弹的发射者 } else { Debug.LogError("EntityManage.Instance 或 Program.Instance 为空,无法生成子弹。请确保它们已正确初始化。"); } } public virtual WeaponResource GetCurrentWeapon() { return null; } } }