Add enum script field support
This commit is contained in:
@@ -266,3 +266,9 @@ C# 脚本模块已经完成了第一阶段的核心闭环,不再是“从 0
|
|||||||
- 已新增 `TagLayerProbe` 与对应 C++ / Mono 回归测试,覆盖默认 tag、layer 裁剪、脚本侧读写与场景 roundtrip。
|
- 已新增 `TagLayerProbe` 与对应 C++ / Mono 回归测试,覆盖默认 tag、layer 裁剪、脚本侧读写与场景 roundtrip。
|
||||||
- 相关定向测试全部通过;完整 `MonoScriptRuntimeTest.*:ProjectScriptAssemblyTest.*` 输出全绿,当前仍存在历史性的 `exit code 3` 异常,需后续单独跟踪。
|
- 相关定向测试全部通过;完整 `MonoScriptRuntimeTest.*:ProjectScriptAssemblyTest.*` 输出全绿,当前仍存在历史性的 `exit code 3` 异常,需后续单独跟踪。
|
||||||
- 下一步建议继续做第三小项:`Object.Instantiate`
|
- 下一步建议继续做第三小项:`Object.Instantiate`
|
||||||
|
|
||||||
|
- 已完成字段系统第一小项:`enum` 字段支持
|
||||||
|
- `MonoScriptRuntime` 现已支持把常见整数底层的 C# `enum` 字段纳入脚本字段模型,并按 `Int32` 进入默认值、存储值、运行时值三层同步链路。
|
||||||
|
- 已新增 `FieldMetadataProbeState` / `EnumFieldProbeState` 探针,覆盖枚举字段发现、默认值提取、运行时写回与场景 roundtrip。
|
||||||
|
- 已通过新增定向测试,以及完整 `ScriptFieldStorage_Test.*:MonoScriptRuntimeTest.*:ProjectScriptAssemblyTest.*` 整组验证;输出全绿,`exit code 3` 仍为既有历史现象。
|
||||||
|
- 字段系统下一步建议切到:`[SerializeField] private` 字段支持
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ private:
|
|||||||
struct FieldMetadata {
|
struct FieldMetadata {
|
||||||
ScriptFieldType type = ScriptFieldType::None;
|
ScriptFieldType type = ScriptFieldType::None;
|
||||||
MonoClassField* field = nullptr;
|
MonoClassField* field = nullptr;
|
||||||
|
bool isEnum = false;
|
||||||
|
int32_t enumUnderlyingType = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClassMetadata {
|
struct ClassMetadata {
|
||||||
@@ -153,7 +155,7 @@ private:
|
|||||||
void DiscoverScriptClassesInImage(const std::string& assemblyName, MonoImage* image);
|
void DiscoverScriptClassesInImage(const std::string& assemblyName, MonoImage* image);
|
||||||
bool IsMonoBehaviourSubclass(MonoClass* monoClass) const;
|
bool IsMonoBehaviourSubclass(MonoClass* monoClass) const;
|
||||||
|
|
||||||
ScriptFieldType MapMonoFieldType(MonoClassField* field) const;
|
FieldMetadata BuildFieldMetadata(MonoClassField* field) const;
|
||||||
|
|
||||||
static const char* ToLifecycleMethodName(ScriptLifecycleMethod method);
|
static const char* ToLifecycleMethodName(ScriptLifecycleMethod method);
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <mono/metadata/reflection.h>
|
#include <mono/metadata/reflection.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
@@ -2267,14 +2268,12 @@ void MonoScriptRuntime::DiscoverScriptClassesInImage(const std::string& assembly
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ScriptFieldType fieldType = MapMonoFieldType(field);
|
FieldMetadata fieldMetadata = BuildFieldMetadata(field);
|
||||||
if (fieldType == ScriptFieldType::None) {
|
if (fieldMetadata.type == ScriptFieldType::None) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata.fields.emplace(
|
metadata.fields.emplace(mono_field_get_name(field), std::move(fieldMetadata));
|
||||||
mono_field_get_name(field),
|
|
||||||
FieldMetadata{fieldType, field});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_classes.emplace(
|
m_classes.emplace(
|
||||||
@@ -2300,68 +2299,101 @@ bool MonoScriptRuntime::IsMonoBehaviourSubclass(MonoClass* monoClass) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptFieldType MonoScriptRuntime::MapMonoFieldType(MonoClassField* field) const {
|
MonoScriptRuntime::FieldMetadata MonoScriptRuntime::BuildFieldMetadata(MonoClassField* field) const {
|
||||||
|
FieldMetadata metadata;
|
||||||
|
metadata.field = field;
|
||||||
if (!field) {
|
if (!field) {
|
||||||
return ScriptFieldType::None;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
MonoType* monoType = mono_field_get_type(field);
|
MonoType* monoType = mono_field_get_type(field);
|
||||||
if (!monoType) {
|
if (!monoType) {
|
||||||
return ScriptFieldType::None;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (mono_type_get_type(monoType)) {
|
switch (mono_type_get_type(monoType)) {
|
||||||
case MONO_TYPE_R4:
|
case MONO_TYPE_R4:
|
||||||
return ScriptFieldType::Float;
|
metadata.type = ScriptFieldType::Float;
|
||||||
|
return metadata;
|
||||||
case MONO_TYPE_R8:
|
case MONO_TYPE_R8:
|
||||||
return ScriptFieldType::Double;
|
metadata.type = ScriptFieldType::Double;
|
||||||
|
return metadata;
|
||||||
case MONO_TYPE_BOOLEAN:
|
case MONO_TYPE_BOOLEAN:
|
||||||
return ScriptFieldType::Bool;
|
metadata.type = ScriptFieldType::Bool;
|
||||||
|
return metadata;
|
||||||
case MONO_TYPE_I4:
|
case MONO_TYPE_I4:
|
||||||
return ScriptFieldType::Int32;
|
metadata.type = ScriptFieldType::Int32;
|
||||||
|
return metadata;
|
||||||
case MONO_TYPE_U8:
|
case MONO_TYPE_U8:
|
||||||
return ScriptFieldType::UInt64;
|
metadata.type = ScriptFieldType::UInt64;
|
||||||
|
return metadata;
|
||||||
case MONO_TYPE_STRING:
|
case MONO_TYPE_STRING:
|
||||||
return ScriptFieldType::String;
|
metadata.type = ScriptFieldType::String;
|
||||||
|
return metadata;
|
||||||
case MONO_TYPE_CLASS: {
|
case MONO_TYPE_CLASS: {
|
||||||
MonoClass* referenceClass = mono_class_from_mono_type(monoType);
|
MonoClass* referenceClass = mono_class_from_mono_type(monoType);
|
||||||
if (!referenceClass) {
|
if (!referenceClass) {
|
||||||
return ScriptFieldType::None;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string namespaceName = SafeString(mono_class_get_namespace(referenceClass));
|
const std::string namespaceName = SafeString(mono_class_get_namespace(referenceClass));
|
||||||
const std::string className = SafeString(mono_class_get_name(referenceClass));
|
const std::string className = SafeString(mono_class_get_name(referenceClass));
|
||||||
if (namespaceName == m_settings.baseNamespace && className == "GameObject") {
|
if (namespaceName == m_settings.baseNamespace && className == "GameObject") {
|
||||||
return ScriptFieldType::GameObject;
|
metadata.type = ScriptFieldType::GameObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ScriptFieldType::None;
|
return metadata;
|
||||||
}
|
}
|
||||||
case MONO_TYPE_VALUETYPE: {
|
case MONO_TYPE_VALUETYPE: {
|
||||||
MonoClass* valueTypeClass = mono_class_from_mono_type(monoType);
|
MonoClass* valueTypeClass = mono_class_from_mono_type(monoType);
|
||||||
if (!valueTypeClass) {
|
if (!valueTypeClass) {
|
||||||
return ScriptFieldType::None;
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mono_class_is_enum(valueTypeClass) != 0) {
|
||||||
|
MonoType* underlyingType = mono_class_enum_basetype(valueTypeClass);
|
||||||
|
if (!underlyingType) {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mono_type_get_type(underlyingType)) {
|
||||||
|
case MONO_TYPE_I1:
|
||||||
|
case MONO_TYPE_U1:
|
||||||
|
case MONO_TYPE_I2:
|
||||||
|
case MONO_TYPE_U2:
|
||||||
|
case MONO_TYPE_I4:
|
||||||
|
case MONO_TYPE_U4:
|
||||||
|
metadata.type = ScriptFieldType::Int32;
|
||||||
|
metadata.isEnum = true;
|
||||||
|
metadata.enumUnderlyingType = static_cast<int32_t>(mono_type_get_type(underlyingType));
|
||||||
|
return metadata;
|
||||||
|
default:
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string namespaceName = SafeString(mono_class_get_namespace(valueTypeClass));
|
const std::string namespaceName = SafeString(mono_class_get_namespace(valueTypeClass));
|
||||||
const std::string className = SafeString(mono_class_get_name(valueTypeClass));
|
const std::string className = SafeString(mono_class_get_name(valueTypeClass));
|
||||||
if (namespaceName != m_settings.baseNamespace) {
|
if (namespaceName != m_settings.baseNamespace) {
|
||||||
return ScriptFieldType::None;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (className == "Vector2") {
|
if (className == "Vector2") {
|
||||||
return ScriptFieldType::Vector2;
|
metadata.type = ScriptFieldType::Vector2;
|
||||||
|
return metadata;
|
||||||
}
|
}
|
||||||
if (className == "Vector3") {
|
if (className == "Vector3") {
|
||||||
return ScriptFieldType::Vector3;
|
metadata.type = ScriptFieldType::Vector3;
|
||||||
|
return metadata;
|
||||||
}
|
}
|
||||||
if (className == "Vector4") {
|
if (className == "Vector4") {
|
||||||
return ScriptFieldType::Vector4;
|
metadata.type = ScriptFieldType::Vector4;
|
||||||
|
return metadata;
|
||||||
}
|
}
|
||||||
return ScriptFieldType::None;
|
return metadata;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return ScriptFieldType::None;
|
return metadata;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2623,6 +2655,70 @@ bool MonoScriptRuntime::TrySetFieldValue(
|
|||||||
|
|
||||||
SetCurrentDomain();
|
SetCurrentDomain();
|
||||||
|
|
||||||
|
if (fieldMetadata.isEnum) {
|
||||||
|
if (!std::holds_alternative<int32_t>(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32_t storedValue = std::get<int32_t>(value);
|
||||||
|
switch (fieldMetadata.enumUnderlyingType) {
|
||||||
|
case MONO_TYPE_I1: {
|
||||||
|
if (storedValue < std::numeric_limits<int8_t>::min()
|
||||||
|
|| storedValue > std::numeric_limits<int8_t>::max()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t nativeValue = static_cast<int8_t>(storedValue);
|
||||||
|
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case MONO_TYPE_U1: {
|
||||||
|
if (storedValue < 0 || storedValue > std::numeric_limits<uint8_t>::max()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t nativeValue = static_cast<uint8_t>(storedValue);
|
||||||
|
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case MONO_TYPE_I2: {
|
||||||
|
if (storedValue < std::numeric_limits<int16_t>::min()
|
||||||
|
|| storedValue > std::numeric_limits<int16_t>::max()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t nativeValue = static_cast<int16_t>(storedValue);
|
||||||
|
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case MONO_TYPE_U2: {
|
||||||
|
if (storedValue < 0 || storedValue > std::numeric_limits<uint16_t>::max()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t nativeValue = static_cast<uint16_t>(storedValue);
|
||||||
|
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case MONO_TYPE_I4: {
|
||||||
|
int32_t nativeValue = storedValue;
|
||||||
|
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case MONO_TYPE_U4: {
|
||||||
|
if (storedValue < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t nativeValue = static_cast<uint32_t>(storedValue);
|
||||||
|
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (fieldMetadata.type) {
|
switch (fieldMetadata.type) {
|
||||||
case ScriptFieldType::Float: {
|
case ScriptFieldType::Float: {
|
||||||
float nativeValue = std::get<float>(value);
|
float nativeValue = std::get<float>(value);
|
||||||
@@ -2693,6 +2789,53 @@ bool MonoScriptRuntime::TryReadFieldValue(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fieldMetadata.isEnum) {
|
||||||
|
switch (fieldMetadata.enumUnderlyingType) {
|
||||||
|
case MONO_TYPE_I1: {
|
||||||
|
int8_t nativeValue = 0;
|
||||||
|
mono_field_get_value(instance, fieldMetadata.field, &nativeValue);
|
||||||
|
outValue = static_cast<int32_t>(nativeValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case MONO_TYPE_U1: {
|
||||||
|
uint8_t nativeValue = 0;
|
||||||
|
mono_field_get_value(instance, fieldMetadata.field, &nativeValue);
|
||||||
|
outValue = static_cast<int32_t>(nativeValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case MONO_TYPE_I2: {
|
||||||
|
int16_t nativeValue = 0;
|
||||||
|
mono_field_get_value(instance, fieldMetadata.field, &nativeValue);
|
||||||
|
outValue = static_cast<int32_t>(nativeValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case MONO_TYPE_U2: {
|
||||||
|
uint16_t nativeValue = 0;
|
||||||
|
mono_field_get_value(instance, fieldMetadata.field, &nativeValue);
|
||||||
|
outValue = static_cast<int32_t>(nativeValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case MONO_TYPE_I4: {
|
||||||
|
int32_t nativeValue = 0;
|
||||||
|
mono_field_get_value(instance, fieldMetadata.field, &nativeValue);
|
||||||
|
outValue = nativeValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case MONO_TYPE_U4: {
|
||||||
|
uint32_t nativeValue = 0;
|
||||||
|
mono_field_get_value(instance, fieldMetadata.field, &nativeValue);
|
||||||
|
if (nativeValue > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outValue = static_cast<int32_t>(nativeValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (fieldMetadata.type) {
|
switch (fieldMetadata.type) {
|
||||||
case ScriptFieldType::Float: {
|
case ScriptFieldType::Float: {
|
||||||
float nativeValue = 0.0f;
|
float nativeValue = 0.0f;
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ set(XCENGINE_SCRIPT_CORE_SOURCES
|
|||||||
set(XCENGINE_GAME_SCRIPT_SOURCES
|
set(XCENGINE_GAME_SCRIPT_SOURCES
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/BuiltinComponentProbe.cs
|
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/BuiltinComponentProbe.cs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/AddComponentProbe.cs
|
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/AddComponentProbe.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/EnumFieldProbe.cs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/FieldMetadataProbe.cs
|
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/FieldMetadataProbe.cs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/GetComponentsProbe.cs
|
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/GetComponentsProbe.cs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/ScriptComponentApiProbe.cs
|
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/ScriptComponentApiProbe.cs
|
||||||
|
|||||||
28
managed/GameScripts/EnumFieldProbe.cs
Normal file
28
managed/GameScripts/EnumFieldProbe.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using XCEngine;
|
||||||
|
|
||||||
|
namespace Gameplay
|
||||||
|
{
|
||||||
|
public enum EnumFieldProbeState
|
||||||
|
{
|
||||||
|
Default = 1,
|
||||||
|
StoredConfigured = 5,
|
||||||
|
RuntimeUpdated = 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class EnumFieldProbe : MonoBehaviour
|
||||||
|
{
|
||||||
|
public EnumFieldProbeState State = EnumFieldProbeState.Default;
|
||||||
|
public int ObservedInitialState = -1;
|
||||||
|
public bool ObservedStoredStateApplied;
|
||||||
|
public int ObservedUpdatedState = -1;
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
ObservedInitialState = (int)State;
|
||||||
|
ObservedStoredStateApplied = State == EnumFieldProbeState.StoredConfigured;
|
||||||
|
|
||||||
|
State = EnumFieldProbeState.RuntimeUpdated;
|
||||||
|
ObservedUpdatedState = (int)State;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,12 +2,19 @@ using XCEngine;
|
|||||||
|
|
||||||
namespace Gameplay
|
namespace Gameplay
|
||||||
{
|
{
|
||||||
|
public enum FieldMetadataProbeState
|
||||||
|
{
|
||||||
|
Idle = 0,
|
||||||
|
Running = 2,
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class FieldMetadataProbe : MonoBehaviour
|
public sealed class FieldMetadataProbe : MonoBehaviour
|
||||||
{
|
{
|
||||||
public int Health;
|
public int Health;
|
||||||
public float Speed;
|
public float Speed;
|
||||||
public string Label = string.Empty;
|
public string Label = string.Empty;
|
||||||
public Vector3 SpawnPoint;
|
public Vector3 SpawnPoint;
|
||||||
|
public FieldMetadataProbeState State = FieldMetadataProbeState.Running;
|
||||||
public GameObject Target;
|
public GameObject Target;
|
||||||
public Quaternion UnsupportedRotation;
|
public Quaternion UnsupportedRotation;
|
||||||
public static int SharedCounter;
|
public static int SharedCounter;
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ TEST_F(MonoScriptRuntimeTest, ClassFieldMetadataListsSupportedPublicInstanceFiel
|
|||||||
{"Label", ScriptFieldType::String},
|
{"Label", ScriptFieldType::String},
|
||||||
{"SpawnPoint", ScriptFieldType::Vector3},
|
{"SpawnPoint", ScriptFieldType::Vector3},
|
||||||
{"Speed", ScriptFieldType::Float},
|
{"Speed", ScriptFieldType::Float},
|
||||||
|
{"State", ScriptFieldType::Int32},
|
||||||
{"Target", ScriptFieldType::GameObject},
|
{"Target", ScriptFieldType::GameObject},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -212,6 +213,23 @@ TEST_F(MonoScriptRuntimeTest, ClassFieldDefaultValueQueryReturnsManagedInitializ
|
|||||||
EXPECT_EQ(std::get<int32_t>(fieldIt->value), -1);
|
EXPECT_EQ(std::get<int32_t>(fieldIt->value), -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, ClassFieldDefaultValueQueryReturnsEnumInitializersAsInt32) {
|
||||||
|
std::vector<ScriptFieldDefaultValue> fields;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetClassFieldDefaultValues("GameScripts", "Gameplay", "FieldMetadataProbe", fields));
|
||||||
|
|
||||||
|
const auto fieldIt = std::find_if(
|
||||||
|
fields.begin(),
|
||||||
|
fields.end(),
|
||||||
|
[](const ScriptFieldDefaultValue& field) {
|
||||||
|
return field.fieldName == "State";
|
||||||
|
});
|
||||||
|
|
||||||
|
ASSERT_NE(fieldIt, fields.end());
|
||||||
|
EXPECT_EQ(fieldIt->type, ScriptFieldType::Int32);
|
||||||
|
EXPECT_EQ(std::get<int32_t>(fieldIt->value), 2);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(MonoScriptRuntimeTest, ScriptEngineAppliesStoredFieldsAndInvokesLifecycleMethods) {
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineAppliesStoredFieldsAndInvokesLifecycleMethods) {
|
||||||
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
GameObject* host = runtimeScene->CreateGameObject("Host");
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
@@ -786,6 +804,66 @@ TEST_F(MonoScriptRuntimeTest, ManagedFieldChangesWriteBackToStoredCacheAndPersis
|
|||||||
EXPECT_EQ(observedTargetName, "Target");
|
EXPECT_EQ(observedTargetName, "Target");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, EnumScriptFieldsApplyStoredValuesAndPersistAcrossSceneRoundTrip) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
ScriptComponent* component = AddScript(host, "Gameplay", "EnumFieldProbe");
|
||||||
|
|
||||||
|
component->GetFieldStorage().SetFieldValue("State", int32_t(5));
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
int32_t observedInitialState = 0;
|
||||||
|
bool observedStoredStateApplied = false;
|
||||||
|
int32_t observedUpdatedState = 0;
|
||||||
|
int32_t runtimeState = 0;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialState", observedInitialState));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedStoredStateApplied", observedStoredStateApplied));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedState", observedUpdatedState));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "State", runtimeState));
|
||||||
|
|
||||||
|
EXPECT_EQ(observedInitialState, 5);
|
||||||
|
EXPECT_TRUE(observedStoredStateApplied);
|
||||||
|
EXPECT_EQ(observedUpdatedState, 9);
|
||||||
|
EXPECT_EQ(runtimeState, 9);
|
||||||
|
|
||||||
|
int32_t storedState = 0;
|
||||||
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("State", storedState));
|
||||||
|
EXPECT_EQ(storedState, 9);
|
||||||
|
|
||||||
|
const std::string serializedScene = runtimeScene->SerializeToString();
|
||||||
|
|
||||||
|
engine->OnRuntimeStop();
|
||||||
|
|
||||||
|
scene = std::make_unique<Scene>("ReloadedEnumScene");
|
||||||
|
scene->DeserializeFromString(serializedScene);
|
||||||
|
Scene* reloadedScene = scene.get();
|
||||||
|
|
||||||
|
GameObject* loadedHost = reloadedScene->Find("Host");
|
||||||
|
ASSERT_NE(loadedHost, nullptr);
|
||||||
|
|
||||||
|
ScriptComponent* loadedComponent = FindScriptComponentByClass(loadedHost, "Gameplay", "EnumFieldProbe");
|
||||||
|
ASSERT_NE(loadedComponent, nullptr);
|
||||||
|
|
||||||
|
int32_t loadedStoredState = 0;
|
||||||
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("State", loadedStoredState));
|
||||||
|
EXPECT_EQ(loadedStoredState, 9);
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(reloadedScene);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
int32_t loadedObservedInitialState = 0;
|
||||||
|
int32_t loadedRuntimeState = 0;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedInitialState", loadedObservedInitialState));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "State", loadedRuntimeState));
|
||||||
|
|
||||||
|
EXPECT_EQ(loadedObservedInitialState, 9);
|
||||||
|
EXPECT_EQ(loadedRuntimeState, 9);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldApiUpdatesLiveManagedInstanceAndStoredCache) {
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldApiUpdatesLiveManagedInstanceAndStoredCache) {
|
||||||
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
GameObject* host = runtimeScene->CreateGameObject("Host");
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
|||||||
Reference in New Issue
Block a user