feat(scripting): add script add-component api

This commit is contained in:
2026-03-27 15:32:37 +08:00
parent 9c94adb4a2
commit f0d6d4f41c
10 changed files with 401 additions and 6 deletions

View File

@@ -393,6 +393,175 @@ TEST_F(MonoScriptRuntimeTest, ManagedMeshComponentWrappersReadAndWritePathsAndFl
EXPECT_EQ(meshRenderer->GetRenderLayer(), 11u);
}
TEST_F(MonoScriptRuntimeTest, ManagedMeshRendererWrapperHandlesClearAndBoundaryCases) {
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
GameObject* host = runtimeScene->CreateGameObject("Host");
MeshRendererComponent* meshRenderer = host->AddComponent<MeshRendererComponent>();
ScriptComponent* component = AddScript(host, "Gameplay", "MeshRendererEdgeCaseProbe");
meshRenderer->SetMaterialPath(0, "Materials/initial0.mat");
meshRenderer->SetMaterialPath(1, "Materials/initial1.mat");
meshRenderer->SetCastShadows(false);
meshRenderer->SetReceiveShadows(true);
meshRenderer->SetRenderLayer(9);
engine->OnRuntimeStart(runtimeScene);
engine->OnUpdate(0.016f);
int32_t observedInitialMaterialCount = 0;
std::string observedNegativeIndexPath;
std::string observedOutOfRangePathBeforeClear;
std::string observedMaterial0PathAfterNegativeWrite;
std::string observedMaterial1PathAfterNegativeWrite;
int32_t observedMaterialCountAfterNegativeWrite = 0;
int32_t observedRenderLayerAfterNegativeWrite = -1;
int32_t observedMaterialCountAfterClear = -1;
std::string observedMaterial0PathAfterClear;
std::string observedMaterial3PathAfterClear;
bool observedCastShadowsAfterClear = true;
bool observedReceiveShadowsAfterClear = false;
int32_t observedRenderLayerAfterClear = -1;
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialMaterialCount", observedInitialMaterialCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedNegativeIndexPath", observedNegativeIndexPath));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedOutOfRangePathBeforeClear", observedOutOfRangePathBeforeClear));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial0PathAfterNegativeWrite", observedMaterial0PathAfterNegativeWrite));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial1PathAfterNegativeWrite", observedMaterial1PathAfterNegativeWrite));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterialCountAfterNegativeWrite", observedMaterialCountAfterNegativeWrite));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayerAfterNegativeWrite", observedRenderLayerAfterNegativeWrite));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterialCountAfterClear", observedMaterialCountAfterClear));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial0PathAfterClear", observedMaterial0PathAfterClear));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial3PathAfterClear", observedMaterial3PathAfterClear));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCastShadowsAfterClear", observedCastShadowsAfterClear));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedReceiveShadowsAfterClear", observedReceiveShadowsAfterClear));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayerAfterClear", observedRenderLayerAfterClear));
EXPECT_EQ(observedInitialMaterialCount, 2);
EXPECT_EQ(observedNegativeIndexPath, "");
EXPECT_EQ(observedOutOfRangePathBeforeClear, "");
EXPECT_EQ(observedMaterial0PathAfterNegativeWrite, "Materials/initial0.mat");
EXPECT_EQ(observedMaterial1PathAfterNegativeWrite, "Materials/initial1.mat");
EXPECT_EQ(observedMaterialCountAfterNegativeWrite, 2);
EXPECT_EQ(observedRenderLayerAfterNegativeWrite, 0);
EXPECT_EQ(observedMaterialCountAfterClear, 0);
EXPECT_EQ(observedMaterial0PathAfterClear, "");
EXPECT_EQ(observedMaterial3PathAfterClear, "");
EXPECT_FALSE(observedCastShadowsAfterClear);
EXPECT_TRUE(observedReceiveShadowsAfterClear);
EXPECT_EQ(observedRenderLayerAfterClear, 0);
EXPECT_EQ(meshRenderer->GetMaterialCount(), 0u);
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "");
EXPECT_EQ(meshRenderer->GetMaterialPath(3), "");
EXPECT_FALSE(meshRenderer->GetCastShadows());
EXPECT_TRUE(meshRenderer->GetReceiveShadows());
EXPECT_EQ(meshRenderer->GetRenderLayer(), 0u);
}
TEST_F(MonoScriptRuntimeTest, GameObjectAddComponentApiCreatesBuiltinComponentsAndAvoidsDuplicates) {
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
GameObject* host = runtimeScene->CreateGameObject("Host");
ScriptComponent* component = AddScript(host, "Gameplay", "AddComponentProbe");
engine->OnRuntimeStart(runtimeScene);
engine->OnUpdate(0.016f);
bool initialHasCamera = true;
bool initialHasLight = true;
bool initialHasMeshFilter = true;
bool initialHasMeshRenderer = true;
bool addedTransform = false;
bool addedCamera = false;
bool addedLight = false;
bool addedMeshFilter = false;
bool addedMeshRenderer = false;
bool hasCameraAfterAdd = false;
bool hasLightAfterAdd = false;
bool hasMeshFilterAfterAdd = false;
bool hasMeshRendererAfterAdd = false;
bool cameraLookupSucceeded = false;
bool lightLookupSucceeded = false;
bool meshFilterLookupSucceeded = false;
bool meshRendererLookupSucceeded = false;
float observedCameraFieldOfView = 0.0f;
float observedLightIntensity = 0.0f;
std::string observedMeshPath;
int32_t observedMaterialCount = 0;
std::string observedMaterial0Path;
int32_t observedRenderLayer = 0;
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasCamera", initialHasCamera));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasLight", initialHasLight));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasMeshFilter", initialHasMeshFilter));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasMeshRenderer", initialHasMeshRenderer));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedTransform", addedTransform));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedCamera", addedCamera));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedLight", addedLight));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedMeshFilter", addedMeshFilter));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedMeshRenderer", addedMeshRenderer));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasCameraAfterAdd", hasCameraAfterAdd));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasLightAfterAdd", hasLightAfterAdd));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasMeshFilterAfterAdd", hasMeshFilterAfterAdd));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasMeshRendererAfterAdd", hasMeshRendererAfterAdd));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CameraLookupSucceeded", cameraLookupSucceeded));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LightLookupSucceeded", lightLookupSucceeded));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshFilterLookupSucceeded", meshFilterLookupSucceeded));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshRendererLookupSucceeded", meshRendererLookupSucceeded));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCameraFieldOfView", observedCameraFieldOfView));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLightIntensity", observedLightIntensity));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMeshPath", observedMeshPath));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterialCount", observedMaterialCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial0Path", observedMaterial0Path));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayer", observedRenderLayer));
EXPECT_FALSE(initialHasCamera);
EXPECT_FALSE(initialHasLight);
EXPECT_FALSE(initialHasMeshFilter);
EXPECT_FALSE(initialHasMeshRenderer);
EXPECT_TRUE(addedTransform);
EXPECT_TRUE(addedCamera);
EXPECT_TRUE(addedLight);
EXPECT_TRUE(addedMeshFilter);
EXPECT_TRUE(addedMeshRenderer);
EXPECT_TRUE(hasCameraAfterAdd);
EXPECT_TRUE(hasLightAfterAdd);
EXPECT_TRUE(hasMeshFilterAfterAdd);
EXPECT_TRUE(hasMeshRendererAfterAdd);
EXPECT_TRUE(cameraLookupSucceeded);
EXPECT_TRUE(lightLookupSucceeded);
EXPECT_TRUE(meshFilterLookupSucceeded);
EXPECT_TRUE(meshRendererLookupSucceeded);
EXPECT_FLOAT_EQ(observedCameraFieldOfView, 82.0f);
EXPECT_FLOAT_EQ(observedLightIntensity, 4.5f);
EXPECT_EQ(observedMeshPath, "Meshes/added.mesh");
EXPECT_EQ(observedMaterialCount, 1);
EXPECT_EQ(observedMaterial0Path, "Materials/added.mat");
EXPECT_EQ(observedRenderLayer, 6);
ASSERT_NE(host->GetTransform(), nullptr);
EXPECT_EQ(host->GetComponents<CameraComponent>().size(), 1u);
EXPECT_EQ(host->GetComponents<LightComponent>().size(), 1u);
EXPECT_EQ(host->GetComponents<MeshFilterComponent>().size(), 1u);
EXPECT_EQ(host->GetComponents<MeshRendererComponent>().size(), 1u);
CameraComponent* camera = host->GetComponent<CameraComponent>();
LightComponent* light = host->GetComponent<LightComponent>();
MeshFilterComponent* meshFilter = host->GetComponent<MeshFilterComponent>();
MeshRendererComponent* meshRenderer = host->GetComponent<MeshRendererComponent>();
ASSERT_NE(camera, nullptr);
ASSERT_NE(light, nullptr);
ASSERT_NE(meshFilter, nullptr);
ASSERT_NE(meshRenderer, nullptr);
EXPECT_FLOAT_EQ(camera->GetFieldOfView(), 82.0f);
EXPECT_FLOAT_EQ(light->GetIntensity(), 4.5f);
EXPECT_EQ(meshFilter->GetMeshPath(), "Meshes/added.mesh");
ASSERT_EQ(meshRenderer->GetMaterialCount(), 1u);
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "Materials/added.mat");
EXPECT_EQ(meshRenderer->GetRenderLayer(), 6u);
}
TEST_F(MonoScriptRuntimeTest, TransformHierarchyApiExposesParentChildAndReparenting) {
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
GameObject* root = runtimeScene->CreateGameObject("Root");