Add concrete component script field support

This commit is contained in:
2026-04-03 16:51:42 +08:00
parent 73415915e6
commit e0e5c1fcaa
11 changed files with 507 additions and 4 deletions

View File

@@ -248,6 +248,43 @@ TEST_F(MonoScriptRuntimeTest, ClassFieldDefaultValueQueryReturnsSerializeFieldPr
EXPECT_TRUE(std::get<bool>(fieldIt->value));
}
TEST_F(MonoScriptRuntimeTest, ClassFieldMetadataListsConcreteComponentReferenceFields) {
std::vector<ScriptFieldMetadata> fields;
EXPECT_TRUE(runtime->TryGetClassFieldMetadata("GameScripts", "Gameplay", "ComponentFieldMetadataProbe", fields));
const std::vector<ScriptFieldMetadata> expected = {
{"Pivot", ScriptFieldType::Component},
{"SceneCamera", ScriptFieldType::Component},
{"ScriptTarget", ScriptFieldType::Component},
};
EXPECT_EQ(fields, expected);
}
TEST_F(MonoScriptRuntimeTest, ClassFieldDefaultValueQueryReturnsNullComponentReferences) {
std::vector<ScriptFieldDefaultValue> fields;
EXPECT_TRUE(runtime->TryGetClassFieldDefaultValues("GameScripts", "Gameplay", "ComponentFieldMetadataProbe", fields));
const auto expectNullComponentField = [&](const char* fieldName) {
const auto fieldIt = std::find_if(
fields.begin(),
fields.end(),
[fieldName](const ScriptFieldDefaultValue& field) {
return field.fieldName == fieldName;
});
ASSERT_NE(fieldIt, fields.end());
EXPECT_EQ(fieldIt->type, ScriptFieldType::Component);
EXPECT_EQ(std::get<ComponentReference>(fieldIt->value), (ComponentReference{}));
};
expectNullComponentField("Pivot");
expectNullComponentField("SceneCamera");
expectNullComponentField("ScriptTarget");
}
TEST_F(MonoScriptRuntimeTest, ScriptEngineAppliesStoredFieldsAndInvokesLifecycleMethods) {
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
GameObject* host = runtimeScene->CreateGameObject("Host");
@@ -968,6 +1005,163 @@ TEST_F(MonoScriptRuntimeTest, SerializeFieldPrivateFieldsApplyStoredValuesAndPer
EXPECT_FALSE(loadedRuntimeHiddenEnabled);
}
TEST_F(MonoScriptRuntimeTest, ComponentReferenceFieldsApplyStoredValuesAndPersistAcrossSceneRoundTrip) {
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
GameObject* host = runtimeScene->CreateGameObject("Host");
host->AddComponent<CameraComponent>();
GameObject* pivotTarget = runtimeScene->CreateGameObject("PivotTarget");
GameObject* cameraTarget = runtimeScene->CreateGameObject("CameraTarget");
cameraTarget->AddComponent<CameraComponent>();
GameObject* scriptTargetHost = runtimeScene->CreateGameObject("ScriptTarget");
ScriptComponent* referencedScript = AddScript(scriptTargetHost, "Gameplay", "ScriptComponentTargetProbe");
ScriptComponent* component = AddScript(host, "Gameplay", "ComponentFieldProbe");
component->GetFieldStorage().SetFieldValue("Pivot", ComponentReference{pivotTarget->GetUUID(), 0});
component->GetFieldStorage().SetFieldValue("SceneCamera", ComponentReference{cameraTarget->GetUUID(), 0});
component->GetFieldStorage().SetFieldValue(
"ScriptTarget",
ComponentReference{scriptTargetHost->GetUUID(), referencedScript->GetScriptComponentUUID()});
engine->OnRuntimeStart(runtimeScene);
engine->OnUpdate(0.016f);
bool observedStoredPivotApplied = false;
bool observedStoredCameraApplied = false;
bool observedStoredScriptApplied = false;
std::string observedPivotName;
std::string observedCameraName;
std::string observedScriptName;
int32_t observedScriptAwakeCount = -1;
int32_t observedScriptHostCallCount = -1;
bool observedUpdatedPivotAssigned = false;
bool observedUpdatedCameraAssigned = false;
bool observedUpdatedScriptAssigned = false;
std::string observedUpdatedPivotName;
std::string observedUpdatedCameraName;
std::string observedUpdatedScriptName;
ComponentReference runtimePivot;
ComponentReference runtimeCamera;
ComponentReference runtimeScript;
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedStoredPivotApplied", observedStoredPivotApplied));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedStoredCameraApplied", observedStoredCameraApplied));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedStoredScriptApplied", observedStoredScriptApplied));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedPivotName", observedPivotName));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCameraName", observedCameraName));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedScriptName", observedScriptName));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedScriptAwakeCount", observedScriptAwakeCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedScriptHostCallCount", observedScriptHostCallCount));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedPivotAssigned", observedUpdatedPivotAssigned));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedCameraAssigned", observedUpdatedCameraAssigned));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedScriptAssigned", observedUpdatedScriptAssigned));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedPivotName", observedUpdatedPivotName));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedCameraName", observedUpdatedCameraName));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedScriptName", observedUpdatedScriptName));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Pivot", runtimePivot));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SceneCamera", runtimeCamera));
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ScriptTarget", runtimeScript));
EXPECT_TRUE(observedStoredPivotApplied);
EXPECT_TRUE(observedStoredCameraApplied);
EXPECT_TRUE(observedStoredScriptApplied);
EXPECT_EQ(observedPivotName, "PivotTarget");
EXPECT_EQ(observedCameraName, "CameraTarget");
EXPECT_EQ(observedScriptName, "ScriptTarget");
EXPECT_EQ(observedScriptAwakeCount, 1);
EXPECT_EQ(observedScriptHostCallCount, 1);
EXPECT_TRUE(observedUpdatedPivotAssigned);
EXPECT_TRUE(observedUpdatedCameraAssigned);
EXPECT_TRUE(observedUpdatedScriptAssigned);
EXPECT_EQ(observedUpdatedPivotName, "Host");
EXPECT_EQ(observedUpdatedCameraName, "Host");
EXPECT_EQ(observedUpdatedScriptName, "Host");
ScriptComponent* assignedHostScript = FindScriptComponentByClass(host, "Gameplay", "ScriptComponentTargetProbe");
ASSERT_NE(assignedHostScript, nullptr);
EXPECT_EQ(runtimePivot, (ComponentReference{host->GetUUID(), 0}));
EXPECT_EQ(runtimeCamera, (ComponentReference{host->GetUUID(), 0}));
EXPECT_EQ(
runtimeScript,
(ComponentReference{host->GetUUID(), assignedHostScript->GetScriptComponentUUID()}));
ComponentReference storedPivot;
ComponentReference storedCamera;
ComponentReference storedScript;
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Pivot", storedPivot));
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SceneCamera", storedCamera));
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("ScriptTarget", storedScript));
EXPECT_EQ(storedPivot, runtimePivot);
EXPECT_EQ(storedCamera, runtimeCamera);
EXPECT_EQ(storedScript, runtimeScript);
const std::string serializedScene = runtimeScene->SerializeToString();
engine->OnRuntimeStop();
scene = std::make_unique<Scene>("ReloadedComponentFieldScene");
scene->DeserializeFromString(serializedScene);
Scene* reloadedScene = scene.get();
GameObject* loadedHost = reloadedScene->Find("Host");
ASSERT_NE(loadedHost, nullptr);
ScriptComponent* loadedComponent = FindScriptComponentByClass(loadedHost, "Gameplay", "ComponentFieldProbe");
ScriptComponent* loadedAssignedHostScript =
FindScriptComponentByClass(loadedHost, "Gameplay", "ScriptComponentTargetProbe");
ASSERT_NE(loadedComponent, nullptr);
ASSERT_NE(loadedAssignedHostScript, nullptr);
ComponentReference loadedStoredPivot;
ComponentReference loadedStoredCamera;
ComponentReference loadedStoredScript;
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("Pivot", loadedStoredPivot));
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("SceneCamera", loadedStoredCamera));
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("ScriptTarget", loadedStoredScript));
EXPECT_EQ(loadedStoredPivot, (ComponentReference{loadedHost->GetUUID(), 0}));
EXPECT_EQ(loadedStoredCamera, (ComponentReference{loadedHost->GetUUID(), 0}));
EXPECT_EQ(
loadedStoredScript,
(ComponentReference{loadedHost->GetUUID(), loadedAssignedHostScript->GetScriptComponentUUID()}));
engine->OnRuntimeStart(reloadedScene);
engine->OnUpdate(0.016f);
bool loadedObservedStoredPivotApplied = false;
bool loadedObservedStoredCameraApplied = false;
bool loadedObservedStoredScriptApplied = false;
std::string loadedObservedPivotName;
std::string loadedObservedCameraName;
std::string loadedObservedScriptName;
ComponentReference loadedRuntimePivot;
ComponentReference loadedRuntimeCamera;
ComponentReference loadedRuntimeScript;
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedStoredPivotApplied", loadedObservedStoredPivotApplied));
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedStoredCameraApplied", loadedObservedStoredCameraApplied));
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedStoredScriptApplied", loadedObservedStoredScriptApplied));
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedPivotName", loadedObservedPivotName));
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedCameraName", loadedObservedCameraName));
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedScriptName", loadedObservedScriptName));
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "Pivot", loadedRuntimePivot));
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "SceneCamera", loadedRuntimeCamera));
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ScriptTarget", loadedRuntimeScript));
EXPECT_TRUE(loadedObservedStoredPivotApplied);
EXPECT_TRUE(loadedObservedStoredCameraApplied);
EXPECT_TRUE(loadedObservedStoredScriptApplied);
EXPECT_EQ(loadedObservedPivotName, "Host");
EXPECT_EQ(loadedObservedCameraName, "Host");
EXPECT_EQ(loadedObservedScriptName, "Host");
EXPECT_EQ(loadedRuntimePivot, (ComponentReference{loadedHost->GetUUID(), 0}));
EXPECT_EQ(loadedRuntimeCamera, (ComponentReference{loadedHost->GetUUID(), 0}));
EXPECT_EQ(
loadedRuntimeScript,
(ComponentReference{loadedHost->GetUUID(), loadedAssignedHostScript->GetScriptComponentUUID()}));
}
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldApiUpdatesLiveManagedInstanceAndStoredCache) {
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
GameObject* host = runtimeScene->CreateGameObject("Host");

View File

@@ -41,6 +41,7 @@ TEST(ScriptComponent_Test, SerializeRoundTripPreservesMetadataAndFields) {
ASSERT_TRUE(original.GetFieldStorage().SetFieldValue("DisplayName", std::string("Hero=One;Ready|100%")));
ASSERT_TRUE(original.GetFieldStorage().SetFieldValue("MoveSpeed", 6.25f));
ASSERT_TRUE(original.GetFieldStorage().SetFieldValue("Target", GameObjectReference{9988}));
ASSERT_TRUE(original.GetFieldStorage().SetFieldValue("TargetCamera", ComponentReference{9988, 0}));
std::ostringstream stream;
original.Serialize(stream);
@@ -52,6 +53,7 @@ TEST(ScriptComponent_Test, SerializeRoundTripPreservesMetadataAndFields) {
std::string displayName;
float moveSpeed = 0.0f;
GameObjectReference target;
ComponentReference targetCamera;
EXPECT_EQ(restored.GetScriptComponentUUID(), original.GetScriptComponentUUID());
EXPECT_EQ(restored.GetAssemblyName(), "GameScripts.Runtime");
@@ -61,9 +63,11 @@ TEST(ScriptComponent_Test, SerializeRoundTripPreservesMetadataAndFields) {
EXPECT_TRUE(restored.GetFieldStorage().TryGetFieldValue("DisplayName", displayName));
EXPECT_TRUE(restored.GetFieldStorage().TryGetFieldValue("MoveSpeed", moveSpeed));
EXPECT_TRUE(restored.GetFieldStorage().TryGetFieldValue("Target", target));
EXPECT_TRUE(restored.GetFieldStorage().TryGetFieldValue("TargetCamera", targetCamera));
EXPECT_EQ(displayName, "Hero=One;Ready|100%");
EXPECT_FLOAT_EQ(moveSpeed, 6.25f);
EXPECT_EQ(target, GameObjectReference{9988});
EXPECT_EQ(targetCamera, (ComponentReference{9988, 0}));
}
TEST(ScriptComponent_Test, ComponentFactoryRegistryCreatesScriptComponents) {

View File

@@ -20,6 +20,7 @@ TEST(ScriptFieldStorage_Test, StoresRetrievesAndRemovesSupportedValues) {
EXPECT_TRUE(storage.SetFieldValue("Velocity", Vector3(3.0f, 4.0f, 5.0f)));
EXPECT_TRUE(storage.SetFieldValue("Tint", Vector4(0.1f, 0.2f, 0.3f, 1.0f)));
EXPECT_TRUE(storage.SetFieldValue("Target", GameObjectReference{42}));
EXPECT_TRUE(storage.SetFieldValue("TargetCamera", ComponentReference{42, 0}));
float speed = 0.0f;
double accuracy = 0.0;
@@ -31,8 +32,9 @@ TEST(ScriptFieldStorage_Test, StoresRetrievesAndRemovesSupportedValues) {
Vector3 velocity;
Vector4 tint;
GameObjectReference target;
ComponentReference targetCamera;
EXPECT_EQ(storage.GetFieldCount(), 10u);
EXPECT_EQ(storage.GetFieldCount(), 11u);
EXPECT_TRUE(storage.Contains("Velocity"));
EXPECT_TRUE(storage.TryGetFieldValue("Speed", speed));
EXPECT_TRUE(storage.TryGetFieldValue("Accuracy", accuracy));
@@ -44,6 +46,7 @@ TEST(ScriptFieldStorage_Test, StoresRetrievesAndRemovesSupportedValues) {
EXPECT_TRUE(storage.TryGetFieldValue("Velocity", velocity));
EXPECT_TRUE(storage.TryGetFieldValue("Tint", tint));
EXPECT_TRUE(storage.TryGetFieldValue("Target", target));
EXPECT_TRUE(storage.TryGetFieldValue("TargetCamera", targetCamera));
EXPECT_FLOAT_EQ(speed, 3.5f);
EXPECT_DOUBLE_EQ(accuracy, 0.875);
@@ -55,10 +58,11 @@ TEST(ScriptFieldStorage_Test, StoresRetrievesAndRemovesSupportedValues) {
EXPECT_EQ(velocity, Vector3(3.0f, 4.0f, 5.0f));
EXPECT_EQ(tint, Vector4(0.1f, 0.2f, 0.3f, 1.0f));
EXPECT_EQ(target, GameObjectReference{42});
EXPECT_EQ(targetCamera, (ComponentReference{42, 0}));
EXPECT_TRUE(storage.Remove("IsAlive"));
EXPECT_FALSE(storage.Contains("IsAlive"));
EXPECT_EQ(storage.GetFieldCount(), 9u);
EXPECT_EQ(storage.GetFieldCount(), 10u);
}
TEST(ScriptFieldStorage_Test, SerializeRoundTripPreservesFieldValues) {
@@ -66,6 +70,7 @@ TEST(ScriptFieldStorage_Test, SerializeRoundTripPreservesFieldValues) {
ASSERT_TRUE(original.SetFieldValue("Message", std::string("Ready;Set=Go|100%\nLine2")));
ASSERT_TRUE(original.SetFieldValue("Scale", Vector3(2.0f, 3.0f, 4.0f)));
ASSERT_TRUE(original.SetFieldValue("Owner", GameObjectReference{778899}));
ASSERT_TRUE(original.SetFieldValue("View", ComponentReference{778899, 123456}));
ASSERT_TRUE(original.SetFieldValue("Counter", int32_t(-17)));
const std::string serialized = original.SerializeToString();
@@ -76,16 +81,19 @@ TEST(ScriptFieldStorage_Test, SerializeRoundTripPreservesFieldValues) {
std::string message;
Vector3 scale;
GameObjectReference owner;
ComponentReference view;
int32_t counter = 0;
EXPECT_TRUE(restored.TryGetFieldValue("Message", message));
EXPECT_TRUE(restored.TryGetFieldValue("Scale", scale));
EXPECT_TRUE(restored.TryGetFieldValue("Owner", owner));
EXPECT_TRUE(restored.TryGetFieldValue("View", view));
EXPECT_TRUE(restored.TryGetFieldValue("Counter", counter));
EXPECT_EQ(message, "Ready;Set=Go|100%\nLine2");
EXPECT_EQ(scale, Vector3(2.0f, 3.0f, 4.0f));
EXPECT_EQ(owner, GameObjectReference{778899});
EXPECT_EQ(view, (ComponentReference{778899, 123456}));
EXPECT_EQ(counter, -17);
}