Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/Entity/Entity.cs

921 lines
34 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;
using System.Collections.Generic;
using System.Linq;
using AI;
using Base;
using Data;
using Item;
using Managers;
using Prefab;
using UnityEngine;
namespace Entity
{
/// <summary>
/// 表示游戏中的实体类,继承自 MonoBehaviour 并实现 ITick 接口。
/// </summary>
public class Entity : MonoBehaviour, ITick
{
/// <summary>
/// 动画预制体,用于管理实体的动画逻辑。
/// </summary>
public SpriteAnimator animatorPrefab;
/// <summary>
/// 图像预制体,用于管理实体的静态图像显示。
/// </summary>
public ImagePrefab imagePrefab;
public ProgressBarPrefab healthBarPrefab;
public EntityPrefab entityPrefab;
public EntityDef entityDef;
/// <summary>
/// 手上拿着显示的贴图
/// </summary>
public GameObject weaponAnimator;
public ITick[] weaponAnimatorNodeList;
/// <summary>
/// 人工智能行为树,定义实体的行为逻辑。
/// </summary>
public AIBase aiTree;
/// <summary>
/// 当前实体正在执行的任务。
/// </summary>
public JobBase currentJob;
/// <summary>
/// 实体的属性定义,包括生命值、攻击力、防御力等。
/// </summary>
public Attributes attributes = new();
/// <summary>
/// 实体当前的移动方向。
/// </summary>
public Vector3 direction;
/// <summary>
/// 实体的身体部分,用于挂载动画和图像节点。
/// </summary>
public GameObject body;
/// <summary>
/// 实体所属的阵营或派系。
/// </summary>
public string affiliation;
/// <summary>
/// 表示实体是否可以被选择。
/// </summary>
public bool canSelect = true;
/// <summary>
/// 表示实体是否处于追逐状态(影响移动速度)。
/// </summary>
public bool IsChase { set; get; } = true;
public string currentDimensionId = null;
/// <summary>
/// 表示实体是否由玩家控制。
/// </summary>
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;
/// <summary>
/// 获取实体当前位置。
/// </summary>
public Vector3 Position => transform.position;
/// <summary>
/// 表示实体是否已经死亡(生命值小于等于零)。
/// </summary>
public bool IsDead => attributes.health <= 0;
public bool IsShowingHealthBarUI => _hitBarUIShowTimer > 0;
public bool IsAttacking => _attackCoroutine != null;
/// <summary>
/// 当实体受到伤害时触发的事件。
/// 可以订阅此事件来响应实体的生命值变化例如更新UI或播放受击特效。
/// </summary>
public event Action<EntityHitEventArgs> OnEntityHit;
/// <summary>
/// 当实体死亡时触发的事件。
/// 只在实体首次进入死亡状态时触发一次。
/// </summary>
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>
/// 存储不同朝向下的身体节点对象。
/// </summary>
protected Dictionary<EntityState, Dictionary<Orientation, GameObject>> bodyNodes = new();
/// <summary>
/// 当前实体的朝向。
/// </summary>
private Orientation _currentOrientation = Orientation.Down;
/// <summary>
/// 当前实体的状态
/// </summary>
private EntityState _currentState = EntityState.Idle;
// 协程引用
private Coroutine _attackCoroutine;
[SerializeField] private float _hitBarUIShowTime = 5;
private float _hitBarUIShowTimer = 0;
private int _walkingTimer = 0;
/// <summary>
/// 初始化实体的基本属性和行为树。
/// </summary>
/// <param name="entityDef">实体的定义数据。</param>
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;
}
/// <summary>
/// 初始化实体的身体部分,包括不同朝向下的绘图节点。
/// </summary>
/// <param name="drawingOrder">绘制顺序定义。</param>
protected virtual void InitBody(DrawingOrderDef drawingOrder)
{
// 预缓存枚举值(避免每次循环重复调用 Enum.GetValues
var states = Enum.GetValues(typeof(EntityState)).Cast<EntityState>().ToArray();
var orientations = Enum.GetValues(typeof(Orientation)).Cast<Orientation>().ToArray();
// 预初始化字典结构(减少内层循环的字典检查)
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;
// --- 修改点一:处理空节点定义(增加默认精灵显示) ---
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<ImagePrefab>();
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<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>();
}
}
}
// 批量隐藏所有节点(使用字典值集合直接操作)
foreach (var nodeDict in bodyNodes.Values)
{
foreach (var obj in nodeDict.Values)
{
obj.SetActive(false);
}
}
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>
/// 更新实体的逻辑,包括玩家控制和自动行为。
/// </summary>
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();
}
}
}
/// <summary>
/// 尝试攻击目标实体。
/// </summary>
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[] { }; // 如果没有找到动画,则使用空列表
}
}
/// <summary>
/// 根据方向尝试移动实体。
/// </summary>
public virtual void TryMove()
{
if (IsAttacking)
return;
transform.position += direction * (attributes.moveSpeed * Time.deltaTime * (IsChase ? 1 : 0.5f));
SetBodyTexture(EntityState.Walking, _currentOrientation);
_walkingTimer = 2;
}
/// <summary>
/// 处理实体受到攻击的逻辑。
/// </summary>
/// <param name="from">攻击来源实体。</param>
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);
}
/// <summary>
/// 杀死实体,设置生命值为零。
/// </summary>
public virtual void Kill()
{
if (IsDead)
{
return;
}
attributes.health = 0; // 直接设置生命值为0
// MODIFIED: 停止所有活动,包括当前工作
currentJob?.StopJob();
currentJob = null; // 清除当前工作
// 触发 OnEntityDied 事件
OnEntityDied?.Invoke(this);
ShowHealthBar();
}
/// <summary>
/// 设置实体的目标位置。
/// </summary>
/// <param name="pos">目标位置。</param>
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);
}
/// <summary>
/// 自动行为逻辑,根据行为树执行任务。
/// </summary>
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();
}
/// <summary>
/// 更新玩家控制的逻辑,处理输入和移动。
/// </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;
// 检测 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<Entity>();
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;
}
}
}