feat(scripting): support managed script component api
This commit is contained in:
@@ -53,6 +53,8 @@ public:
|
||||
|
||||
bool HasManagedInstance(const ScriptComponent* component) const;
|
||||
size_t GetManagedInstanceCount() const { return m_instances.size(); }
|
||||
MonoObject* GetManagedInstanceObject(const ScriptComponent* component) const;
|
||||
MonoObject* CreateManagedComponentWrapper(MonoClass* componentClass, uint64_t gameObjectUUID);
|
||||
|
||||
bool TryGetFieldValue(
|
||||
const ScriptComponent* component,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Debug/Logger.h"
|
||||
#include "Scene/Scene.h"
|
||||
#include "Scripting/ScriptComponent.h"
|
||||
#include "Scripting/ScriptEngine.h"
|
||||
|
||||
#include <mono/jit/jit.h>
|
||||
#include <mono/metadata/appdomain.h>
|
||||
@@ -38,6 +39,7 @@ struct MonoRootState {
|
||||
|
||||
enum class ManagedComponentKind {
|
||||
Unknown,
|
||||
Script,
|
||||
Transform,
|
||||
Camera,
|
||||
Light,
|
||||
@@ -45,6 +47,14 @@ enum class ManagedComponentKind {
|
||||
MeshRenderer,
|
||||
};
|
||||
|
||||
struct ManagedComponentTypeInfo {
|
||||
ManagedComponentKind kind = ManagedComponentKind::Unknown;
|
||||
MonoClass* monoClass = nullptr;
|
||||
std::string assemblyName;
|
||||
std::string namespaceName;
|
||||
std::string className;
|
||||
};
|
||||
|
||||
MonoRootState& GetMonoRootState() {
|
||||
static MonoRootState state;
|
||||
return state;
|
||||
@@ -94,40 +104,62 @@ std::string MonoStringToUtf8(MonoString* stringObject) {
|
||||
return result;
|
||||
}
|
||||
|
||||
ManagedComponentKind ResolveManagedComponentKind(MonoReflectionType* reflectionType) {
|
||||
MonoScriptRuntime* GetActiveMonoScriptRuntime() {
|
||||
return dynamic_cast<MonoScriptRuntime*>(ScriptEngine::Get().GetRuntime());
|
||||
}
|
||||
|
||||
ManagedComponentTypeInfo ResolveManagedComponentTypeInfo(MonoReflectionType* reflectionType) {
|
||||
ManagedComponentTypeInfo typeInfo;
|
||||
if (!reflectionType) {
|
||||
return ManagedComponentKind::Unknown;
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
MonoType* monoType = mono_reflection_type_get_type(reflectionType);
|
||||
if (!monoType) {
|
||||
return ManagedComponentKind::Unknown;
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
MonoClass* monoClass = mono_class_from_mono_type(monoType);
|
||||
if (!monoClass) {
|
||||
return ManagedComponentKind::Unknown;
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
const std::string namespaceName = SafeString(mono_class_get_namespace(monoClass));
|
||||
const std::string className = SafeString(mono_class_get_name(monoClass));
|
||||
if (namespaceName == "XCEngine" && className == "Transform") {
|
||||
return ManagedComponentKind::Transform;
|
||||
typeInfo.monoClass = monoClass;
|
||||
typeInfo.namespaceName = SafeString(mono_class_get_namespace(monoClass));
|
||||
typeInfo.className = SafeString(mono_class_get_name(monoClass));
|
||||
|
||||
if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "Transform") {
|
||||
typeInfo.kind = ManagedComponentKind::Transform;
|
||||
return typeInfo;
|
||||
}
|
||||
if (namespaceName == "XCEngine" && className == "Camera") {
|
||||
return ManagedComponentKind::Camera;
|
||||
if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "Camera") {
|
||||
typeInfo.kind = ManagedComponentKind::Camera;
|
||||
return typeInfo;
|
||||
}
|
||||
if (namespaceName == "XCEngine" && className == "Light") {
|
||||
return ManagedComponentKind::Light;
|
||||
if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "Light") {
|
||||
typeInfo.kind = ManagedComponentKind::Light;
|
||||
return typeInfo;
|
||||
}
|
||||
if (namespaceName == "XCEngine" && className == "MeshFilter") {
|
||||
return ManagedComponentKind::MeshFilter;
|
||||
if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "MeshFilter") {
|
||||
typeInfo.kind = ManagedComponentKind::MeshFilter;
|
||||
return typeInfo;
|
||||
}
|
||||
if (namespaceName == "XCEngine" && className == "MeshRenderer") {
|
||||
return ManagedComponentKind::MeshRenderer;
|
||||
if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "MeshRenderer") {
|
||||
typeInfo.kind = ManagedComponentKind::MeshRenderer;
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
return ManagedComponentKind::Unknown;
|
||||
MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime();
|
||||
if (runtime
|
||||
&& runtime->IsClassAvailable(
|
||||
runtime->GetSettings().appAssemblyName,
|
||||
typeInfo.namespaceName,
|
||||
typeInfo.className)) {
|
||||
typeInfo.kind = ManagedComponentKind::Script;
|
||||
typeInfo.assemblyName = runtime->GetSettings().appAssemblyName;
|
||||
}
|
||||
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
Components::GameObject* FindGameObjectByUUIDRecursive(Components::GameObject* gameObject, uint64_t uuid) {
|
||||
@@ -214,6 +246,7 @@ bool HasNativeComponent(Components::GameObject* gameObject, ManagedComponentKind
|
||||
return gameObject->GetComponent<Components::MeshFilterComponent>() != nullptr;
|
||||
case ManagedComponentKind::MeshRenderer:
|
||||
return gameObject->GetComponent<Components::MeshRendererComponent>() != nullptr;
|
||||
case ManagedComponentKind::Script:
|
||||
case ManagedComponentKind::Unknown:
|
||||
return false;
|
||||
}
|
||||
@@ -245,6 +278,7 @@ Components::Component* AddOrGetNativeComponent(Components::GameObject* gameObjec
|
||||
return gameObject->GetComponent<Components::MeshRendererComponent>()
|
||||
? static_cast<Components::Component*>(gameObject->GetComponent<Components::MeshRendererComponent>())
|
||||
: static_cast<Components::Component*>(gameObject->AddComponent<Components::MeshRendererComponent>());
|
||||
case ManagedComponentKind::Script:
|
||||
case ManagedComponentKind::Unknown:
|
||||
return nullptr;
|
||||
}
|
||||
@@ -252,6 +286,28 @@ Components::Component* AddOrGetNativeComponent(Components::GameObject* gameObjec
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ScriptComponent* FindMatchingScriptComponent(
|
||||
Components::GameObject* gameObject,
|
||||
const ManagedComponentTypeInfo& typeInfo) {
|
||||
if (!gameObject || typeInfo.kind != ManagedComponentKind::Script) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (ScriptComponent* component : gameObject->GetComponents<ScriptComponent>()) {
|
||||
if (!component || !component->HasScriptClass()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (component->GetAssemblyName() == typeInfo.assemblyName
|
||||
&& component->GetNamespaceName() == typeInfo.namespaceName
|
||||
&& component->GetClassName() == typeInfo.className) {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Components::CameraComponent* FindCameraComponent(uint64_t gameObjectUUID) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
return gameObject ? gameObject->GetComponent<Components::CameraComponent>() : nullptr;
|
||||
@@ -337,21 +393,69 @@ void InternalCall_GameObject_SetActive(uint64_t gameObjectUUID, mono_bool active
|
||||
|
||||
mono_bool InternalCall_GameObject_HasComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
return HasNativeComponent(gameObject, ResolveManagedComponentKind(componentType)) ? 1 : 0;
|
||||
}
|
||||
|
||||
uint64_t InternalCall_GameObject_GetComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
if (!HasNativeComponent(gameObject, ResolveManagedComponentKind(componentType))) {
|
||||
return 0;
|
||||
const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(componentType);
|
||||
if (typeInfo.kind == ManagedComponentKind::Script) {
|
||||
return FindMatchingScriptComponent(gameObject, typeInfo) ? 1 : 0;
|
||||
}
|
||||
|
||||
return gameObjectUUID;
|
||||
return HasNativeComponent(gameObject, typeInfo.kind) ? 1 : 0;
|
||||
}
|
||||
|
||||
uint64_t InternalCall_GameObject_AddComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
|
||||
MonoObject* InternalCall_GameObject_GetComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
return AddOrGetNativeComponent(gameObject, ResolveManagedComponentKind(componentType)) ? gameObjectUUID : 0;
|
||||
if (!gameObject) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime();
|
||||
if (!runtime) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(componentType);
|
||||
if (typeInfo.kind == ManagedComponentKind::Script) {
|
||||
ScriptComponent* component = FindMatchingScriptComponent(gameObject, typeInfo);
|
||||
if (!component) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!runtime->HasManagedInstance(component)) {
|
||||
ScriptEngine::Get().OnScriptComponentEnabled(component);
|
||||
}
|
||||
|
||||
return runtime->GetManagedInstanceObject(component);
|
||||
}
|
||||
|
||||
if (!HasNativeComponent(gameObject, typeInfo.kind) || !typeInfo.monoClass) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return runtime->CreateManagedComponentWrapper(typeInfo.monoClass, gameObjectUUID);
|
||||
}
|
||||
|
||||
MonoObject* InternalCall_GameObject_AddComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
if (!gameObject) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime();
|
||||
if (!runtime) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(componentType);
|
||||
if (typeInfo.kind == ManagedComponentKind::Script) {
|
||||
ScriptComponent* component = gameObject->AddComponent<ScriptComponent>();
|
||||
component->SetScriptClass(typeInfo.assemblyName, typeInfo.namespaceName, typeInfo.className);
|
||||
return runtime->GetManagedInstanceObject(component);
|
||||
}
|
||||
|
||||
if (!AddOrGetNativeComponent(gameObject, typeInfo.kind) || !typeInfo.monoClass) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return runtime->CreateManagedComponentWrapper(typeInfo.monoClass, gameObjectUUID);
|
||||
}
|
||||
|
||||
uint64_t InternalCall_GameObject_Find(MonoString* name) {
|
||||
@@ -1199,6 +1303,11 @@ bool MonoScriptRuntime::HasManagedInstance(const ScriptComponent* component) con
|
||||
return instanceData != nullptr && GetManagedObject(*instanceData) != nullptr;
|
||||
}
|
||||
|
||||
MonoObject* MonoScriptRuntime::GetManagedInstanceObject(const ScriptComponent* component) const {
|
||||
const InstanceData* instanceData = FindInstance(component);
|
||||
return instanceData ? GetManagedObject(*instanceData) : nullptr;
|
||||
}
|
||||
|
||||
bool MonoScriptRuntime::TryGetFieldValue(
|
||||
const ScriptComponent* component,
|
||||
const std::string& fieldName,
|
||||
@@ -1766,6 +1875,37 @@ bool MonoScriptRuntime::ApplyStoredFields(
|
||||
return true;
|
||||
}
|
||||
|
||||
MonoObject* MonoScriptRuntime::CreateManagedComponentWrapper(MonoClass* componentClass, uint64_t gameObjectUUID) {
|
||||
if (!m_initialized || !componentClass || gameObjectUUID == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SetCurrentDomain();
|
||||
|
||||
MonoMethod* constructor = mono_class_get_method_from_name(componentClass, ".ctor", 1);
|
||||
if (!constructor) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MonoObject* managedObject = mono_object_new(m_appDomain, componentClass);
|
||||
if (!managedObject) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* args[1];
|
||||
uint64_t uuidArgument = gameObjectUUID;
|
||||
args[0] = &uuidArgument;
|
||||
|
||||
MonoObject* exception = nullptr;
|
||||
mono_runtime_invoke(constructor, managedObject, args, &exception);
|
||||
if (exception) {
|
||||
RecordException(exception);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return managedObject;
|
||||
}
|
||||
|
||||
MonoObject* MonoScriptRuntime::CreateManagedGameObject(uint64_t gameObjectUUID) {
|
||||
if (gameObjectUUID == 0 || !m_gameObjectClass || !m_gameObjectConstructor) {
|
||||
return nullptr;
|
||||
|
||||
@@ -37,7 +37,8 @@ void ScriptEngine::OnRuntimeStart(Components::Scene* scene) {
|
||||
CollectScriptComponents(root);
|
||||
}
|
||||
|
||||
for (const ScriptInstanceKey& key : m_scriptOrder) {
|
||||
const std::vector<ScriptInstanceKey> startupKeys = m_scriptOrder;
|
||||
for (const ScriptInstanceKey& key : startupKeys) {
|
||||
auto it = m_scriptStates.find(key);
|
||||
if (it == m_scriptStates.end()) {
|
||||
continue;
|
||||
@@ -87,7 +88,8 @@ void ScriptEngine::OnFixedUpdate(float fixedDeltaTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const ScriptInstanceKey& key : m_scriptOrder) {
|
||||
const std::vector<ScriptInstanceKey> updateKeys = m_scriptOrder;
|
||||
for (const ScriptInstanceKey& key : updateKeys) {
|
||||
auto it = m_scriptStates.find(key);
|
||||
if (it == m_scriptStates.end()) {
|
||||
continue;
|
||||
@@ -107,7 +109,8 @@ void ScriptEngine::OnUpdate(float deltaTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const ScriptInstanceKey& key : m_scriptOrder) {
|
||||
const std::vector<ScriptInstanceKey> updateKeys = m_scriptOrder;
|
||||
for (const ScriptInstanceKey& key : updateKeys) {
|
||||
auto it = m_scriptStates.find(key);
|
||||
if (it == m_scriptStates.end()) {
|
||||
continue;
|
||||
@@ -133,7 +136,8 @@ void ScriptEngine::OnLateUpdate(float deltaTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const ScriptInstanceKey& key : m_scriptOrder) {
|
||||
const std::vector<ScriptInstanceKey> updateKeys = m_scriptOrder;
|
||||
for (const ScriptInstanceKey& key : updateKeys) {
|
||||
auto it = m_scriptStates.find(key);
|
||||
if (it == m_scriptStates.end()) {
|
||||
continue;
|
||||
|
||||
@@ -72,6 +72,7 @@ set(XCENGINE_SCRIPT_CORE_SOURCES
|
||||
set(XCENGINE_GAME_SCRIPT_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/BuiltinComponentProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/AddComponentProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/ScriptComponentApiProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/RuntimeGameObjectProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/HierarchyProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/LifecycleProbe.cs
|
||||
|
||||
74
managed/GameScripts/ScriptComponentApiProbe.cs
Normal file
74
managed/GameScripts/ScriptComponentApiProbe.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using XCEngine;
|
||||
|
||||
namespace Gameplay
|
||||
{
|
||||
public sealed class ScriptComponentTargetProbe : MonoBehaviour
|
||||
{
|
||||
public int AwakeCount;
|
||||
public int StartCount;
|
||||
public int HostCallCount;
|
||||
public string HostName = string.Empty;
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
AwakeCount++;
|
||||
HostName = gameObject.Name;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
StartCount++;
|
||||
}
|
||||
|
||||
public void IncrementFromHost()
|
||||
{
|
||||
HostCallCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ScriptComponentApiProbe : MonoBehaviour
|
||||
{
|
||||
public bool InitialHasTarget;
|
||||
public bool InitialLookupReturnedNull;
|
||||
public bool AddedTarget;
|
||||
public bool HasTargetAfterAdd;
|
||||
public bool LookupSucceededAfterAdd;
|
||||
public bool TryGetSucceededAfterAdd;
|
||||
public bool ReturnedSameInstance;
|
||||
public bool TargetEnabledAfterAdd;
|
||||
public int ObservedTargetAwakeCount;
|
||||
public int ObservedTargetStartCount;
|
||||
public int ObservedTargetHostCallCount;
|
||||
public string ObservedTargetHostName = string.Empty;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
InitialHasTarget = HasComponent<ScriptComponentTargetProbe>();
|
||||
InitialLookupReturnedNull = GetComponent<ScriptComponentTargetProbe>() == null;
|
||||
|
||||
ScriptComponentTargetProbe added = AddComponent<ScriptComponentTargetProbe>();
|
||||
AddedTarget = added != null;
|
||||
HasTargetAfterAdd = HasComponent<ScriptComponentTargetProbe>();
|
||||
|
||||
ScriptComponentTargetProbe resolved = GetComponent<ScriptComponentTargetProbe>();
|
||||
TryGetSucceededAfterAdd = TryGetComponent(out ScriptComponentTargetProbe tried);
|
||||
LookupSucceededAfterAdd = resolved != null;
|
||||
ReturnedSameInstance = added != null
|
||||
&& ReferenceEquals(added, resolved)
|
||||
&& ReferenceEquals(added, tried);
|
||||
|
||||
if (added == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
added.IncrementFromHost();
|
||||
TargetEnabledAfterAdd = added.enabled;
|
||||
ObservedTargetAwakeCount = added.AwakeCount;
|
||||
ObservedTargetStartCount = added.StartCount;
|
||||
ObservedTargetHostCallCount = added.HostCallCount;
|
||||
ObservedTargetHostName = added.HostName ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,14 +64,12 @@ namespace XCEngine
|
||||
|
||||
public T GetComponent<T>() where T : Component
|
||||
{
|
||||
ulong componentOwnerUUID = InternalCalls.GameObject_GetComponent(UUID, typeof(T));
|
||||
return Component.Create<T>(componentOwnerUUID);
|
||||
return InternalCalls.GameObject_GetComponent(UUID, typeof(T)) as T;
|
||||
}
|
||||
|
||||
public T AddComponent<T>() where T : Component
|
||||
{
|
||||
ulong componentOwnerUUID = InternalCalls.GameObject_AddComponent(UUID, typeof(T));
|
||||
return Component.Create<T>(componentOwnerUUID);
|
||||
return InternalCalls.GameObject_AddComponent(UUID, typeof(T)) as T;
|
||||
}
|
||||
|
||||
public bool TryGetComponent<T>(out T component) where T : Component
|
||||
|
||||
@@ -36,10 +36,10 @@ namespace XCEngine
|
||||
internal static extern bool GameObject_HasComponent(ulong gameObjectUUID, Type componentType);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern ulong GameObject_GetComponent(ulong gameObjectUUID, Type componentType);
|
||||
internal static extern Component GameObject_GetComponent(ulong gameObjectUUID, Type componentType);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern ulong GameObject_AddComponent(ulong gameObjectUUID, Type componentType);
|
||||
internal static extern Component GameObject_AddComponent(ulong gameObjectUUID, Type componentType);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern ulong GameObject_Find(string name);
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user