fix 1
1. 修复投射物闪避口径(使用 Value,与全局伤害逻辑一致) - 将 SimulationWorld 内投射物伤害计算改为直接复用 AIUtility.CalcDamageHP(...)。 - 删除了 SimulationWorld 里那套重复且使用 dodgeStat.Percent 的私有计算函数,避免再次分叉。 2. 修复目标选择索引“重建过于激进” - 去掉了 Job Tick 末尾的“每帧强制 MarkDirty + Build”。 - 改为在 Job 输出回写时,只有敌人 XZ 坐标发生变化才标记 dirty。 - 空间索引构建条件改为“仅 dirty 时重建”,不再因跨帧自动重建。
This commit is contained in:
parent
1a45b513f2
commit
688fefe848
|
|
@ -226,9 +226,6 @@ namespace Simulation
|
||||||
RecycleInactiveProjectiles();
|
RecycleInactiveProjectiles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MarkEnemyTargetSpatialIndexDirty();
|
|
||||||
BuildEnemyTargetSpatialIndexIfNeeded();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private JobHandle ExecuteEnemyMovementJob(in SimulationTickContext context)
|
private JobHandle ExecuteEnemyMovementJob(in SimulationTickContext context)
|
||||||
|
|
|
||||||
|
|
@ -619,9 +619,23 @@ namespace Simulation
|
||||||
private void ApplyJobOutputToSimulation()
|
private void ApplyJobOutputToSimulation()
|
||||||
{
|
{
|
||||||
int enemyCount = Mathf.Min(_enemies.Count, _enemyJobOutputs.Length);
|
int enemyCount = Mathf.Min(_enemies.Count, _enemyJobOutputs.Length);
|
||||||
|
bool hasEnemyPositionChanged = false;
|
||||||
for (int i = 0; i < enemyCount; i++)
|
for (int i = 0; i < enemyCount; i++)
|
||||||
{
|
{
|
||||||
_enemies[i] = ConvertToEnemySimData(_enemyJobOutputs[i]);
|
EnemyJobOutputData output = _enemyJobOutputs[i];
|
||||||
|
if (!hasEnemyPositionChanged)
|
||||||
|
{
|
||||||
|
Vector3 currentPosition = _enemies[i].Position;
|
||||||
|
hasEnemyPositionChanged = currentPosition.x != output.Position.x ||
|
||||||
|
currentPosition.z != output.Position.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
_enemies[i] = ConvertToEnemySimData(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasEnemyPositionChanged)
|
||||||
|
{
|
||||||
|
MarkEnemyTargetSpatialIndexDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
int projectileCount = Mathf.Min(_projectiles.Count, _projectileJobOutputs.Length);
|
int projectileCount = Mathf.Min(_projectiles.Count, _projectileJobOutputs.Length);
|
||||||
|
|
|
||||||
|
|
@ -453,7 +453,7 @@ namespace Simulation
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
damage = CalculateProjectileDamage(sourceImpact.AttackBase, sourceImpact.AttackStat,
|
damage = AIUtility.CalcDamageHP(sourceImpact.AttackBase, sourceImpact.AttackStat,
|
||||||
targetImpact.DefenseStat,
|
targetImpact.DefenseStat,
|
||||||
targetImpact.DodgeStat);
|
targetImpact.DodgeStat);
|
||||||
shouldDispatchPresentation = true;
|
shouldDispatchPresentation = true;
|
||||||
|
|
@ -537,36 +537,6 @@ namespace Simulation
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int CalculateProjectileDamage(int attack, StatProperty attackStat, StatProperty defenseStat,
|
|
||||||
StatProperty dodgeStat)
|
|
||||||
{
|
|
||||||
if (dodgeStat != null)
|
|
||||||
{
|
|
||||||
if (UnityEngine.Random.value < Mathf.Clamp(dodgeStat.Percent, 0f, 0.9f))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float damage = attack;
|
|
||||||
if (attackStat != null)
|
|
||||||
{
|
|
||||||
damage = (attack + attackStat.Value) * attackStat.Percent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defenseStat != null)
|
|
||||||
{
|
|
||||||
damage = (damage - defenseStat.Value) / defenseStat.Percent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (damage < 1f)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Mathf.CeilToInt(damage);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryGetTargetableEntity(int entityId, out TargetableObject target)
|
private static bool TryGetTargetableEntity(int entityId, out TargetableObject target)
|
||||||
{
|
{
|
||||||
target = null;
|
target = null;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ namespace Simulation
|
||||||
{
|
{
|
||||||
private NativeParallelMultiHashMap<long, int> _enemyTargetBuckets;
|
private NativeParallelMultiHashMap<long, int> _enemyTargetBuckets;
|
||||||
private bool _enemyTargetBucketsDirty = true;
|
private bool _enemyTargetBucketsDirty = true;
|
||||||
private int _enemyTargetBucketsFrame = -1;
|
|
||||||
|
|
||||||
[SerializeField] private float _targetSelectionCellSize = 2f;
|
[SerializeField] private float _targetSelectionCellSize = 2f;
|
||||||
|
|
||||||
|
|
@ -85,7 +84,6 @@ namespace Simulation
|
||||||
|
|
||||||
_enemyTargetBuckets = new NativeParallelMultiHashMap<long, int>(256, Allocator.Persistent);
|
_enemyTargetBuckets = new NativeParallelMultiHashMap<long, int>(256, Allocator.Persistent);
|
||||||
_enemyTargetBucketsDirty = true;
|
_enemyTargetBucketsDirty = true;
|
||||||
_enemyTargetBucketsFrame = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisposeEnemyTargetSpatialIndex()
|
private void DisposeEnemyTargetSpatialIndex()
|
||||||
|
|
@ -97,7 +95,6 @@ namespace Simulation
|
||||||
|
|
||||||
_enemyTargetBuckets = default;
|
_enemyTargetBuckets = default;
|
||||||
_enemyTargetBucketsDirty = true;
|
_enemyTargetBucketsDirty = true;
|
||||||
_enemyTargetBucketsFrame = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearEnemyTargetSpatialIndex()
|
private void ClearEnemyTargetSpatialIndex()
|
||||||
|
|
@ -108,20 +105,18 @@ namespace Simulation
|
||||||
}
|
}
|
||||||
|
|
||||||
_enemyTargetBucketsDirty = true;
|
_enemyTargetBucketsDirty = true;
|
||||||
_enemyTargetBucketsFrame = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MarkEnemyTargetSpatialIndexDirty()
|
private void MarkEnemyTargetSpatialIndexDirty()
|
||||||
{
|
{
|
||||||
_enemyTargetBucketsDirty = true;
|
_enemyTargetBucketsDirty = true;
|
||||||
_enemyTargetBucketsFrame = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildEnemyTargetSpatialIndexIfNeeded()
|
private void BuildEnemyTargetSpatialIndexIfNeeded()
|
||||||
{
|
{
|
||||||
InitializeEnemyTargetSpatialIndex();
|
InitializeEnemyTargetSpatialIndex();
|
||||||
|
|
||||||
if (!_enemyTargetBucketsDirty && _enemyTargetBucketsFrame == Time.frameCount)
|
if (!_enemyTargetBucketsDirty)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +143,6 @@ namespace Simulation
|
||||||
}
|
}
|
||||||
|
|
||||||
_enemyTargetBucketsDirty = false;
|
_enemyTargetBucketsDirty = false;
|
||||||
_enemyTargetBucketsFrame = Time.frameCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetTargetSelectionCellSize()
|
private float GetTargetSelectionCellSize()
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
schema: spec-driven
|
|
||||||
|
|
||||||
# Project context (optional)
|
|
||||||
# This is shown to AI when creating artifacts.
|
|
||||||
# Add your tech stack, conventions, style guides, domain knowledge, etc.
|
|
||||||
# Example:
|
|
||||||
# context: |
|
|
||||||
# Tech stack: TypeScript, React, Node.js
|
|
||||||
# We use conventional commits
|
|
||||||
# Domain: e-commerce platform
|
|
||||||
|
|
||||||
# Per-artifact rules (optional)
|
|
||||||
# Add custom rules for specific artifacts.
|
|
||||||
# Example:
|
|
||||||
# rules:
|
|
||||||
# proposal:
|
|
||||||
# - Keep proposals under 500 words
|
|
||||||
# - Always include a "Non-goals" section
|
|
||||||
# tasks:
|
|
||||||
# - Break tasks into chunks of max 2 hours
|
|
||||||
Loading…
Reference in New Issue