Checkpoint 3 fix

修复将互斥算法由同步转成异步产生的抖动问题
This commit is contained in:
SepComet
2026-02-22 14:11:53 +08:00
parent 34b1424bb6
commit 5fb7ea499f
14 changed files with 983 additions and 44 deletions
@@ -37,6 +37,9 @@ namespace Simulation.Tests.PlayMode
private static readonly MethodInfo TickMethod =
SimulationWorldType?.GetMethod("Tick", PublicInstance);
private static readonly MethodInfo TryGetNearestEnemyEntityIdMethod =
SimulationWorldType?.GetMethod("TryGetNearestEnemyEntityId", PublicInstance);
private static readonly MethodInfo SetUseSimulationMovementMethod =
SimulationWorldType?.GetMethod("SetUseSimulationMovement", PublicInstance);
@@ -69,6 +72,7 @@ namespace Simulation.Tests.PlayMode
Assert.NotNull(RemoveEnemyByEntityIdMethod, "RemoveEnemyByEntityId reflection lookup failed.");
Assert.NotNull(TryGetEnemyDataMethod, "TryGetEnemyData reflection lookup failed.");
Assert.NotNull(TickMethod, "Tick reflection lookup failed.");
Assert.NotNull(TryGetNearestEnemyEntityIdMethod, "TryGetNearestEnemyEntityId reflection lookup failed.");
Assert.NotNull(SetUseSimulationMovementMethod, "SetUseSimulationMovement reflection lookup failed.");
Assert.NotNull(SetUseJobSimulationMethod, "SetUseJobSimulation reflection lookup failed.");
Assert.NotNull(UseGridBucketSolverMethod, "UseGridBucketSolver reflection lookup failed.");
@@ -177,7 +181,73 @@ namespace Simulation.Tests.PlayMode
yield break;
}
private object CreateEnemy(int entityId, Vector3 position, float speed, float attackRange)
[UnityTest]
public IEnumerator TryGetNearestEnemyEntityId_SelectsNearestBucketCandidate_WhenJobSimulationEnabled()
{
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
UpsertEnemy(CreateEnemy(entityId: 3301, position: new Vector3(1f, 0f, 0f), speed: 0f, attackRange: 1f));
UpsertEnemy(CreateEnemy(entityId: 3302, position: new Vector3(6f, 0f, 0f), speed: 0f, attackRange: 1f));
InvokeTick(deltaTime: 0.016f, realDeltaTime: 0.016f, playerPosition: Vector3.zero);
object[] parameters = { Vector3.zero, 100f, 0 };
bool found = (bool)TryGetNearestEnemyEntityIdMethod.Invoke(_worldComponent, parameters);
int nearestEntityId = (int)parameters[2];
Assert.IsTrue(found);
Assert.That(nearestEntityId, Is.EqualTo(3301));
yield break;
}
[UnityTest]
public IEnumerator TickEnemies_SeparatesOverlappedEnemies_WhenJobSimulationEnabled()
{
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
UpsertEnemy(CreateEnemy(entityId: 3401, position: new Vector3(0f, 0f, 0f), speed: 1f, attackRange: 0.1f,
avoidEnemyOverlap: true, enemyBodyRadius: 0.45f, separationIterations: 2));
UpsertEnemy(CreateEnemy(entityId: 3402, position: new Vector3(0.1f, 0f, 0f), speed: 1f, attackRange: 0.1f,
avoidEnemyOverlap: true, enemyBodyRadius: 0.45f, separationIterations: 2));
InvokeTick(deltaTime: 0.1f, realDeltaTime: 0.1f, playerPosition: new Vector3(10f, 0f, 0f));
object enemyA = GetEnemyAt(0);
object enemyB = GetEnemyAt(1);
Vector3 posA = (Vector3)GetField(enemyA, "Position");
Vector3 posB = (Vector3)GetField(enemyB, "Position");
posA.y = 0f;
posB.y = 0f;
float distance = Vector3.Distance(posA, posB);
Assert.That(distance, Is.GreaterThanOrEqualTo(0.89f));
yield break;
}
[UnityTest]
public IEnumerator TickEnemies_SeparatesOverlappedEnemies_WhenPlayerIsStaticAndInRange()
{
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
UpsertEnemy(CreateEnemy(entityId: 3411, position: new Vector3(0f, 0f, 0f), speed: 1f, attackRange: 10f,
avoidEnemyOverlap: true, enemyBodyRadius: 0.45f, separationIterations: 3));
UpsertEnemy(CreateEnemy(entityId: 3412, position: new Vector3(0.05f, 0f, 0f), speed: 1f, attackRange: 10f,
avoidEnemyOverlap: true, enemyBodyRadius: 0.45f, separationIterations: 3));
InvokeTick(deltaTime: 0.1f, realDeltaTime: 0.1f, playerPosition: Vector3.zero);
object enemyA = GetEnemyAt(0);
object enemyB = GetEnemyAt(1);
Assert.That((int)GetField(enemyA, "State"), Is.EqualTo(2));
Assert.That((int)GetField(enemyB, "State"), Is.EqualTo(2));
Vector3 posA = (Vector3)GetField(enemyA, "Position");
Vector3 posB = (Vector3)GetField(enemyB, "Position");
posA.y = 0f;
posB.y = 0f;
float distance = Vector3.Distance(posA, posB);
Assert.That(distance, Is.GreaterThanOrEqualTo(0.5f));
yield break;
}
private object CreateEnemy(int entityId, Vector3 position, float speed, float attackRange,
bool avoidEnemyOverlap = false, float enemyBodyRadius = 0.45f, int separationIterations = 1)
{
object enemy = System.Activator.CreateInstance(EnemySimDataType);
SetField(ref enemy, "EntityId", entityId);
@@ -186,9 +256,9 @@ namespace Simulation.Tests.PlayMode
SetField(ref enemy, "Rotation", Quaternion.identity);
SetField(ref enemy, "Speed", speed);
SetField(ref enemy, "AttackRange", attackRange);
SetField(ref enemy, "AvoidEnemyOverlap", false);
SetField(ref enemy, "EnemyBodyRadius", 0.45f);
SetField(ref enemy, "SeparationIterations", 1);
SetField(ref enemy, "AvoidEnemyOverlap", avoidEnemyOverlap);
SetField(ref enemy, "EnemyBodyRadius", enemyBodyRadius);
SetField(ref enemy, "SeparationIterations", separationIterations);
SetField(ref enemy, "TargetType", 0);
SetField(ref enemy, "State", 0);
return enemy;