(client) feat:添加消息定义,添加故事定义及其运算,武器动画可用,装备UI可用以及切换武器 fix:修复快速攻击导致协程释放出错卡死,重构为计时器,修复类型转换错误导致报错
This commit is contained in:
@ -12,7 +12,7 @@ namespace Entity
|
||||
public override void SetTarget(Vector3 pos)
|
||||
{
|
||||
base.SetTarget(pos);
|
||||
RotateTransformToDirection(transform, direction);
|
||||
Utils.RotateTool.RotateTransformToDirection(transform, direction);
|
||||
}
|
||||
|
||||
protected override void AutoBehave()
|
||||
@ -41,19 +41,6 @@ namespace Entity
|
||||
attributes.health -= 1;
|
||||
}
|
||||
|
||||
// 旋转对象到指定方向
|
||||
public static void RotateTransformToDirection(Transform transform, Vector3 targetDirection)
|
||||
{
|
||||
// 确保目标方向不是零向量
|
||||
if (targetDirection == Vector3.zero)
|
||||
return;
|
||||
|
||||
// 计算当前向上方向与目标方向之间的角度
|
||||
var angle = Mathf.Atan2(targetDirection.y, targetDirection.x) * Mathf.Rad2Deg;
|
||||
|
||||
|
||||
// 应用旋转
|
||||
transform.rotation = Quaternion.Euler(0f, 0f, angle);
|
||||
}
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ namespace Entity
|
||||
public override void Init(EntityDef entityDef)
|
||||
{
|
||||
Inventory = new Inventory(this, 3);
|
||||
|
||||
Inventory.OnInventoryChanged += InventoryChange;
|
||||
CurrentSelected = 0;
|
||||
base.Init(entityDef);
|
||||
}
|
||||
@ -64,7 +64,12 @@ namespace Entity
|
||||
public override WeaponResource GetCurrentWeapon()
|
||||
{
|
||||
var currentSelectItem = Inventory.GetSlot(CurrentSelected);
|
||||
return (WeaponResource)currentSelectItem?.Item;
|
||||
return currentSelectItem?.Item as WeaponResource;
|
||||
}
|
||||
|
||||
private void InventoryChange()
|
||||
{
|
||||
InitWeaponAnimator();
|
||||
}
|
||||
}
|
||||
}
|
@ -76,6 +76,8 @@ namespace Entity
|
||||
/// </summary>
|
||||
public Vector3 direction;
|
||||
|
||||
public Vector3 attackDirection;
|
||||
|
||||
/// <summary>
|
||||
/// 实体的身体部分,用于挂载动画和图像节点。
|
||||
/// </summary>
|
||||
@ -137,7 +139,10 @@ namespace Entity
|
||||
public bool IsDead => attributes.health <= 0;
|
||||
|
||||
public bool IsShowingHealthBarUI => _hitBarUIShowTimer > 0;
|
||||
public bool IsAttacking => _attackCoroutine != null;
|
||||
public bool IsAttacking => _attackTimer > 0;
|
||||
private float _attackTimer = 0;
|
||||
private float _attackDetectionTime = 0;
|
||||
private WeaponResource currentAttackWeapon;
|
||||
|
||||
/// <summary>
|
||||
/// 当实体受到伤害时触发的事件。
|
||||
@ -170,9 +175,7 @@ namespace Entity
|
||||
/// 当前实体的状态
|
||||
/// </summary>
|
||||
private EntityState _currentState = EntityState.Idle;
|
||||
|
||||
// 协程引用
|
||||
private Coroutine _attackCoroutine;
|
||||
|
||||
|
||||
|
||||
[SerializeField] private float _hitBarUIShowTime = 5;
|
||||
@ -200,19 +203,19 @@ namespace Entity
|
||||
{
|
||||
if (weaponAnimator && weaponAnimator.transform.childCount > 0)
|
||||
{
|
||||
foreach (GameObject child in weaponAnimator.transform)
|
||||
foreach (Transform child in weaponAnimator.transform)
|
||||
{
|
||||
Destroy(child);
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
var currentWeapon = GetCurrentWeapon();
|
||||
if (currentWeapon?.AttackAnimationDef == null)
|
||||
if (currentWeapon?.AttackAnimation == null)
|
||||
return;
|
||||
weaponItem=GameObjectCreate.SpriteAnimator(currentWeapon.Icon.ToArray(), weaponAnimator.transform);
|
||||
weaponItem.SetFPS(currentWeapon.FPS);
|
||||
|
||||
weaponAttackAnimation = GameObjectCreate.SpriteAnimator(currentWeapon.AttackAnimationDef.ToArray(),
|
||||
weaponAttackAnimation = GameObjectCreate.SpriteAnimator(currentWeapon.AttackAnimation.ToArray(),
|
||||
weaponAnimator.transform);
|
||||
weaponAttackAnimation.SetFPS(currentWeapon.FPS);
|
||||
weaponAttackAnimation.gameObject.SetActive(false);
|
||||
@ -330,7 +333,7 @@ namespace Entity
|
||||
{
|
||||
AutoBehave();
|
||||
}
|
||||
|
||||
|
||||
if (IsShowingHealthBarUI)
|
||||
{
|
||||
_hitBarUIShowTimer -= Time.deltaTime;
|
||||
@ -339,6 +342,30 @@ namespace Entity
|
||||
HideHealthBar();
|
||||
}
|
||||
}
|
||||
|
||||
if (_attackTimer > 0)
|
||||
{
|
||||
_attackTimer -= Time.deltaTime;
|
||||
if (currentAttackWeapon != null && _attackTimer <= _attackDetectionTime)
|
||||
{
|
||||
if (currentAttackWeapon.Type == WeaponType.Melee)
|
||||
{
|
||||
ExecuteMeleeAttack(currentAttackWeapon);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteRangedAttack(currentAttackWeapon);
|
||||
}
|
||||
|
||||
currentAttackWeapon = null;
|
||||
}
|
||||
|
||||
if (_attackTimer <= 0)
|
||||
{
|
||||
|
||||
SetBodyTexture(EntityState.Idle, _currentOrientation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -358,21 +385,39 @@ namespace Entity
|
||||
return;
|
||||
}
|
||||
|
||||
// 启动基于武器的攻击协程
|
||||
_attackCoroutine = StartCoroutine(AttackFlow(currentWeapon));
|
||||
StartAttack(currentWeapon);
|
||||
}
|
||||
|
||||
private void StartAttack(WeaponResource weaponResource)
|
||||
{
|
||||
_attackTimer = weaponResource.AttackCooldown;
|
||||
_attackDetectionTime = weaponResource.AttackDetectionTime;
|
||||
if (weaponResource.AttackAnimationTime > 0)
|
||||
{
|
||||
TemporaryAnimationManager.Instance.GenerateTemporaryAnimation(
|
||||
weaponResource.AttackAnimation.ToArray(), Position,transform, weaponResource.AttackAnimationTime,
|
||||
weaponResource.FPS);
|
||||
}
|
||||
if (weaponResource.UseEntityAttackAnimation)
|
||||
{
|
||||
SetBodyTexture(
|
||||
weaponResource.Type == WeaponType.Melee ? EntityState.MeleeAttack : EntityState.RangedAttack,
|
||||
_currentOrientation);
|
||||
}
|
||||
else
|
||||
{
|
||||
HideCurrentBodyTexture();
|
||||
}
|
||||
|
||||
currentAttackWeapon = weaponResource;
|
||||
}
|
||||
|
||||
|
||||
public virtual void SetBodyTexture(EntityState state, Orientation orientation)
|
||||
public void SetBodyTexture(EntityState state, Orientation orientation)
|
||||
{
|
||||
if (bodyNodes.TryGetValue(_currentState, out var stateNode))
|
||||
{
|
||||
if (stateNode.TryGetValue(_currentOrientation, out var node))
|
||||
{
|
||||
node.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
HideCurrentBodyTexture();
|
||||
if (IsAttacking && !currentAttackWeapon.UseEntityAttackAnimation)
|
||||
return;
|
||||
if (bodyNodes.TryGetValue(state, out var showStateNode))
|
||||
{
|
||||
if (showStateNode.TryGetValue(orientation, out var showNode))
|
||||
@ -385,13 +430,22 @@ namespace Entity
|
||||
_currentOrientation = orientation;
|
||||
}
|
||||
|
||||
public void HideCurrentBodyTexture()
|
||||
{
|
||||
if (!bodyNodes.TryGetValue(_currentState, out var stateNode)) return;
|
||||
if (stateNode.TryGetValue(_currentOrientation, out var node))
|
||||
{
|
||||
node.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据方向尝试移动实体。
|
||||
/// </summary>
|
||||
public virtual void TryMove()
|
||||
{
|
||||
if (IsAttacking)
|
||||
return;
|
||||
// if (IsAttacking)
|
||||
// return;
|
||||
transform.position += direction * (attributes.moveSpeed * Time.deltaTime);
|
||||
SetBodyTexture(EntityState.Walking, _currentOrientation);
|
||||
_walkingTimer = 2;
|
||||
@ -428,8 +482,15 @@ namespace Entity
|
||||
OnEntityDied?.Invoke(this);
|
||||
}
|
||||
|
||||
ShowHealthBar(); // 无论是否死亡,都更新血条UI
|
||||
TemporaryAnimationManager.Instance.GenerateTemporaryAnimation(hit.ToString(), Position);
|
||||
if (Setting.Instance.CurrentSettings.showHealthBarByHit)
|
||||
{
|
||||
ShowHealthBar();
|
||||
}
|
||||
|
||||
if (Setting.Instance.CurrentSettings.showHitNumber)
|
||||
{
|
||||
TemporaryAnimationManager.Instance.GenerateTemporaryAnimation(hit.ToString(), Position);
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowHealthBar()
|
||||
@ -488,6 +549,10 @@ namespace Entity
|
||||
}
|
||||
|
||||
SetBodyTexture(_currentState, ori);
|
||||
if (!PlayerControlled)
|
||||
{
|
||||
attackDirection=direction;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -523,6 +588,12 @@ namespace Entity
|
||||
/// </summary>
|
||||
protected virtual void UpdatePlayerControls()
|
||||
{
|
||||
if (Input.GetMouseButton(0))
|
||||
{
|
||||
var mousePos = MousePosition.GetWorldPosition();
|
||||
attackDirection = new Vector3(mousePos.x,mousePos.y) - Position;
|
||||
}
|
||||
|
||||
// 获取当前键盘输入状态(2D 移动,只使用 X 和 Y 轴)
|
||||
var inputDirection = Vector2.zero;
|
||||
|
||||
@ -566,89 +637,9 @@ namespace Entity
|
||||
// 调用 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 4: 等待到攻击判定时间
|
||||
var elapsedTime = 0f;
|
||||
while (elapsedTime < weapon.AttackCooldown)
|
||||
{
|
||||
if (IsDead)
|
||||
{
|
||||
/* 如果实体在此期间死亡,立刻中断 */
|
||||
break;
|
||||
}
|
||||
|
||||
elapsedTime += Time.deltaTime;
|
||||
yield return null; // 等待一帧
|
||||
}
|
||||
|
||||
// 如果实体在等待期间死亡,清理并退出
|
||||
if (IsDead)
|
||||
{
|
||||
CleanupAttack(weapon);
|
||||
yield break;
|
||||
}
|
||||
|
||||
ExecuteWeaponAction(weapon);
|
||||
|
||||
var 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;
|
||||
}
|
||||
|
||||
_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)
|
||||
@ -690,13 +681,7 @@ namespace Entity
|
||||
|
||||
// 获取子弹方向。这里使用实体当前的移动方向作为子弹发射方向
|
||||
// 更复杂的逻辑可能根据鼠标位置、目标位置等确定
|
||||
var bulletDirection = direction; // 实体当前的朝向
|
||||
if (PlayerControlled && Input.GetMouseButton(0)) // 玩家控制时,如果鼠标按下,尝试朝鼠标方向发射
|
||||
{
|
||||
var mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
|
||||
mouseWorldPos.z = transform.position.z; // 保持Z轴一致
|
||||
bulletDirection = (mouseWorldPos - transform.position).normalized;
|
||||
}
|
||||
var bulletDirection = attackDirection; // 实体当前的朝向
|
||||
|
||||
// 如果没有明确的方向,给一个默认值以防万一
|
||||
if (bulletDirection == Vector3.zero) bulletDirection = Vector3.down;
|
||||
|
@ -12,6 +12,7 @@ namespace Entity
|
||||
public class Pickup : Entity
|
||||
{
|
||||
public ItemResource itemResource;
|
||||
|
||||
protected override void AutoBehave()
|
||||
{
|
||||
}
|
||||
@ -49,6 +50,7 @@ namespace Entity
|
||||
var animator = animatorObj.GetComponent<SpriteAnimator>();
|
||||
animator.SetSprites(texture.ToArray());
|
||||
}
|
||||
|
||||
|
||||
SetBodyTexture(EntityState.Idle, Orientation.Down);
|
||||
}
|
||||
|
Reference in New Issue
Block a user