- 补充游戏核心逻辑

- 完成 TodOList P0
    - 添加游戏内控制台便于调试
    - 增加敌人互斥机制提高运算负载
    - 优化互斥算法将游戏性能基准线拉到 1k 敌人
This commit is contained in:
SepComet
2026-02-20 18:08:27 +08:00
parent b4bc8b0445
commit 3742110e9a
196 changed files with 4868 additions and 31326 deletions
@@ -0,0 +1,25 @@
namespace Entity.Weapon
{
public partial class WeaponHandgun
{
private class AttackState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
public override void OnEnter()
{
_weapon._currAttackTimer = 0f;
_weapon.Attack();
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
}
public override void OnLeave() { }
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4e72df067466464fa3be92bfd049dd0d
timeCreated: 1771579220
@@ -0,0 +1,46 @@
namespace Entity.Weapon
{
public partial class WeaponHandgun
{
private class CheckInRangeState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Check_InRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
public override void OnEnter()
{
if (_weapon._currAttackTimer >= _weapon._weaponData.Cooldown)
{
_weapon.TransitionTo(WeaponStateType.Attack);
}
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
_weapon._currAttackTimer += elapseSeconds;
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (!_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
return;
}
if (_weapon._currAttackTimer >= _weapon._weaponData.Cooldown)
{
_weapon.TransitionTo(WeaponStateType.Attack);
}
}
public override void OnLeave() { }
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bfd76c0fb7284ceaa28f78c702f595da
timeCreated: 1771579231
@@ -0,0 +1,33 @@
namespace Entity.Weapon
{
public partial class WeaponHandgun
{
private class CheckOutRangeState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Check_OutRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
public override void OnEnter() { }
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
_weapon._currAttackTimer += elapseSeconds;
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
}
}
public override void OnLeave() { }
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a9f936d3c8643f7bf632734548f800c
timeCreated: 1771579241
@@ -0,0 +1,27 @@
namespace Entity.Weapon
{
public partial class WeaponHandgun
{
private class IdleState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
public override void OnEnter() { }
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToOrigin(elapseSeconds);
_weapon._currAttackTimer += elapseSeconds;
if (_weapon._target != null && _weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
}
}
public override void OnLeave() { }
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9c5341944d1a467b8553d31bc3d81d5b
timeCreated: 1771579252
@@ -0,0 +1,143 @@
using Definition.DataStruct;
using Definition.Enum;
using Entity.EntityData;
using CustomUtility;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity.Weapon
{
public partial class WeaponHandgun : WeaponBase
{
#region Property
private WeaponHandgunData _weaponData;
private Quaternion _cachedRotation;
[SerializeField] private float _rotateSpeed = 6f;
[SerializeField] private LayerMask _hitMask = ~0;
[SerializeField] private Vector3 _fireOriginOffset = Vector3.zero;
[SerializeField] private float _hitMarkerSize = 0.2f;
[SerializeField] private float _hitMarkerYOffset = 1.2f;
[SerializeField] private float _hitMarkerDuration = 0.15f;
[SerializeField] private Color _hitMarkerColor = new(1f, 0f, 0f, 0.95f);
private IWeaponAttackEffect _attackEffect;
#endregion
public override ImpactData GetImpactData()
{
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, AttackStat);
}
protected override void BuildStates()
{
RegisterState(new IdleState());
RegisterState(new CheckOutRangeState());
RegisterState(new CheckInRangeState());
RegisterState(new AttackState());
}
protected override void Attack()
{
FaceTargetImmediately();
Vector3 fireOrigin = CachedTransform.TransformPoint(_fireOriginOffset);
Vector3 fireDirection = CachedTransform.forward;
float maxDistance = Mathf.Max(0.1f, _weaponData.AttackRange);
if (Physics.Raycast(fireOrigin, fireDirection, out RaycastHit hit, maxDistance, _hitMask,
QueryTriggerInteraction.Collide))
{
TargetableObject targetable = hit.collider.GetComponentInParent<TargetableObject>();
if (targetable != null && targetable.Available && !targetable.IsDead)
{
_attackEffect?.Play(this, hit.point, targetable, 0f);
_isAttacking = true;
AIUtility.PerformCollision(targetable, this);
_isAttacking = false;
}
}
}
protected override void Check()
{
_target = SelectTarget(_sqrRange);
}
private void RotateToTarget(float elapseSeconds)
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = (_target.CachedTransform.position - CachedTransform.position).normalized;
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget, Vector3.up);
CachedTransform.rotation = Quaternion.Slerp(CachedTransform.rotation, targetRotation,
_rotateSpeed * elapseSeconds);
}
private void RotateToOrigin(float elapseSeconds)
{
CachedTransform.rotation = Quaternion.Slerp(CachedTransform.rotation, _cachedRotation,
_rotateSpeed * elapseSeconds);
}
private void FaceTargetImmediately()
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
}
#region Lifecycle
protected override bool OnWeaponShow(object userData)
{
_weaponData = RequireWeaponData<WeaponHandgunData>(userData);
if (_weaponData == null) return false;
WeaponData = _weaponData;
_currAttackTimer = 0f;
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
_cachedRotation = CachedTransform.rotation;
_attackEffect = new HandgunHitMarkerAttackEffect(_hitMarkerSize, _hitMarkerYOffset, _hitMarkerDuration,
_hitMarkerColor);
if (_weaponData.OwnerCamp == CampType.Player)
{
gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
_hitMask = LayerMask.GetMask("Enemy");
}
else if (_weaponData.OwnerCamp == CampType.Enemy)
{
gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
_hitMask = LayerMask.GetMask("Player");
}
return true;
}
protected override void OnWeaponHide(object userData)
{
_attackEffect = null;
}
protected override void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
{
BindAttackStatFromOwner(parentEntity);
}
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
{
ReleaseAttackStatSubscription();
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 44ae1bdd1d43e274980743df21658730
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: