feat(scripting): support managed script component api

This commit is contained in:
2026-03-28 00:43:13 +08:00
parent 6bde199393
commit e522bad582
8 changed files with 349 additions and 37 deletions

View File

@@ -28,6 +28,24 @@ void ExpectVector3Near(const XCEngine::Math::Vector3& actual, const XCEngine::Ma
EXPECT_NEAR(actual.z, expected.z, tolerance);
}
ScriptComponent* FindScriptComponentByClass(GameObject* gameObject, const std::string& namespaceName, const std::string& className) {
if (!gameObject) {
return nullptr;
}
for (ScriptComponent* component : gameObject->GetComponents<ScriptComponent>()) {
if (!component) {
continue;
}
if (component->GetNamespaceName() == namespaceName && component->GetClassName() == className) {
return component;
}
}
return nullptr;
}
MonoScriptRuntime::Settings CreateMonoSettings() {
MonoScriptRuntime::Settings settings;
settings.assemblyDirectory = XCENGINE_TEST_MANAGED_OUTPUT_DIR;
@@ -562,6 +580,81 @@ TEST_F(MonoScriptRuntimeTest, GameObjectAddComponentApiCreatesBuiltinComponentsA
EXPECT_EQ(meshRenderer->GetRenderLayer(), 6u);
}
TEST_F(MonoScriptRuntimeTest, GameObjectComponentApiSupportsManagedScriptTypes) {
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
GameObject* host = runtimeScene->CreateGameObject("Host");
ScriptComponent* component = AddScript(host, "Gameplay", "ScriptComponentApiProbe");
engine->OnRuntimeStart(runtimeScene);
engine->OnUpdate(0.016f);
bool initialHasTarget = true;
bool initialLookupReturnedNull = false;
bool addedTarget = false;
bool hasTargetAfterAdd = false;
bool lookupSucceededAfterAdd = false;
bool tryGetSucceededAfterAdd = false;
bool returnedSameInstance = false;
bool targetEnabledAfterAdd = false;
int32_t observedTargetAwakeCount = 0;
int32_t observedTargetStartCount = -1;
int32_t observedTargetHostCallCount = 0;
std::string observedTargetHostName;
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasTarget", initialHasTarget));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialLookupReturnedNull", initialLookupReturnedNull));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedTarget", addedTarget));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasTargetAfterAdd", hasTargetAfterAdd));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LookupSucceededAfterAdd", lookupSucceededAfterAdd));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TryGetSucceededAfterAdd", tryGetSucceededAfterAdd));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ReturnedSameInstance", returnedSameInstance));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetEnabledAfterAdd", targetEnabledAfterAdd));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetAwakeCount", observedTargetAwakeCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetStartCount", observedTargetStartCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetHostCallCount", observedTargetHostCallCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetHostName", observedTargetHostName));
EXPECT_FALSE(initialHasTarget);
EXPECT_TRUE(initialLookupReturnedNull);
EXPECT_TRUE(addedTarget);
EXPECT_TRUE(hasTargetAfterAdd);
EXPECT_TRUE(lookupSucceededAfterAdd);
EXPECT_TRUE(tryGetSucceededAfterAdd);
EXPECT_TRUE(returnedSameInstance);
EXPECT_TRUE(targetEnabledAfterAdd);
EXPECT_EQ(observedTargetAwakeCount, 1);
EXPECT_EQ(observedTargetStartCount, 0);
EXPECT_EQ(observedTargetHostCallCount, 1);
EXPECT_EQ(observedTargetHostName, "Host");
ASSERT_EQ(host->GetComponents<ScriptComponent>().size(), 2u);
ScriptComponent* targetScript = FindScriptComponentByClass(host, "Gameplay", "ScriptComponentTargetProbe");
ASSERT_NE(targetScript, nullptr);
EXPECT_TRUE(runtime->HasManagedInstance(targetScript));
EXPECT_EQ(runtime->GetManagedInstanceCount(), 2u);
int32_t awakeCount = 0;
int32_t startCount = -1;
int32_t hostCallCount = 0;
std::string hostName;
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "AwakeCount", awakeCount));
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "StartCount", startCount));
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "HostCallCount", hostCallCount));
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "HostName", hostName));
EXPECT_EQ(awakeCount, 1);
EXPECT_EQ(startCount, 0);
EXPECT_EQ(hostCallCount, 1);
EXPECT_EQ(hostName, "Host");
engine->OnUpdate(0.016f);
startCount = 0;
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "StartCount", startCount));
EXPECT_EQ(startCount, 1);
}
TEST_F(MonoScriptRuntimeTest, GameObjectRuntimeApiCreatesFindsAndDestroysSceneObjects) {
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
GameObject* host = runtimeScene->CreateGameObject("Host");