feat(physics): sync runtime physx body state

This commit is contained in:
2026-04-15 12:47:40 +08:00
parent 7cbc992bd8
commit c5dcfaedd5
3 changed files with 176 additions and 10 deletions

View File

@@ -280,4 +280,122 @@ TEST(PhysicsWorld_Test, RaycastCanHitTriggerCollider) {
EXPECT_NEAR(hit.normal.z, -1.0f, 0.02f);
}
TEST(PhysicsWorld_Test, RuntimeColliderGeometryChangesUpdateRaycast) {
using namespace XCEngine::Components;
Scene scene("PhysicsScene");
GameObject* target = scene.CreateGameObject("Target");
BoxColliderComponent* boxCollider = target->AddComponent<BoxColliderComponent>();
boxCollider->SetSize(XCEngine::Math::Vector3(1.0f, 1.0f, 1.0f));
XCEngine::Physics::PhysicsWorld world;
XCEngine::Physics::PhysicsWorldCreateInfo createInfo = {};
createInfo.scene = &scene;
const bool expectedInitialized = XCEngine::Physics::PhysicsWorld::IsPhysXAvailable();
ASSERT_EQ(world.Initialize(createInfo), expectedInitialized);
XCEngine::Physics::RaycastHit hit;
EXPECT_FALSE(world.Raycast(
XCEngine::Math::Vector3(0.0f, 1.5f, -5.0f),
XCEngine::Math::Vector3::Forward(),
10.0f,
hit));
boxCollider->SetSize(XCEngine::Math::Vector3(1.0f, 4.0f, 1.0f));
const bool result = world.Raycast(
XCEngine::Math::Vector3(0.0f, 1.5f, -5.0f),
XCEngine::Math::Vector3::Forward(),
10.0f,
hit);
if (!expectedInitialized) {
EXPECT_FALSE(result);
EXPECT_EQ(hit.gameObject, nullptr);
return;
}
ASSERT_TRUE(result);
EXPECT_EQ(hit.gameObject, target);
EXPECT_NEAR(hit.distance, 4.5f, 0.02f);
}
TEST(PhysicsWorld_Test, RuntimeTriggerFlagChangesUpdateRaycastHit) {
using namespace XCEngine::Components;
Scene scene("PhysicsScene");
GameObject* target = scene.CreateGameObject("Target");
SphereColliderComponent* sphereCollider = target->AddComponent<SphereColliderComponent>();
sphereCollider->SetRadius(1.0f);
XCEngine::Physics::PhysicsWorld world;
XCEngine::Physics::PhysicsWorldCreateInfo createInfo = {};
createInfo.scene = &scene;
const bool expectedInitialized = XCEngine::Physics::PhysicsWorld::IsPhysXAvailable();
ASSERT_EQ(world.Initialize(createInfo), expectedInitialized);
sphereCollider->SetTrigger(true);
XCEngine::Physics::RaycastHit hit;
const bool result = world.Raycast(
XCEngine::Math::Vector3(0.0f, 0.0f, -5.0f),
XCEngine::Math::Vector3::Forward(),
10.0f,
hit);
if (!expectedInitialized) {
EXPECT_FALSE(result);
EXPECT_EQ(hit.gameObject, nullptr);
return;
}
ASSERT_TRUE(result);
EXPECT_EQ(hit.gameObject, target);
EXPECT_TRUE(hit.isTrigger);
}
TEST(PhysicsWorld_Test, RuntimeBodyTypeChangesRebuildActorAndEnableSimulation) {
using namespace XCEngine::Components;
if (!XCEngine::Physics::PhysicsWorld::IsPhysXAvailable()) {
GTEST_SKIP() << "PhysX backend is not available in this build.";
}
Scene scene("PhysicsScene");
GameObject* ground = scene.CreateGameObject("Ground");
ground->GetTransform()->SetPosition(XCEngine::Math::Vector3(0.0f, -0.5f, 0.0f));
BoxColliderComponent* groundCollider = ground->AddComponent<BoxColliderComponent>();
groundCollider->SetSize(XCEngine::Math::Vector3(20.0f, 1.0f, 20.0f));
GameObject* body = scene.CreateGameObject("Body");
body->GetTransform()->SetPosition(XCEngine::Math::Vector3(0.0f, 4.0f, 0.0f));
RigidbodyComponent* rigidbody = body->AddComponent<RigidbodyComponent>();
rigidbody->SetBodyType(XCEngine::Physics::PhysicsBodyType::Static);
SphereColliderComponent* sphereCollider = body->AddComponent<SphereColliderComponent>();
sphereCollider->SetRadius(0.5f);
XCEngine::Physics::PhysicsWorld world;
XCEngine::Physics::PhysicsWorldCreateInfo createInfo = {};
createInfo.scene = &scene;
ASSERT_TRUE(world.Initialize(createInfo));
ASSERT_EQ(world.GetNativeActorCount(), 2u);
const float initialY = body->GetTransform()->GetPosition().y;
world.Step(1.0f / 60.0f);
EXPECT_NEAR(body->GetTransform()->GetPosition().y, initialY, 0.001f);
rigidbody->SetBodyType(XCEngine::Physics::PhysicsBodyType::Dynamic);
world.Step(1.0f / 60.0f);
EXPECT_EQ(world.GetNativeActorCount(), 2u);
for (int index = 0; index < 30; ++index) {
world.Step(1.0f / 60.0f);
}
EXPECT_LT(body->GetTransform()->GetPosition().y, initialY - 0.1f);
}
} // namespace