955 lines
49 KiB
C#
955 lines
49 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using NUnit.Framework;
|
|
using UnityEngine;
|
|
using Object = UnityEngine.Object;
|
|
|
|
namespace Simulation.Tests.Editor
|
|
{
|
|
public class SimulationWorldTickTests
|
|
{
|
|
private const string GameAssemblyName = "Assembly-CSharp";
|
|
private const string RuntimeAssemblyName = "UnityGameFramework.Runtime";
|
|
private const BindingFlags PublicStatic = BindingFlags.Public | BindingFlags.Static;
|
|
private const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance;
|
|
private const BindingFlags NonPublicInstance = BindingFlags.NonPublic | BindingFlags.Instance;
|
|
private const BindingFlags NonPublicStatic = BindingFlags.NonPublic | BindingFlags.Static;
|
|
|
|
private static readonly System.Type SimulationWorldType =
|
|
System.Type.GetType($"Simulation.SimulationWorld, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type SimulationTickContextType =
|
|
System.Type.GetType($"Simulation.SimulationTickContext, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type EnemySimDataType =
|
|
System.Type.GetType($"Simulation.EnemySimData, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type ProjectileSimDataType =
|
|
System.Type.GetType($"Simulation.ProjectileSimData, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type EnemyProjectileType =
|
|
System.Type.GetType($"Entity.EnemyProjectile, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type EnemyProjectileDataType =
|
|
System.Type.GetType($"Entity.EntityData.EnemyProjectileData, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type CampTypeType =
|
|
System.Type.GetType($"Definition.Enum.CampType, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type GameEntryType =
|
|
System.Type.GetType($"GameEntry, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type EnemySeparationSolverProviderType =
|
|
System.Type.GetType($"CustomUtility.EnemySeparationSolverProvider, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type EnemyManagerComponentType =
|
|
System.Type.GetType($"CustomComponent.EnemyManagerComponent, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type PlayerType =
|
|
System.Type.GetType($"Entity.Player, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type EntityBaseType =
|
|
System.Type.GetType($"Entity.EntityBase, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type HealthComponentType =
|
|
System.Type.GetType($"Components.HealthComponent, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type EntityLogicType =
|
|
System.Type.GetType($"UnityGameFramework.Runtime.EntityLogic, {RuntimeAssemblyName}");
|
|
|
|
private static readonly System.Type ProcedureComponentType =
|
|
System.Type.GetType($"UnityGameFramework.Runtime.ProcedureComponent, {RuntimeAssemblyName}");
|
|
|
|
private static readonly System.Type ProcedureGameType =
|
|
System.Type.GetType($"Procedure.ProcedureGame, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type GameStateTypeType =
|
|
System.Type.GetType($"Procedure.GameStateType, {GameAssemblyName}");
|
|
|
|
private static readonly System.Type ProcedureManagerType =
|
|
System.Type.GetType("GameFramework.Procedure.ProcedureManager, GameFramework");
|
|
|
|
private static readonly System.Type ProcedureManagerInterfaceType =
|
|
System.Type.GetType("GameFramework.Procedure.IProcedureManager, GameFramework");
|
|
|
|
private static readonly System.Type FsmOpenGenericType =
|
|
System.Type.GetType("GameFramework.Fsm.Fsm`1, GameFramework");
|
|
|
|
private static readonly MethodInfo UpsertEnemyMethod =
|
|
SimulationWorldType?.GetMethod("UpsertEnemy", NonPublicInstance);
|
|
|
|
private static readonly MethodInfo RemoveEnemyByEntityIdMethod =
|
|
SimulationWorldType?.GetMethod("RemoveEnemyByEntityId", NonPublicInstance);
|
|
|
|
private static readonly MethodInfo UpsertProjectileMethod =
|
|
SimulationWorldType?.GetMethod("UpsertProjectile", NonPublicInstance);
|
|
|
|
private static readonly MethodInfo RemoveProjectileByEntityIdMethod =
|
|
SimulationWorldType?.GetMethod("RemoveProjectileByEntityId", NonPublicInstance);
|
|
|
|
private static readonly MethodInfo TryGetEnemyDataMethod =
|
|
SimulationWorldType?.GetMethod("TryGetEnemyData", NonPublicInstance);
|
|
|
|
private static readonly MethodInfo TickMethod =
|
|
SimulationWorldType?.GetMethod("Tick", PublicInstance);
|
|
|
|
private static readonly MethodInfo TryGetNearestEnemyEntityIdMethod =
|
|
SimulationWorldType?.GetMethod("TryGetNearestEnemyEntityId", PublicInstance);
|
|
|
|
private static readonly MethodInfo TryEnqueueAreaCollisionQueryMethod =
|
|
SimulationWorldType?.GetMethod("TryEnqueueAreaCollisionQuery", PublicInstance);
|
|
|
|
private static readonly MethodInfo SetUseSimulationMovementMethod =
|
|
SimulationWorldType?.GetMethod("SetUseSimulationMovement", PublicInstance);
|
|
|
|
private static readonly MethodInfo SetUseJobSimulationMethod =
|
|
SimulationWorldType?.GetMethod("SetUseJobSimulation", PublicInstance);
|
|
|
|
private static readonly MethodInfo SetUseBurstJobsMethod =
|
|
SimulationWorldType?.GetMethod("SetUseBurstJobs", PublicInstance);
|
|
|
|
private static readonly MethodInfo ClearMethod =
|
|
SimulationWorldType?.GetMethod("Clear", PublicInstance);
|
|
|
|
private static readonly MethodInfo UseGridBucketSolverMethod =
|
|
EnemySeparationSolverProviderType?.GetMethod("UseGridBucketSolver", PublicStatic);
|
|
|
|
private static readonly FieldInfo EntitySyncField =
|
|
SimulationWorldType?.GetField("_entitySync", NonPublicInstance);
|
|
|
|
private static readonly FieldInfo PresentationField =
|
|
SimulationWorldType?.GetField("_presentation", NonPublicInstance);
|
|
|
|
private static readonly PropertyInfo EnemiesProperty =
|
|
SimulationWorldType?.GetProperty("Enemies", PublicInstance);
|
|
|
|
private static readonly PropertyInfo ProjectilesProperty =
|
|
SimulationWorldType?.GetProperty("Projectiles", PublicInstance);
|
|
|
|
private static readonly PropertyInfo CollisionCandidateCountProperty =
|
|
SimulationWorldType?.GetProperty("CollisionCandidateCount", PublicInstance);
|
|
|
|
private static readonly PropertyInfo UseSimulationMovementProperty =
|
|
SimulationWorldType?.GetProperty("UseSimulationMovement", PublicInstance);
|
|
|
|
private static readonly PropertyInfo UseJobSimulationProperty =
|
|
SimulationWorldType?.GetProperty("UseJobSimulation", PublicInstance);
|
|
|
|
private static readonly PropertyInfo LastResolvedAreaHitCountProperty =
|
|
SimulationWorldType?.GetProperty("LastResolvedAreaHitCount", PublicInstance);
|
|
|
|
private static readonly FieldInfo CollisionQueryInputsField =
|
|
SimulationWorldType?.GetField("_collisionQueryInputs", NonPublicInstance);
|
|
|
|
private static readonly FieldInfo AreaCollisionRequestsField =
|
|
SimulationWorldType?.GetField("_areaCollisionRequests", NonPublicInstance);
|
|
|
|
private static readonly MethodInfo EnemyProjectileOnUpdateMethod =
|
|
EnemyProjectileType?.GetMethod("OnUpdate", NonPublicInstance);
|
|
|
|
private static readonly FieldInfo EnemyProjectileDataField =
|
|
EnemyProjectileType?.GetField("_projectileData", NonPublicInstance);
|
|
|
|
private static readonly FieldInfo EnemyProjectileIsActiveField =
|
|
EnemyProjectileType?.GetField("_isActive", NonPublicInstance);
|
|
|
|
private static readonly FieldInfo EnemyProjectileIsSimulationDrivenField =
|
|
EnemyProjectileType?.GetField("_isSimulationDriven", NonPublicInstance);
|
|
|
|
private static readonly PropertyInfo GameEntrySimulationWorldProperty =
|
|
GameEntryType?.GetProperty("SimulationWorld", PublicStatic);
|
|
|
|
private static readonly MethodInfo GameEntryGetSimulationWorldMethod =
|
|
GameEntrySimulationWorldProperty?.GetGetMethod(true);
|
|
|
|
private static readonly MethodInfo GameEntrySetSimulationWorldMethod =
|
|
GameEntrySimulationWorldProperty?.GetSetMethod(true);
|
|
|
|
private static readonly PropertyInfo GameEntryEnemyManagerProperty =
|
|
GameEntryType?.GetProperty("EnemyManager", PublicStatic);
|
|
|
|
private static readonly MethodInfo GameEntryGetEnemyManagerMethod =
|
|
GameEntryEnemyManagerProperty?.GetGetMethod(true);
|
|
|
|
private static readonly MethodInfo GameEntrySetEnemyManagerMethod =
|
|
GameEntryEnemyManagerProperty?.GetSetMethod(true);
|
|
|
|
private static readonly PropertyInfo GameEntryProcedureProperty =
|
|
GameEntryType?.GetProperty("Procedure", PublicStatic);
|
|
|
|
private static readonly MethodInfo GameEntryGetProcedureMethod =
|
|
GameEntryProcedureProperty?.GetGetMethod(true);
|
|
|
|
private static readonly MethodInfo GameEntrySetProcedureMethod =
|
|
GameEntryProcedureProperty?.GetSetMethod(true);
|
|
|
|
private static readonly MethodInfo HealthComponentOnInitMethod =
|
|
HealthComponentType?.GetMethod("OnInit", PublicInstance);
|
|
|
|
private static readonly FieldInfo ProjectileMaxDistanceFromPlayerField =
|
|
SimulationWorldType?.GetField("_projectileMaxDistanceFromPlayer", NonPublicInstance);
|
|
|
|
private static readonly FieldInfo ProjectileMaxVerticalOffsetFromPlayerField =
|
|
SimulationWorldType?.GetField("_projectileMaxVerticalOffsetFromPlayer", NonPublicInstance);
|
|
|
|
private GameObject _worldGameObject;
|
|
private Component _worldComponent;
|
|
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
Assert.NotNull(SimulationWorldType, "SimulationWorld type lookup failed.");
|
|
Assert.NotNull(SimulationTickContextType, "SimulationTickContext type lookup failed.");
|
|
Assert.NotNull(EnemySimDataType, "EnemySimData type lookup failed.");
|
|
Assert.NotNull(ProjectileSimDataType, "ProjectileSimData type lookup failed.");
|
|
Assert.NotNull(EnemyProjectileType, "EnemyProjectile type lookup failed.");
|
|
Assert.NotNull(EnemyProjectileDataType, "EnemyProjectileData type lookup failed.");
|
|
Assert.NotNull(CampTypeType, "CampType type lookup failed.");
|
|
Assert.NotNull(GameEntryType, "GameEntry type lookup failed.");
|
|
Assert.NotNull(EnemySeparationSolverProviderType, "EnemySeparationSolverProvider type lookup failed.");
|
|
Assert.NotNull(EnemyManagerComponentType, "EnemyManagerComponent type lookup failed.");
|
|
Assert.NotNull(PlayerType, "Player type lookup failed.");
|
|
Assert.NotNull(EntityBaseType, "EntityBase type lookup failed.");
|
|
Assert.NotNull(HealthComponentType, "HealthComponent type lookup failed.");
|
|
Assert.NotNull(EntityLogicType, "EntityLogic type lookup failed.");
|
|
Assert.NotNull(ProcedureComponentType, "ProcedureComponent type lookup failed.");
|
|
Assert.NotNull(ProcedureGameType, "ProcedureGame type lookup failed.");
|
|
Assert.NotNull(GameStateTypeType, "GameStateType type lookup failed.");
|
|
Assert.NotNull(ProcedureManagerType, "ProcedureManager type lookup failed.");
|
|
Assert.NotNull(ProcedureManagerInterfaceType, "IProcedureManager type lookup failed.");
|
|
Assert.NotNull(FsmOpenGenericType, "Fsm`1 type lookup failed.");
|
|
Assert.NotNull(UpsertEnemyMethod, "UpsertEnemy reflection lookup failed.");
|
|
Assert.NotNull(RemoveEnemyByEntityIdMethod, "RemoveEnemyByEntityId reflection lookup failed.");
|
|
Assert.NotNull(UpsertProjectileMethod, "UpsertProjectile reflection lookup failed.");
|
|
Assert.NotNull(RemoveProjectileByEntityIdMethod, "RemoveProjectileByEntityId 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(TryEnqueueAreaCollisionQueryMethod, "TryEnqueueAreaCollisionQuery reflection lookup failed.");
|
|
Assert.NotNull(SetUseSimulationMovementMethod, "SetUseSimulationMovement reflection lookup failed.");
|
|
Assert.NotNull(SetUseJobSimulationMethod, "SetUseJobSimulation reflection lookup failed.");
|
|
Assert.NotNull(SetUseBurstJobsMethod, "SetUseBurstJobs reflection lookup failed.");
|
|
Assert.NotNull(ClearMethod, "Clear reflection lookup failed.");
|
|
Assert.NotNull(UseGridBucketSolverMethod, "UseGridBucketSolver reflection lookup failed.");
|
|
Assert.NotNull(EnemiesProperty, "Enemies property reflection lookup failed.");
|
|
Assert.NotNull(ProjectilesProperty, "Projectiles property reflection lookup failed.");
|
|
Assert.NotNull(CollisionCandidateCountProperty, "CollisionCandidateCount property reflection lookup failed.");
|
|
Assert.NotNull(UseSimulationMovementProperty, "UseSimulationMovement property reflection lookup failed.");
|
|
Assert.NotNull(UseJobSimulationProperty, "UseJobSimulation property reflection lookup failed.");
|
|
Assert.NotNull(LastResolvedAreaHitCountProperty, "LastResolvedAreaHitCount property reflection lookup failed.");
|
|
Assert.NotNull(CollisionQueryInputsField, "Collision query inputs field reflection lookup failed.");
|
|
Assert.NotNull(AreaCollisionRequestsField, "Area collision requests field reflection lookup failed.");
|
|
Assert.NotNull(ProjectileMaxDistanceFromPlayerField,
|
|
"Projectile max distance field reflection lookup failed.");
|
|
Assert.NotNull(ProjectileMaxVerticalOffsetFromPlayerField,
|
|
"Projectile max vertical offset field reflection lookup failed.");
|
|
Assert.NotNull(EnemyProjectileOnUpdateMethod, "EnemyProjectile.OnUpdate reflection lookup failed.");
|
|
Assert.NotNull(EnemyProjectileDataField, "EnemyProjectile _projectileData reflection lookup failed.");
|
|
Assert.NotNull(EnemyProjectileIsActiveField, "EnemyProjectile _isActive reflection lookup failed.");
|
|
Assert.NotNull(EnemyProjectileIsSimulationDrivenField,
|
|
"EnemyProjectile _isSimulationDriven reflection lookup failed.");
|
|
Assert.NotNull(GameEntrySimulationWorldProperty, "GameEntry.SimulationWorld property lookup failed.");
|
|
Assert.NotNull(GameEntryGetSimulationWorldMethod,
|
|
"GameEntry.SimulationWorld getter reflection lookup failed.");
|
|
Assert.NotNull(GameEntrySetSimulationWorldMethod,
|
|
"GameEntry.SimulationWorld setter reflection lookup failed.");
|
|
Assert.NotNull(GameEntryEnemyManagerProperty, "GameEntry.EnemyManager property lookup failed.");
|
|
Assert.NotNull(GameEntryGetEnemyManagerMethod, "GameEntry.EnemyManager getter reflection lookup failed.");
|
|
Assert.NotNull(GameEntrySetEnemyManagerMethod, "GameEntry.EnemyManager setter reflection lookup failed.");
|
|
Assert.NotNull(GameEntryProcedureProperty, "GameEntry.Procedure property lookup failed.");
|
|
Assert.NotNull(GameEntryGetProcedureMethod, "GameEntry.Procedure getter reflection lookup failed.");
|
|
Assert.NotNull(GameEntrySetProcedureMethod, "GameEntry.Procedure setter reflection lookup failed.");
|
|
Assert.NotNull(HealthComponentOnInitMethod, "HealthComponent.OnInit reflection lookup failed.");
|
|
|
|
_worldGameObject = new GameObject("SimulationWorldTickTests");
|
|
_worldComponent = _worldGameObject.AddComponent(SimulationWorldType);
|
|
SetUseSimulationMovementMethod.Invoke(_worldComponent, new object[] { true });
|
|
UseGridBucketSolverMethod.Invoke(null, new object[] { 1f });
|
|
}
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
if (_worldComponent != null)
|
|
{
|
|
EntitySyncField?.SetValue(_worldComponent, null);
|
|
PresentationField?.SetValue(_worldComponent, null);
|
|
}
|
|
|
|
if (_worldGameObject != null)
|
|
{
|
|
Object.DestroyImmediate(_worldGameObject);
|
|
}
|
|
|
|
_worldComponent = null;
|
|
_worldGameObject = null;
|
|
}
|
|
|
|
[Test]
|
|
public void TickEnemies_ChasesPlayer_WhenOutOfAttackRange()
|
|
{
|
|
UpsertEnemy(CreateEnemy(entityId: 1001, position: Vector3.zero, speed: 2f, attackRange: 1f));
|
|
|
|
InvokeTick(deltaTime: 1f, realDeltaTime: 1f, playerPosition: new Vector3(10f, 0f, 0f));
|
|
|
|
object enemy = GetEnemyAt(0);
|
|
Assert.That((int)GetField(enemy, "State"), Is.EqualTo(1));
|
|
Vector3 position = (Vector3)GetField(enemy, "Position");
|
|
Vector3 forward = (Vector3)GetField(enemy, "Forward");
|
|
Assert.That(position.x, Is.EqualTo(2f).Within(0.0001f));
|
|
Assert.That(position.z, Is.EqualTo(0f).Within(0.0001f));
|
|
Assert.That(forward.x, Is.EqualTo(1f).Within(0.0001f));
|
|
}
|
|
|
|
[Test]
|
|
public void TickEnemies_StopsMovement_WhenInAttackRange()
|
|
{
|
|
Vector3 startPosition = Vector3.zero;
|
|
UpsertEnemy(CreateEnemy(entityId: 1002, position: startPosition, speed: 3f, attackRange: 2f));
|
|
|
|
InvokeTick(deltaTime: 1f, realDeltaTime: 1f, playerPosition: new Vector3(1f, 0f, 0f));
|
|
|
|
object enemy = GetEnemyAt(0);
|
|
Assert.That((int)GetField(enemy, "State"), Is.EqualTo(2));
|
|
Vector3 position = (Vector3)GetField(enemy, "Position");
|
|
Assert.That(position.x, Is.EqualTo(startPosition.x).Within(0.0001f));
|
|
Assert.That(position.y, Is.EqualTo(startPosition.y).Within(0.0001f));
|
|
Assert.That(position.z, Is.EqualTo(startPosition.z).Within(0.0001f));
|
|
}
|
|
|
|
[Test]
|
|
public void RemoveEnemyByEntityId_RemapIndex_ForMovedEnemy()
|
|
{
|
|
UpsertEnemy(CreateEnemy(entityId: 2001, position: new Vector3(0f, 0f, 0f), speed: 1f, attackRange: 1f));
|
|
UpsertEnemy(CreateEnemy(entityId: 2002, position: new Vector3(2f, 0f, 0f), speed: 1f, attackRange: 1f));
|
|
UpsertEnemy(CreateEnemy(entityId: 2003, position: new Vector3(4f, 0f, 0f), speed: 1f, attackRange: 1f));
|
|
|
|
bool removed = RemoveEnemyByEntityId(2002);
|
|
bool removedEntityExists = TryGetEnemyData(2002, out _);
|
|
bool movedEntityExists = TryGetEnemyData(2003, out object movedEnemy);
|
|
|
|
Assert.IsTrue(removed);
|
|
Assert.That(GetEnemiesCount(), Is.EqualTo(2));
|
|
Assert.IsFalse(removedEntityExists);
|
|
Assert.IsTrue(movedEntityExists);
|
|
Assert.That((int)GetField(movedEnemy, "EntityId"), Is.EqualTo(2003));
|
|
Assert.That((int)GetField(GetEnemyAt(1), "EntityId"), Is.EqualTo(2003));
|
|
}
|
|
|
|
[Test]
|
|
public void TickEnemies_ChasesPlayer_WhenJobSimulationChannelEnabled()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
UpsertEnemy(CreateEnemy(entityId: 1101, position: Vector3.zero, speed: 2f, attackRange: 1f));
|
|
|
|
InvokeTick(deltaTime: 1f, realDeltaTime: 1f, playerPosition: new Vector3(10f, 0f, 0f));
|
|
|
|
object enemy = GetEnemyAt(0);
|
|
Assert.That((int)GetField(enemy, "State"), Is.EqualTo(1));
|
|
Vector3 position = (Vector3)GetField(enemy, "Position");
|
|
Vector3 forward = (Vector3)GetField(enemy, "Forward");
|
|
Assert.That(position.x, Is.EqualTo(2f).Within(0.0001f));
|
|
Assert.That(position.z, Is.EqualTo(0f).Within(0.0001f));
|
|
Assert.That(forward.x, Is.EqualTo(1f).Within(0.0001f));
|
|
}
|
|
|
|
[Test]
|
|
public void TickEnemies_MatchesOutput_WhenBurstJobsToggled()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
SetUseBurstJobsMethod.Invoke(_worldComponent, new object[] { false });
|
|
UpsertEnemy(CreateEnemy(entityId: 1151, position: Vector3.zero, speed: 2f, attackRange: 1f));
|
|
InvokeTick(deltaTime: 1f, realDeltaTime: 1f, playerPosition: new Vector3(10f, 0f, 0f));
|
|
|
|
object nonBurstEnemy = GetEnemyAt(0);
|
|
int nonBurstState = (int)GetField(nonBurstEnemy, "State");
|
|
Vector3 nonBurstPosition = (Vector3)GetField(nonBurstEnemy, "Position");
|
|
Vector3 nonBurstForward = (Vector3)GetField(nonBurstEnemy, "Forward");
|
|
|
|
ClearMethod.Invoke(_worldComponent, null);
|
|
|
|
SetUseSimulationMovementMethod.Invoke(_worldComponent, new object[] { true });
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
SetUseBurstJobsMethod.Invoke(_worldComponent, new object[] { true });
|
|
UpsertEnemy(CreateEnemy(entityId: 1151, position: Vector3.zero, speed: 2f, attackRange: 1f));
|
|
InvokeTick(deltaTime: 1f, realDeltaTime: 1f, playerPosition: new Vector3(10f, 0f, 0f));
|
|
|
|
object burstEnemy = GetEnemyAt(0);
|
|
int burstState = (int)GetField(burstEnemy, "State");
|
|
Vector3 burstPosition = (Vector3)GetField(burstEnemy, "Position");
|
|
Vector3 burstForward = (Vector3)GetField(burstEnemy, "Forward");
|
|
|
|
Assert.That(burstState, Is.EqualTo(nonBurstState));
|
|
Assert.That((burstPosition - nonBurstPosition).sqrMagnitude, Is.LessThanOrEqualTo(1e-8f));
|
|
Assert.That((burstForward - nonBurstForward).sqrMagnitude, Is.LessThanOrEqualTo(1e-8f));
|
|
}
|
|
|
|
[Test]
|
|
public void TryGetNearestEnemyEntityId_SelectsNearestBucketCandidate_WhenJobSimulationEnabled()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
UpsertEnemy(CreateEnemy(entityId: 1201, position: new Vector3(1f, 0f, 0f), speed: 0f, attackRange: 1f));
|
|
UpsertEnemy(CreateEnemy(entityId: 1202, 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(1201));
|
|
}
|
|
|
|
[Test]
|
|
public void TickEnemies_SeparatesOverlappedEnemies_WhenJobSimulationEnabled()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
UpsertEnemy(CreateEnemy(entityId: 1301, position: new Vector3(0f, 0f, 0f), speed: 1f, attackRange: 0.1f,
|
|
avoidEnemyOverlap: true, enemyBodyRadius: 0.45f, separationIterations: 2));
|
|
UpsertEnemy(CreateEnemy(entityId: 1302, 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));
|
|
}
|
|
|
|
[Test]
|
|
public void TickEnemies_SeparatesOverlappedEnemies_WhenPlayerIsStaticAndInRange()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
UpsertEnemy(CreateEnemy(entityId: 1311, position: new Vector3(0f, 0f, 0f), speed: 1f, attackRange: 10f,
|
|
avoidEnemyOverlap: true, enemyBodyRadius: 0.45f, separationIterations: 3));
|
|
UpsertEnemy(CreateEnemy(entityId: 1312, 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));
|
|
}
|
|
|
|
[Test]
|
|
public void TickProjectiles_MovesAndUpdatesLifetime_WhenJobSimulationEnabled()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
UpsertProjectile(CreateProjectile(entityId: 5101, position: Vector3.zero, forward: Vector3.right,
|
|
velocity: new Vector3(2f, 0f, 0f), speed: 0f, lifeTime: 2f, age: 0f, active: true,
|
|
remainingLifetime: 2f, state: 0));
|
|
|
|
InvokeTick(deltaTime: 0.5f, realDeltaTime: 0.5f, playerPosition: Vector3.zero);
|
|
|
|
Assert.That(GetProjectilesCount(), Is.EqualTo(1));
|
|
object projectile = GetProjectileAt(0);
|
|
Vector3 position = (Vector3)GetField(projectile, "Position");
|
|
float age = (float)GetField(projectile, "Age");
|
|
float remainingLifetime = (float)GetField(projectile, "RemainingLifetime");
|
|
bool active = (bool)GetField(projectile, "Active");
|
|
|
|
Assert.That(position.x, Is.EqualTo(1f).Within(0.0001f));
|
|
Assert.That(age, Is.EqualTo(0.5f).Within(0.0001f));
|
|
Assert.That(remainingLifetime, Is.EqualTo(1.5f).Within(0.0001f));
|
|
Assert.IsTrue(active);
|
|
}
|
|
|
|
[Test]
|
|
public void TickProjectiles_ResumesFromLatestState_AfterTogglingJobSimulation()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
UpsertProjectile(CreateProjectile(entityId: 5110, position: Vector3.zero, forward: Vector3.right,
|
|
velocity: new Vector3(2f, 0f, 0f), speed: 0f, lifeTime: 5f, age: 0f, active: true,
|
|
remainingLifetime: 5f, state: 0));
|
|
|
|
InvokeTick(deltaTime: 0.5f, realDeltaTime: 0.5f, playerPosition: Vector3.zero);
|
|
object afterJobEnabled = GetProjectileAt(0);
|
|
Vector3 positionAfterJobEnabled = (Vector3)GetField(afterJobEnabled, "Position");
|
|
float ageAfterJobEnabled = (float)GetField(afterJobEnabled, "Age");
|
|
Assert.That(positionAfterJobEnabled.x, Is.EqualTo(1f).Within(0.0001f));
|
|
Assert.That(ageAfterJobEnabled, Is.EqualTo(0.5f).Within(0.0001f));
|
|
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { false });
|
|
InvokeTick(deltaTime: 0.5f, realDeltaTime: 0.5f, playerPosition: Vector3.zero);
|
|
object afterJobDisabled = GetProjectileAt(0);
|
|
Vector3 positionAfterJobDisabled = (Vector3)GetField(afterJobDisabled, "Position");
|
|
float ageAfterJobDisabled = (float)GetField(afterJobDisabled, "Age");
|
|
Assert.That(positionAfterJobDisabled.x, Is.EqualTo(1f).Within(0.0001f));
|
|
Assert.That(ageAfterJobDisabled, Is.EqualTo(0.5f).Within(0.0001f));
|
|
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
InvokeTick(deltaTime: 0.5f, realDeltaTime: 0.5f, playerPosition: Vector3.zero);
|
|
object afterJobReEnabled = GetProjectileAt(0);
|
|
Vector3 positionAfterJobReEnabled = (Vector3)GetField(afterJobReEnabled, "Position");
|
|
float ageAfterJobReEnabled = (float)GetField(afterJobReEnabled, "Age");
|
|
float remainingLifetimeAfterJobReEnabled = (float)GetField(afterJobReEnabled, "RemainingLifetime");
|
|
bool activeAfterJobReEnabled = (bool)GetField(afterJobReEnabled, "Active");
|
|
|
|
Assert.That(positionAfterJobReEnabled.x, Is.EqualTo(2f).Within(0.0001f));
|
|
Assert.That(ageAfterJobReEnabled, Is.EqualTo(1f).Within(0.0001f));
|
|
Assert.That(remainingLifetimeAfterJobReEnabled, Is.EqualTo(4f).Within(0.0001f));
|
|
Assert.IsTrue(activeAfterJobReEnabled);
|
|
}
|
|
|
|
[Test]
|
|
public void EnemyProjectile_TogglesCollider_WhenJobSimulationSwitchesAtRuntime()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { false });
|
|
|
|
GameObject projectileObject = new GameObject("EnemyProjectileColliderToggleEditMode");
|
|
try
|
|
{
|
|
Component projectileComponent = projectileObject.AddComponent(EnemyProjectileType);
|
|
Collider projectileCollider = projectileObject.AddComponent<BoxCollider>();
|
|
projectileCollider.enabled = true;
|
|
|
|
object previousSimulationWorld = GetGameEntrySimulationWorld();
|
|
SetGameEntrySimulationWorld(_worldComponent);
|
|
try
|
|
{
|
|
object neutralCamp = System.Enum.Parse(CampTypeType, "Neutral");
|
|
object projectileData = System.Activator.CreateInstance(
|
|
EnemyProjectileDataType,
|
|
BindingFlags.Public | BindingFlags.Instance,
|
|
null,
|
|
new object[] { 7001, 1, neutralCamp, 1, 0f, 10f, Vector3.forward },
|
|
null);
|
|
|
|
EnemyProjectileDataField.SetValue(projectileComponent, projectileData);
|
|
EnemyProjectileIsActiveField.SetValue(projectileComponent, true);
|
|
EnemyProjectileIsSimulationDrivenField.SetValue(projectileComponent, false);
|
|
|
|
InvokeEnemyProjectileUpdate(projectileComponent, 0.016f, 0.016f);
|
|
Assert.IsTrue(projectileCollider.enabled);
|
|
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
InvokeEnemyProjectileUpdate(projectileComponent, 0.016f, 0.016f);
|
|
Assert.IsFalse(projectileCollider.enabled);
|
|
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { false });
|
|
InvokeEnemyProjectileUpdate(projectileComponent, 0.016f, 0.016f);
|
|
Assert.IsTrue(projectileCollider.enabled);
|
|
}
|
|
finally
|
|
{
|
|
SetGameEntrySimulationWorld(previousSimulationWorld);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Object.DestroyImmediate(projectileObject);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void RemoveProjectileByEntityId_RemapIndex_ForMovedProjectile()
|
|
{
|
|
UpsertProjectile(CreateProjectile(entityId: 5105, position: new Vector3(0f, 0f, 0f),
|
|
forward: Vector3.forward, velocity: Vector3.zero, speed: 0f, lifeTime: 3f, age: 0f, active: true,
|
|
remainingLifetime: 3f, state: 0));
|
|
UpsertProjectile(CreateProjectile(entityId: 5106, position: new Vector3(1f, 0f, 0f),
|
|
forward: Vector3.forward, velocity: Vector3.zero, speed: 0f, lifeTime: 3f, age: 0f, active: true,
|
|
remainingLifetime: 3f, state: 0));
|
|
UpsertProjectile(CreateProjectile(entityId: 5107, position: new Vector3(2f, 0f, 0f),
|
|
forward: Vector3.forward, velocity: Vector3.zero, speed: 0f, lifeTime: 3f, age: 0f, active: true,
|
|
remainingLifetime: 3f, state: 0));
|
|
|
|
bool removed = RemoveProjectileByEntityId(5106);
|
|
bool removedMoved = RemoveProjectileByEntityId(5107);
|
|
|
|
Assert.IsTrue(removed);
|
|
Assert.That(GetProjectilesCount(), Is.EqualTo(1));
|
|
Assert.IsTrue(removedMoved);
|
|
Assert.That((int)GetField(GetProjectileAt(0), "EntityId"), Is.EqualTo(5105));
|
|
}
|
|
|
|
[Test]
|
|
public void TickProjectiles_RecyclesWhenExceedingPlayerDistance_WhenJobSimulationEnabled()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
ProjectileMaxDistanceFromPlayerField.SetValue(_worldComponent, 5f);
|
|
ProjectileMaxVerticalOffsetFromPlayerField.SetValue(_worldComponent, 1000f);
|
|
UpsertProjectile(CreateProjectile(entityId: 5108, position: new Vector3(6f, 0f, 0f),
|
|
forward: Vector3.forward, velocity: Vector3.zero, speed: 0f, lifeTime: 3f, age: 0f, active: true,
|
|
remainingLifetime: 3f, state: 0));
|
|
|
|
InvokeTick(deltaTime: 0.016f, realDeltaTime: 0.016f, playerPosition: Vector3.zero);
|
|
|
|
Assert.That(GetProjectilesCount(), Is.EqualTo(0));
|
|
}
|
|
|
|
[Test]
|
|
public void TickProjectiles_RecyclesWhenExceedingVerticalOffset_WhenJobSimulationEnabled()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
ProjectileMaxDistanceFromPlayerField.SetValue(_worldComponent, 0f);
|
|
ProjectileMaxVerticalOffsetFromPlayerField.SetValue(_worldComponent, 1f);
|
|
UpsertProjectile(CreateProjectile(entityId: 5109, position: new Vector3(0f, 2f, 0f),
|
|
forward: Vector3.forward, velocity: Vector3.zero, speed: 0f, lifeTime: 3f, age: 0f, active: true,
|
|
remainingLifetime: 3f, state: 0));
|
|
|
|
InvokeTick(deltaTime: 0.016f, realDeltaTime: 0.016f, playerPosition: Vector3.zero);
|
|
|
|
Assert.That(GetProjectilesCount(), Is.EqualTo(0));
|
|
}
|
|
|
|
[Test]
|
|
public void TickProjectiles_RecyclesExpiredProjectile_WhenJobSimulationEnabled()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
UpsertProjectile(CreateProjectile(entityId: 5102, position: Vector3.zero, forward: Vector3.forward,
|
|
velocity: Vector3.zero, speed: 0f, lifeTime: 1f, age: 0.95f, active: true, remainingLifetime: 0.05f,
|
|
state: 0));
|
|
|
|
InvokeTick(deltaTime: 0.1f, realDeltaTime: 0.1f, playerPosition: Vector3.zero);
|
|
|
|
Assert.That(GetProjectilesCount(), Is.EqualTo(0));
|
|
}
|
|
|
|
[Test]
|
|
public void TickProjectiles_BuildsCollisionCandidatesAgainstEnemies_WhenJobSimulationEnabled()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
UpsertEnemy(CreateEnemy(entityId: 5201, position: Vector3.zero, speed: 0f, attackRange: 1f));
|
|
UpsertProjectile(CreateProjectile(entityId: 5202, position: Vector3.zero, forward: Vector3.forward,
|
|
velocity: Vector3.zero, speed: 0f, lifeTime: 2f, age: 0f, active: true, remainingLifetime: 2f,
|
|
state: 0));
|
|
|
|
InvokeTick(deltaTime: 0.016f, realDeltaTime: 0.016f, playerPosition: Vector3.zero);
|
|
|
|
Assert.That(GetCollisionCandidateCount(), Is.GreaterThan(0));
|
|
}
|
|
|
|
[Test]
|
|
public void TickProjectiles_BuildsCollisionCandidates_WithLatestEnemyMovement_WhenJobSimulationEnabled()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
UpsertEnemy(CreateEnemy(entityId: 5211, position: new Vector3(2f, 0f, 0f), speed: 1f, attackRange: 0.1f));
|
|
UpsertProjectile(CreateProjectile(entityId: 5212, position: new Vector3(1f, 0f, 0f),
|
|
forward: Vector3.forward, velocity: Vector3.zero, speed: 0f, lifeTime: 2f, age: 0f, active: true,
|
|
remainingLifetime: 2f, state: 0));
|
|
|
|
InvokeTick(deltaTime: 1f, realDeltaTime: 1f, playerPosition: Vector3.zero);
|
|
|
|
Assert.That(GetCollisionCandidateCount(), Is.GreaterThan(0));
|
|
}
|
|
|
|
[Test]
|
|
public void TickProjectiles_ExpiresAfterCollisionCandidateConsumed_WhenJobSimulationEnabled()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
UpsertEnemy(CreateEnemy(entityId: 5203, position: Vector3.zero, speed: 0f, attackRange: 1f));
|
|
UpsertProjectile(CreateProjectile(entityId: 5204, position: Vector3.zero, forward: Vector3.forward,
|
|
velocity: Vector3.zero, speed: 0f, lifeTime: 10f, age: 0f, active: true, remainingLifetime: 10f,
|
|
state: 0));
|
|
|
|
InvokeTick(deltaTime: 0.016f, realDeltaTime: 0.016f, playerPosition: Vector3.zero);
|
|
|
|
Assert.That(GetProjectilesCount(), Is.EqualTo(0));
|
|
}
|
|
|
|
[Test]
|
|
public void TickProjectiles_LimitsCandidatesToMaxTargets_IncludingPlayerCandidate()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
object previousEnemyManager = GetGameEntryEnemyManager();
|
|
GameObject enemyManagerObject = new GameObject("EnemyManagerMaxTargetsEditMode");
|
|
GameObject playerObject = new GameObject("PlayerTargetMaxTargetsEditMode");
|
|
try
|
|
{
|
|
Component enemyManager = enemyManagerObject.AddComponent(EnemyManagerComponentType);
|
|
Component player = playerObject.AddComponent(PlayerType);
|
|
Component healthComponent = playerObject.AddComponent(HealthComponentType);
|
|
HealthComponentOnInitMethod.Invoke(healthComponent, new object[] { 100, null });
|
|
SetPrivateField(player, "_healthComponent", healthComponent);
|
|
SetPrivateField(player, "m_CachedTransform", playerObject.transform);
|
|
SetPrivateField(player, "m_Available", true);
|
|
|
|
object enemyById = Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(typeof(int), EntityBaseType));
|
|
enemyById.GetType().GetMethod("Add")?.Invoke(enemyById, new object[] { -1, player });
|
|
object enemies = Activator.CreateInstance(typeof(List<>).MakeGenericType(EntityBaseType));
|
|
enemies.GetType().GetMethod("Add")?.Invoke(enemies, new object[] { player });
|
|
SetPrivateField(enemyManager, "_enemyById", enemyById);
|
|
SetPrivateField(enemyManager, "_enemies", enemies);
|
|
SetGameEntryEnemyManager(enemyManager);
|
|
|
|
UpsertEnemy(CreateEnemy(entityId: 5221, position: Vector3.zero, speed: 0f, attackRange: 1f));
|
|
UpsertProjectile(CreateProjectile(entityId: 5222, position: Vector3.zero, forward: Vector3.forward,
|
|
velocity: Vector3.zero, speed: 0f, lifeTime: 1f, age: 0f, active: true, remainingLifetime: 1f,
|
|
state: 0));
|
|
|
|
InvokeTick(deltaTime: 0.016f, realDeltaTime: 0.016f, playerPosition: Vector3.zero);
|
|
|
|
Assert.That(GetCollisionCandidateCount(), Is.EqualTo(1));
|
|
}
|
|
finally
|
|
{
|
|
SetGameEntryEnemyManager(previousEnemyManager);
|
|
Object.DestroyImmediate(enemyManagerObject);
|
|
Object.DestroyImmediate(playerObject);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void SetUseSimulationAndJob_AreIgnored_WhenBattleStateIsActive()
|
|
{
|
|
SetUseSimulationMovementMethod.Invoke(_worldComponent, new object[] { true });
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { false });
|
|
|
|
object previousProcedure = GetGameEntryProcedure();
|
|
GameObject procedureObject = new GameObject("ProcedureGuardEditMode");
|
|
try
|
|
{
|
|
Component procedureComponent = procedureObject.AddComponent(ProcedureComponentType);
|
|
object procedureManager = Activator.CreateInstance(ProcedureManagerType);
|
|
Type fsmType = FsmOpenGenericType.MakeGenericType(ProcedureManagerInterfaceType);
|
|
object fsm = Activator.CreateInstance(fsmType);
|
|
object procedureGame = Activator.CreateInstance(ProcedureGameType);
|
|
object battleState = Enum.Parse(GameStateTypeType, "Battle");
|
|
|
|
SetPrivateField(procedureGame, "_currentGameState", battleState);
|
|
SetPrivateField(fsm, "m_CurrentState", procedureGame);
|
|
SetPrivateField(procedureManager, "m_ProcedureFsm", fsm);
|
|
SetPrivateField(procedureComponent, "m_ProcedureManager", procedureManager);
|
|
SetGameEntryProcedure(procedureComponent);
|
|
|
|
SetUseSimulationMovementMethod.Invoke(_worldComponent, new object[] { false });
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
|
|
Assert.IsTrue((bool)UseSimulationMovementProperty.GetValue(_worldComponent));
|
|
Assert.IsFalse((bool)UseJobSimulationProperty.GetValue(_worldComponent));
|
|
}
|
|
finally
|
|
{
|
|
SetGameEntryProcedure(previousProcedure);
|
|
Object.DestroyImmediate(procedureObject);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EnqueueAreaQuery_CapturesInactiveSourceSnapshot_WhenSourceEntityUnavailable()
|
|
{
|
|
SetUseJobSimulationMethod.Invoke(_worldComponent, new object[] { true });
|
|
UpsertEnemy(CreateEnemy(entityId: 5231, position: Vector3.zero, speed: 0f, attackRange: 1f));
|
|
|
|
object[] enqueueArgs = { 99999, 99999, Vector3.zero, 1f, 1 };
|
|
bool enqueueResult = (bool)TryEnqueueAreaCollisionQueryMethod.Invoke(_worldComponent, enqueueArgs);
|
|
Assert.IsTrue(enqueueResult);
|
|
|
|
object areaCollisionRequests = AreaCollisionRequestsField.GetValue(_worldComponent);
|
|
Assert.NotNull(areaCollisionRequests);
|
|
PropertyInfo requestCountProperty = areaCollisionRequests.GetType().GetProperty("Count", PublicInstance);
|
|
int requestCount = (int)requestCountProperty.GetValue(areaCollisionRequests);
|
|
Assert.That(requestCount, Is.GreaterThan(0));
|
|
|
|
PropertyInfo requestItemProperty = areaCollisionRequests.GetType().GetProperty("Item", PublicInstance);
|
|
object firstRequest = requestItemProperty.GetValue(areaCollisionRequests, new object[] { 0 });
|
|
FieldInfo requestSnapshotField =
|
|
firstRequest.GetType().GetField("SourceWasActiveAtQueryTime", PublicInstance);
|
|
Assert.NotNull(requestSnapshotField);
|
|
bool requestSnapshot = (bool)requestSnapshotField.GetValue(firstRequest);
|
|
Assert.IsFalse(requestSnapshot);
|
|
|
|
InvokeTick(deltaTime: 0.016f, realDeltaTime: 0.016f, playerPosition: Vector3.zero);
|
|
Assert.That(GetLastResolvedAreaHitCount(), Is.EqualTo(0));
|
|
}
|
|
|
|
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);
|
|
SetField(ref enemy, "Position", position);
|
|
SetField(ref enemy, "Forward", Vector3.forward);
|
|
SetField(ref enemy, "Rotation", Quaternion.identity);
|
|
SetField(ref enemy, "Speed", speed);
|
|
SetField(ref enemy, "AttackRange", attackRange);
|
|
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;
|
|
}
|
|
|
|
private object CreateProjectile(int entityId, Vector3 position, Vector3 forward, Vector3 velocity, float speed,
|
|
float lifeTime, float age, bool active, float remainingLifetime, int state)
|
|
{
|
|
object projectile = System.Activator.CreateInstance(ProjectileSimDataType);
|
|
SetField(ref projectile, "EntityId", entityId);
|
|
SetField(ref projectile, "OwnerEntityId", 0);
|
|
SetField(ref projectile, "Position", position);
|
|
SetField(ref projectile, "Forward", forward);
|
|
SetField(ref projectile, "Velocity", velocity);
|
|
SetField(ref projectile, "Speed", speed);
|
|
SetField(ref projectile, "LifeTime", lifeTime);
|
|
SetField(ref projectile, "Age", age);
|
|
SetField(ref projectile, "Active", active);
|
|
SetField(ref projectile, "RemainingLifetime", remainingLifetime);
|
|
SetField(ref projectile, "State", state);
|
|
return projectile;
|
|
}
|
|
|
|
private void InvokeTick(float deltaTime, float realDeltaTime, Vector3 playerPosition)
|
|
{
|
|
object tickContext = System.Activator.CreateInstance(
|
|
SimulationTickContextType,
|
|
BindingFlags.Public | BindingFlags.Instance,
|
|
null,
|
|
new object[] { deltaTime, realDeltaTime, playerPosition },
|
|
null);
|
|
|
|
TickMethod.Invoke(_worldComponent, new[] { tickContext });
|
|
}
|
|
|
|
private void UpsertEnemy(object enemy)
|
|
{
|
|
UpsertEnemyMethod.Invoke(_worldComponent, new[] { enemy });
|
|
}
|
|
|
|
private void UpsertProjectile(object projectile)
|
|
{
|
|
UpsertProjectileMethod.Invoke(_worldComponent, new[] { projectile });
|
|
}
|
|
|
|
private bool RemoveEnemyByEntityId(int entityId)
|
|
{
|
|
return (bool)RemoveEnemyByEntityIdMethod.Invoke(_worldComponent, new object[] { entityId });
|
|
}
|
|
|
|
private bool RemoveProjectileByEntityId(int entityId)
|
|
{
|
|
return (bool)RemoveProjectileByEntityIdMethod.Invoke(_worldComponent, new object[] { entityId });
|
|
}
|
|
|
|
private static object GetGameEntrySimulationWorld()
|
|
{
|
|
return GameEntryGetSimulationWorldMethod.Invoke(null, null);
|
|
}
|
|
|
|
private static void SetGameEntrySimulationWorld(object simulationWorld)
|
|
{
|
|
GameEntrySetSimulationWorldMethod.Invoke(null, new[] { simulationWorld });
|
|
}
|
|
|
|
private static object GetGameEntryEnemyManager()
|
|
{
|
|
return GameEntryGetEnemyManagerMethod.Invoke(null, null);
|
|
}
|
|
|
|
private static void SetGameEntryEnemyManager(object enemyManager)
|
|
{
|
|
GameEntrySetEnemyManagerMethod.Invoke(null, new[] { enemyManager });
|
|
}
|
|
|
|
private static object GetGameEntryProcedure()
|
|
{
|
|
return GameEntryGetProcedureMethod.Invoke(null, null);
|
|
}
|
|
|
|
private static void SetGameEntryProcedure(object procedureComponent)
|
|
{
|
|
GameEntrySetProcedureMethod.Invoke(null, new[] { procedureComponent });
|
|
}
|
|
|
|
private static void InvokeEnemyProjectileUpdate(Component projectileComponent, float elapseSeconds,
|
|
float realElapseSeconds)
|
|
{
|
|
EnemyProjectileOnUpdateMethod.Invoke(projectileComponent, new object[] { elapseSeconds, realElapseSeconds });
|
|
}
|
|
|
|
private bool TryGetEnemyData(int entityId, out object enemyData)
|
|
{
|
|
object boxedDefault = System.Activator.CreateInstance(EnemySimDataType);
|
|
object[] parameters = { entityId, boxedDefault };
|
|
bool result = (bool)TryGetEnemyDataMethod.Invoke(_worldComponent, parameters);
|
|
enemyData = parameters[1];
|
|
return result;
|
|
}
|
|
|
|
private object GetEnemyAt(int index)
|
|
{
|
|
object enemies = EnemiesProperty.GetValue(_worldComponent);
|
|
PropertyInfo itemProperty = enemies.GetType().GetProperty("Item", PublicInstance);
|
|
return itemProperty.GetValue(enemies, new object[] { index });
|
|
}
|
|
|
|
private int GetEnemiesCount()
|
|
{
|
|
object enemies = EnemiesProperty.GetValue(_worldComponent);
|
|
PropertyInfo countProperty = enemies.GetType().GetProperty("Count", PublicInstance);
|
|
return (int)countProperty.GetValue(enemies);
|
|
}
|
|
|
|
private object GetProjectileAt(int index)
|
|
{
|
|
object projectiles = ProjectilesProperty.GetValue(_worldComponent);
|
|
PropertyInfo itemProperty = projectiles.GetType().GetProperty("Item", PublicInstance);
|
|
return itemProperty.GetValue(projectiles, new object[] { index });
|
|
}
|
|
|
|
private int GetProjectilesCount()
|
|
{
|
|
object projectiles = ProjectilesProperty.GetValue(_worldComponent);
|
|
PropertyInfo countProperty = projectiles.GetType().GetProperty("Count", PublicInstance);
|
|
return (int)countProperty.GetValue(projectiles);
|
|
}
|
|
|
|
private int GetCollisionCandidateCount()
|
|
{
|
|
return (int)CollisionCandidateCountProperty.GetValue(_worldComponent);
|
|
}
|
|
|
|
private int GetLastResolvedAreaHitCount()
|
|
{
|
|
return (int)LastResolvedAreaHitCountProperty.GetValue(_worldComponent);
|
|
}
|
|
|
|
private static object GetField(object target, string fieldName)
|
|
{
|
|
FieldInfo field = target.GetType().GetField(fieldName, PublicInstance);
|
|
return field.GetValue(target);
|
|
}
|
|
|
|
private static void SetField(ref object target, string fieldName, object value)
|
|
{
|
|
FieldInfo field = target.GetType().GetField(fieldName, PublicInstance);
|
|
field.SetValue(target, value);
|
|
}
|
|
|
|
private static void SetPrivateField(object target, string fieldName, object value)
|
|
{
|
|
Type type = target.GetType();
|
|
while (type != null)
|
|
{
|
|
FieldInfo field = type.GetField(fieldName, NonPublicInstance);
|
|
if (field != null)
|
|
{
|
|
field.SetValue(target, value);
|
|
return;
|
|
}
|
|
|
|
type = type.BaseType;
|
|
}
|
|
|
|
Assert.Fail($"Field '{fieldName}' was not found on type '{target.GetType().FullName}'.");
|
|
}
|
|
}
|
|
}
|