feat(physics): dispatch PhysX simulation events to scene scripts

This commit is contained in:
2026-04-15 13:36:39 +08:00
parent 077786e4c7
commit 51aea47c83
20 changed files with 955 additions and 7 deletions

View File

@@ -1,18 +1,23 @@
#include <gtest/gtest.h>
#include <XCEngine/Components/BoxColliderComponent.h>
#include <XCEngine/Components/CameraComponent.h>
#include <XCEngine/Components/LightComponent.h>
#include <XCEngine/Components/MeshFilterComponent.h>
#include <XCEngine/Components/MeshRendererComponent.h>
#include <XCEngine/Components/RigidbodyComponent.h>
#include <XCEngine/Components/SphereColliderComponent.h>
#include <XCEngine/Debug/ILogSink.h>
#include <XCEngine/Debug/Logger.h>
#include <XCEngine/Core/Math/Vector2.h>
#include <XCEngine/Core/Math/Vector3.h>
#include <XCEngine/Core/Math/Vector4.h>
#include <XCEngine/Input/InputManager.h>
#include <XCEngine/Physics/PhysicsWorld.h>
#include <XCEngine/Input/InputTypes.h>
#include <XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h>
#include <XCEngine/Scene/Scene.h>
#include <XCEngine/Scene/SceneRuntime.h>
#include <XCEngine/Scripting/Mono/MonoScriptRuntime.h>
#include <XCEngine/Scripting/ScriptComponent.h>
#include <XCEngine/Scripting/ScriptEngine.h>
@@ -486,6 +491,107 @@ TEST_F(MonoScriptRuntimeTest, TimeFixedDeltaTimeUsesConfiguredRuntimeStepAcrossF
EXPECT_FLOAT_EQ(observedUpdateDeltaTime, 0.016f);
}
TEST_F(MonoScriptRuntimeTest, SceneRuntimeDispatchesManagedTriggerMessagesWithGameObjectArgumentsAndFallbacks) {
if (!XCEngine::Physics::PhysicsWorld::IsPhysXAvailable()) {
GTEST_SKIP() << "PhysX is not available in this configuration.";
}
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
GameObject* mover = runtimeScene->CreateGameObject("Mover");
mover->AddComponent<SphereColliderComponent>();
RigidbodyComponent* rigidbody = mover->AddComponent<RigidbodyComponent>();
rigidbody->SetUseGravity(false);
ScriptComponent* component = AddScript(mover, "Gameplay", "PhysicsEventProbe");
mover->GetTransform()->SetPosition(XCEngine::Math::Vector3(-3.0f, 0.0f, 0.0f));
GameObject* triggerZone = runtimeScene->CreateGameObject("TriggerZone");
BoxColliderComponent* triggerCollider = triggerZone->AddComponent<BoxColliderComponent>();
triggerCollider->SetSize(XCEngine::Math::Vector3(2.0f, 2.0f, 2.0f));
triggerCollider->SetTrigger(true);
SceneRuntime sceneRuntime;
sceneRuntime.Start(runtimeScene);
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3(150.0f, 0.0f, 0.0f));
sceneRuntime.FixedUpdate(0.02f);
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3::Zero());
sceneRuntime.FixedUpdate(0.02f);
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3(150.0f, 0.0f, 0.0f));
sceneRuntime.FixedUpdate(0.02f);
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3::Zero());
sceneRuntime.FixedUpdate(0.02f);
int32_t triggerEnterCount = 0;
int32_t triggerStayCount = 0;
int32_t triggerExitCount = 0;
bool triggerStayFallbackInvoked = false;
std::string lastTriggerOtherName;
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TriggerEnterCount", triggerEnterCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TriggerStayCount", triggerStayCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TriggerExitCount", triggerExitCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TriggerStayFallbackInvoked", triggerStayFallbackInvoked));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LastTriggerOtherName", lastTriggerOtherName));
EXPECT_EQ(triggerEnterCount, 1);
EXPECT_EQ(triggerStayCount, 2);
EXPECT_EQ(triggerExitCount, 1);
EXPECT_TRUE(triggerStayFallbackInvoked);
EXPECT_EQ(lastTriggerOtherName, "TriggerZone");
sceneRuntime.Stop();
}
TEST_F(MonoScriptRuntimeTest, SceneRuntimeDispatchesManagedCollisionMessagesWithGameObjectArgumentsAndFallbacks) {
if (!XCEngine::Physics::PhysicsWorld::IsPhysXAvailable()) {
GTEST_SKIP() << "PhysX is not available in this configuration.";
}
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
GameObject* mover = runtimeScene->CreateGameObject("Mover");
mover->AddComponent<SphereColliderComponent>();
RigidbodyComponent* rigidbody = mover->AddComponent<RigidbodyComponent>();
rigidbody->SetUseGravity(false);
ScriptComponent* component = AddScript(mover, "Gameplay", "PhysicsEventProbe");
mover->GetTransform()->SetPosition(XCEngine::Math::Vector3(-3.0f, 0.0f, 0.0f));
GameObject* wall = runtimeScene->CreateGameObject("Wall");
BoxColliderComponent* wallCollider = wall->AddComponent<BoxColliderComponent>();
wallCollider->SetSize(XCEngine::Math::Vector3(2.0f, 2.0f, 2.0f));
SceneRuntime sceneRuntime;
sceneRuntime.Start(runtimeScene);
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3(150.0f, 0.0f, 0.0f));
sceneRuntime.FixedUpdate(0.02f);
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3::Zero());
sceneRuntime.FixedUpdate(0.02f);
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3(-150.0f, 0.0f, 0.0f));
sceneRuntime.FixedUpdate(0.02f);
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3::Zero());
sceneRuntime.FixedUpdate(0.02f);
int32_t collisionEnterCount = 0;
int32_t collisionStayCount = 0;
int32_t collisionExitCount = 0;
bool collisionStayFallbackInvoked = false;
std::string lastCollisionOtherName;
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CollisionEnterCount", collisionEnterCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CollisionStayCount", collisionStayCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CollisionExitCount", collisionExitCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CollisionStayFallbackInvoked", collisionStayFallbackInvoked));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LastCollisionOtherName", lastCollisionOtherName));
EXPECT_EQ(collisionEnterCount, 1);
EXPECT_EQ(collisionStayCount, 2);
EXPECT_EQ(collisionExitCount, 1);
EXPECT_TRUE(collisionStayFallbackInvoked);
EXPECT_EQ(lastCollisionOtherName, "Wall");
sceneRuntime.Stop();
}
TEST_F(MonoScriptRuntimeTest, ManagedInputApiReadsCurrentNativeInputManagerState) {
XCEngine::Input::InputManager& inputManager = XCEngine::Input::InputManager::Get();
inputManager.Initialize(nullptr);