feat(scripting): add runtime gameobject lifecycle api
This commit is contained in:
@@ -65,6 +65,8 @@ private:
|
|||||||
ScriptEngine() = default;
|
ScriptEngine() = default;
|
||||||
|
|
||||||
void CollectScriptComponents(Components::GameObject* gameObject);
|
void CollectScriptComponents(Components::GameObject* gameObject);
|
||||||
|
void EnsureTrackedScriptsReady(Components::GameObject* gameObject);
|
||||||
|
void HandleGameObjectCreated(Components::GameObject* gameObject);
|
||||||
ScriptInstanceState* TrackScriptComponent(ScriptComponent* component);
|
ScriptInstanceState* TrackScriptComponent(ScriptComponent* component);
|
||||||
ScriptInstanceState* FindState(const ScriptComponent* component);
|
ScriptInstanceState* FindState(const ScriptComponent* component);
|
||||||
const ScriptInstanceState* FindState(const ScriptComponent* component) const;
|
const ScriptInstanceState* FindState(const ScriptComponent* component) const;
|
||||||
@@ -79,6 +81,7 @@ private:
|
|||||||
IScriptRuntime* m_runtime = &m_nullRuntime;
|
IScriptRuntime* m_runtime = &m_nullRuntime;
|
||||||
Components::Scene* m_runtimeScene = nullptr;
|
Components::Scene* m_runtimeScene = nullptr;
|
||||||
bool m_runtimeRunning = false;
|
bool m_runtimeRunning = false;
|
||||||
|
uint64_t m_runtimeSceneCreatedSubscription = 0;
|
||||||
|
|
||||||
std::unordered_map<ScriptInstanceKey, ScriptInstanceState, ScriptInstanceKeyHasher> m_scriptStates;
|
std::unordered_map<ScriptInstanceKey, ScriptInstanceState, ScriptInstanceKeyHasher> m_scriptStates;
|
||||||
std::vector<ScriptInstanceKey> m_scriptOrder;
|
std::vector<ScriptInstanceKey> m_scriptOrder;
|
||||||
|
|||||||
@@ -354,6 +354,49 @@ uint64_t InternalCall_GameObject_AddComponent(uint64_t gameObjectUUID, MonoRefle
|
|||||||
return AddOrGetNativeComponent(gameObject, ResolveManagedComponentKind(componentType)) ? gameObjectUUID : 0;
|
return AddOrGetNativeComponent(gameObject, ResolveManagedComponentKind(componentType)) ? gameObjectUUID : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t InternalCall_GameObject_Find(MonoString* name) {
|
||||||
|
Components::Scene* scene = GetInternalCallScene();
|
||||||
|
if (!scene) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Components::GameObject* gameObject = scene->Find(MonoStringToUtf8(name));
|
||||||
|
return gameObject ? gameObject->GetUUID() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t InternalCall_GameObject_Create(MonoString* name, uint64_t parentGameObjectUUID) {
|
||||||
|
Components::Scene* scene = GetInternalCallScene();
|
||||||
|
if (!scene) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Components::GameObject* parent = nullptr;
|
||||||
|
if (parentGameObjectUUID != 0) {
|
||||||
|
parent = FindGameObjectByUUID(parentGameObjectUUID);
|
||||||
|
if (!parent || parent->GetScene() != scene) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string objectName = MonoStringToUtf8(name);
|
||||||
|
if (objectName.empty()) {
|
||||||
|
objectName = "GameObject";
|
||||||
|
}
|
||||||
|
|
||||||
|
Components::GameObject* created = scene->CreateGameObject(objectName, parent);
|
||||||
|
return created ? created->GetUUID() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternalCall_GameObject_Destroy(uint64_t gameObjectUUID) {
|
||||||
|
Components::Scene* scene = GetInternalCallScene();
|
||||||
|
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||||
|
if (!scene || !gameObject || gameObject->GetScene() != scene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scene->DestroyGameObject(gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
mono_bool InternalCall_Behaviour_GetEnabled(uint64_t scriptComponentUUID) {
|
mono_bool InternalCall_Behaviour_GetEnabled(uint64_t scriptComponentUUID) {
|
||||||
ScriptComponent* component = FindScriptComponentByUUID(scriptComponentUUID);
|
ScriptComponent* component = FindScriptComponentByUUID(scriptComponentUUID);
|
||||||
return (component && component->IsEnabled()) ? 1 : 0;
|
return (component && component->IsEnabled()) ? 1 : 0;
|
||||||
@@ -992,6 +1035,9 @@ void RegisterInternalCalls() {
|
|||||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_HasComponent", reinterpret_cast<const void*>(&InternalCall_GameObject_HasComponent));
|
mono_add_internal_call("XCEngine.InternalCalls::GameObject_HasComponent", reinterpret_cast<const void*>(&InternalCall_GameObject_HasComponent));
|
||||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponent", reinterpret_cast<const void*>(&InternalCall_GameObject_GetComponent));
|
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponent", reinterpret_cast<const void*>(&InternalCall_GameObject_GetComponent));
|
||||||
mono_add_internal_call("XCEngine.InternalCalls::GameObject_AddComponent", reinterpret_cast<const void*>(&InternalCall_GameObject_AddComponent));
|
mono_add_internal_call("XCEngine.InternalCalls::GameObject_AddComponent", reinterpret_cast<const void*>(&InternalCall_GameObject_AddComponent));
|
||||||
|
mono_add_internal_call("XCEngine.InternalCalls::GameObject_Find", reinterpret_cast<const void*>(&InternalCall_GameObject_Find));
|
||||||
|
mono_add_internal_call("XCEngine.InternalCalls::GameObject_Create", reinterpret_cast<const void*>(&InternalCall_GameObject_Create));
|
||||||
|
mono_add_internal_call("XCEngine.InternalCalls::GameObject_Destroy", reinterpret_cast<const void*>(&InternalCall_GameObject_Destroy));
|
||||||
mono_add_internal_call("XCEngine.InternalCalls::Behaviour_GetEnabled", reinterpret_cast<const void*>(&InternalCall_Behaviour_GetEnabled));
|
mono_add_internal_call("XCEngine.InternalCalls::Behaviour_GetEnabled", reinterpret_cast<const void*>(&InternalCall_Behaviour_GetEnabled));
|
||||||
mono_add_internal_call("XCEngine.InternalCalls::Behaviour_SetEnabled", reinterpret_cast<const void*>(&InternalCall_Behaviour_SetEnabled));
|
mono_add_internal_call("XCEngine.InternalCalls::Behaviour_SetEnabled", reinterpret_cast<const void*>(&InternalCall_Behaviour_SetEnabled));
|
||||||
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetLocalPosition", reinterpret_cast<const void*>(&InternalCall_Transform_GetLocalPosition));
|
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetLocalPosition", reinterpret_cast<const void*>(&InternalCall_Transform_GetLocalPosition));
|
||||||
|
|||||||
@@ -24,14 +24,22 @@ ScriptComponent::ScriptComponent()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ScriptComponent::SetScriptClass(const std::string& namespaceName, const std::string& className) {
|
void ScriptComponent::SetScriptClass(const std::string& namespaceName, const std::string& className) {
|
||||||
|
const bool hadScriptClass = HasScriptClass();
|
||||||
m_namespaceName = namespaceName;
|
m_namespaceName = namespaceName;
|
||||||
m_className = className;
|
m_className = className;
|
||||||
|
if (!hadScriptClass && HasScriptClass()) {
|
||||||
|
ScriptEngine::Get().OnScriptComponentEnabled(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptComponent::SetScriptClass(const std::string& assemblyName, const std::string& namespaceName, const std::string& className) {
|
void ScriptComponent::SetScriptClass(const std::string& assemblyName, const std::string& namespaceName, const std::string& className) {
|
||||||
|
const bool hadScriptClass = HasScriptClass();
|
||||||
m_assemblyName = assemblyName;
|
m_assemblyName = assemblyName;
|
||||||
m_namespaceName = namespaceName;
|
m_namespaceName = namespaceName;
|
||||||
m_className = className;
|
m_className = className;
|
||||||
|
if (!hadScriptClass && HasScriptClass()) {
|
||||||
|
ScriptEngine::Get().OnScriptComponentEnabled(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ScriptComponent::GetFullClassName() const {
|
std::string ScriptComponent::GetFullClassName() const {
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ void ScriptEngine::OnRuntimeStart(Components::Scene* scene) {
|
|||||||
m_runtimeScene = scene;
|
m_runtimeScene = scene;
|
||||||
m_runtimeRunning = true;
|
m_runtimeRunning = true;
|
||||||
m_runtime->OnRuntimeStart(scene);
|
m_runtime->OnRuntimeStart(scene);
|
||||||
|
m_runtimeSceneCreatedSubscription = scene->OnGameObjectCreated().Subscribe(
|
||||||
|
[this](Components::GameObject* gameObject) {
|
||||||
|
HandleGameObjectCreated(gameObject);
|
||||||
|
});
|
||||||
|
|
||||||
for (Components::GameObject* root : scene->GetRootGameObjects()) {
|
for (Components::GameObject* root : scene->GetRootGameObjects()) {
|
||||||
CollectScriptComponents(root);
|
CollectScriptComponents(root);
|
||||||
@@ -49,6 +53,12 @@ void ScriptEngine::OnRuntimeStart(Components::Scene* scene) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngine::OnRuntimeStop() {
|
void ScriptEngine::OnRuntimeStop() {
|
||||||
|
if (m_runtimeScene && m_runtimeSceneCreatedSubscription != 0) {
|
||||||
|
m_runtimeScene->OnGameObjectCreated().Unsubscribe(m_runtimeSceneCreatedSubscription);
|
||||||
|
m_runtimeScene->OnGameObjectCreated().ProcessUnsubscribes();
|
||||||
|
m_runtimeSceneCreatedSubscription = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_runtimeRunning) {
|
if (!m_runtimeRunning) {
|
||||||
m_runtimeScene = nullptr;
|
m_runtimeScene = nullptr;
|
||||||
m_scriptStates.clear();
|
m_scriptStates.clear();
|
||||||
@@ -209,6 +219,34 @@ void ScriptEngine::CollectScriptComponents(Components::GameObject* gameObject) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::EnsureTrackedScriptsReady(Components::GameObject* gameObject) {
|
||||||
|
if (!gameObject) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ScriptComponent* component : gameObject->GetComponents<ScriptComponent>()) {
|
||||||
|
ScriptInstanceState* state = FindState(component);
|
||||||
|
if (!state || !ShouldScriptRun(*state)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureScriptReady(*state, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Components::GameObject* child : gameObject->GetChildren()) {
|
||||||
|
EnsureTrackedScriptsReady(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::HandleGameObjectCreated(Components::GameObject* gameObject) {
|
||||||
|
if (!m_runtimeRunning || !gameObject || gameObject->GetScene() != m_runtimeScene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CollectScriptComponents(gameObject);
|
||||||
|
EnsureTrackedScriptsReady(gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
ScriptEngine::ScriptInstanceState* ScriptEngine::TrackScriptComponent(ScriptComponent* component) {
|
ScriptEngine::ScriptInstanceState* ScriptEngine::TrackScriptComponent(ScriptComponent* component) {
|
||||||
if (!component || !component->GetGameObject()) {
|
if (!component || !component->GetGameObject()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@@ -72,6 +72,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/RuntimeGameObjectProbe.cs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/HierarchyProbe.cs
|
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/HierarchyProbe.cs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/LifecycleProbe.cs
|
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/LifecycleProbe.cs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/MeshComponentProbe.cs
|
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/MeshComponentProbe.cs
|
||||||
|
|||||||
98
managed/GameScripts/RuntimeGameObjectProbe.cs
Normal file
98
managed/GameScripts/RuntimeGameObjectProbe.cs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
using XCEngine;
|
||||||
|
|
||||||
|
namespace Gameplay
|
||||||
|
{
|
||||||
|
public sealed class RuntimeGameObjectProbe : MonoBehaviour
|
||||||
|
{
|
||||||
|
public bool MissingBeforeCreate;
|
||||||
|
public bool CreatedRootSucceeded;
|
||||||
|
public bool CreatedChildSucceeded;
|
||||||
|
public bool FoundRootSucceeded;
|
||||||
|
public bool FoundChildSucceeded;
|
||||||
|
public string ObservedFoundRootName = string.Empty;
|
||||||
|
public string ObservedFoundChildParentName = string.Empty;
|
||||||
|
public int ObservedRootChildCountBeforeDestroy;
|
||||||
|
public bool CameraLookupSucceeded;
|
||||||
|
public bool MeshFilterLookupSucceeded;
|
||||||
|
public bool MeshRendererLookupSucceeded;
|
||||||
|
public float ObservedCameraFieldOfView;
|
||||||
|
public string ObservedMeshPath = string.Empty;
|
||||||
|
public int ObservedMaterialCount;
|
||||||
|
public string ObservedMaterial0Path = string.Empty;
|
||||||
|
public int ObservedRenderLayer;
|
||||||
|
public bool MissingChildAfterDestroy;
|
||||||
|
public bool FoundRootAfterDestroySucceeded;
|
||||||
|
public int ObservedRootChildCountAfterDestroy = -1;
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
MissingBeforeCreate = GameObject.Find("RuntimeCreatedRoot") == null;
|
||||||
|
|
||||||
|
GameObject root = GameObject.Create("RuntimeCreatedRoot");
|
||||||
|
GameObject child = GameObject.Create("RuntimeCreatedChild", root);
|
||||||
|
|
||||||
|
CreatedRootSucceeded = root != null;
|
||||||
|
CreatedChildSucceeded = child != null;
|
||||||
|
|
||||||
|
GameObject foundRoot = GameObject.Find("RuntimeCreatedRoot");
|
||||||
|
GameObject foundChild = GameObject.Find("RuntimeCreatedChild");
|
||||||
|
|
||||||
|
FoundRootSucceeded = foundRoot != null;
|
||||||
|
FoundChildSucceeded = foundChild != null;
|
||||||
|
|
||||||
|
if (foundRoot != null)
|
||||||
|
{
|
||||||
|
ObservedFoundRootName = foundRoot.name;
|
||||||
|
ObservedRootChildCountBeforeDestroy = foundRoot.transform.childCount;
|
||||||
|
|
||||||
|
Camera camera = foundRoot.AddComponent<Camera>();
|
||||||
|
MeshFilter meshFilter = foundRoot.AddComponent<MeshFilter>();
|
||||||
|
MeshRenderer meshRenderer = foundRoot.AddComponent<MeshRenderer>();
|
||||||
|
|
||||||
|
CameraLookupSucceeded = camera != null;
|
||||||
|
MeshFilterLookupSucceeded = meshFilter != null;
|
||||||
|
MeshRendererLookupSucceeded = meshRenderer != null;
|
||||||
|
|
||||||
|
if (camera != null)
|
||||||
|
{
|
||||||
|
camera.fieldOfView = 68.0f;
|
||||||
|
ObservedCameraFieldOfView = camera.fieldOfView;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meshFilter != null)
|
||||||
|
{
|
||||||
|
meshFilter.meshPath = "Meshes/runtime_created.mesh";
|
||||||
|
ObservedMeshPath = meshFilter.meshPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meshRenderer != null)
|
||||||
|
{
|
||||||
|
meshRenderer.SetMaterialPath(0, "Materials/runtime_created.mat");
|
||||||
|
meshRenderer.renderLayer = 4;
|
||||||
|
ObservedMaterialCount = meshRenderer.materialCount;
|
||||||
|
ObservedMaterial0Path = meshRenderer.GetMaterialPath(0);
|
||||||
|
ObservedRenderLayer = meshRenderer.renderLayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundChild != null && foundChild.transform.parent != null)
|
||||||
|
{
|
||||||
|
ObservedFoundChildParentName = foundChild.transform.parent.gameObject.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child != null)
|
||||||
|
{
|
||||||
|
child.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
MissingChildAfterDestroy = GameObject.Find("RuntimeCreatedChild") == null;
|
||||||
|
|
||||||
|
GameObject foundRootAfterDestroy = GameObject.Find("RuntimeCreatedRoot");
|
||||||
|
FoundRootAfterDestroySucceeded = foundRootAfterDestroy != null;
|
||||||
|
if (foundRootAfterDestroy != null)
|
||||||
|
{
|
||||||
|
ObservedRootChildCountAfterDestroy = foundRootAfterDestroy.transform.childCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,23 @@ namespace XCEngine
|
|||||||
|
|
||||||
public ulong UUID => m_uuid;
|
public ulong UUID => m_uuid;
|
||||||
|
|
||||||
|
public static GameObject Find(string name)
|
||||||
|
{
|
||||||
|
ulong uuid = InternalCalls.GameObject_Find(name ?? string.Empty);
|
||||||
|
return uuid != 0 ? new GameObject(uuid) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GameObject Create(string name)
|
||||||
|
{
|
||||||
|
return Create(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GameObject Create(string name, GameObject parent)
|
||||||
|
{
|
||||||
|
ulong uuid = InternalCalls.GameObject_Create(name ?? string.Empty, parent?.UUID ?? 0);
|
||||||
|
return uuid != 0 ? new GameObject(uuid) : null;
|
||||||
|
}
|
||||||
|
|
||||||
public string Name
|
public string Name
|
||||||
{
|
{
|
||||||
get => InternalCalls.GameObject_GetName(UUID) ?? string.Empty;
|
get => InternalCalls.GameObject_GetName(UUID) ?? string.Empty;
|
||||||
@@ -32,6 +49,11 @@ namespace XCEngine
|
|||||||
InternalCalls.GameObject_SetActive(UUID, value);
|
InternalCalls.GameObject_SetActive(UUID, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Destroy()
|
||||||
|
{
|
||||||
|
InternalCalls.GameObject_Destroy(UUID);
|
||||||
|
}
|
||||||
|
|
||||||
public Transform Transform => GetComponent<Transform>();
|
public Transform Transform => GetComponent<Transform>();
|
||||||
public Transform transform => Transform;
|
public Transform transform => Transform;
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,15 @@ namespace XCEngine
|
|||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern ulong GameObject_AddComponent(ulong gameObjectUUID, Type componentType);
|
internal static extern ulong GameObject_AddComponent(ulong gameObjectUUID, Type componentType);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern ulong GameObject_Find(string name);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern ulong GameObject_Create(string name, ulong parentGameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void GameObject_Destroy(ulong gameObjectUUID);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern bool Behaviour_GetEnabled(ulong scriptComponentUUID);
|
internal static extern bool Behaviour_GetEnabled(ulong scriptComponentUUID);
|
||||||
|
|
||||||
|
|||||||
@@ -562,6 +562,146 @@ TEST_F(MonoScriptRuntimeTest, GameObjectAddComponentApiCreatesBuiltinComponentsA
|
|||||||
EXPECT_EQ(meshRenderer->GetRenderLayer(), 6u);
|
EXPECT_EQ(meshRenderer->GetRenderLayer(), 6u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, GameObjectRuntimeApiCreatesFindsAndDestroysSceneObjects) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
ScriptComponent* component = AddScript(host, "Gameplay", "RuntimeGameObjectProbe");
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
bool missingBeforeCreate = false;
|
||||||
|
bool createdRootSucceeded = false;
|
||||||
|
bool createdChildSucceeded = false;
|
||||||
|
bool foundRootSucceeded = false;
|
||||||
|
bool foundChildSucceeded = false;
|
||||||
|
std::string observedFoundRootName;
|
||||||
|
std::string observedFoundChildParentName;
|
||||||
|
int32_t observedRootChildCountBeforeDestroy = 0;
|
||||||
|
bool cameraLookupSucceeded = false;
|
||||||
|
bool meshFilterLookupSucceeded = false;
|
||||||
|
bool meshRendererLookupSucceeded = false;
|
||||||
|
float observedCameraFieldOfView = 0.0f;
|
||||||
|
std::string observedMeshPath;
|
||||||
|
int32_t observedMaterialCount = 0;
|
||||||
|
std::string observedMaterial0Path;
|
||||||
|
int32_t observedRenderLayer = 0;
|
||||||
|
bool missingChildAfterDestroy = false;
|
||||||
|
bool foundRootAfterDestroySucceeded = false;
|
||||||
|
int32_t observedRootChildCountAfterDestroy = -1;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MissingBeforeCreate", missingBeforeCreate));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CreatedRootSucceeded", createdRootSucceeded));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CreatedChildSucceeded", createdChildSucceeded));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "FoundRootSucceeded", foundRootSucceeded));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "FoundChildSucceeded", foundChildSucceeded));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFoundRootName", observedFoundRootName));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFoundChildParentName", observedFoundChildParentName));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRootChildCountBeforeDestroy", observedRootChildCountBeforeDestroy));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CameraLookupSucceeded", cameraLookupSucceeded));
|
||||||
|
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, "ObservedMeshPath", observedMeshPath));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterialCount", observedMaterialCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial0Path", observedMaterial0Path));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayer", observedRenderLayer));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MissingChildAfterDestroy", missingChildAfterDestroy));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "FoundRootAfterDestroySucceeded", foundRootAfterDestroySucceeded));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRootChildCountAfterDestroy", observedRootChildCountAfterDestroy));
|
||||||
|
|
||||||
|
EXPECT_TRUE(missingBeforeCreate);
|
||||||
|
EXPECT_TRUE(createdRootSucceeded);
|
||||||
|
EXPECT_TRUE(createdChildSucceeded);
|
||||||
|
EXPECT_TRUE(foundRootSucceeded);
|
||||||
|
EXPECT_TRUE(foundChildSucceeded);
|
||||||
|
EXPECT_EQ(observedFoundRootName, "RuntimeCreatedRoot");
|
||||||
|
EXPECT_EQ(observedFoundChildParentName, "RuntimeCreatedRoot");
|
||||||
|
EXPECT_EQ(observedRootChildCountBeforeDestroy, 1);
|
||||||
|
EXPECT_TRUE(cameraLookupSucceeded);
|
||||||
|
EXPECT_TRUE(meshFilterLookupSucceeded);
|
||||||
|
EXPECT_TRUE(meshRendererLookupSucceeded);
|
||||||
|
EXPECT_FLOAT_EQ(observedCameraFieldOfView, 68.0f);
|
||||||
|
EXPECT_EQ(observedMeshPath, "Meshes/runtime_created.mesh");
|
||||||
|
EXPECT_EQ(observedMaterialCount, 1);
|
||||||
|
EXPECT_EQ(observedMaterial0Path, "Materials/runtime_created.mat");
|
||||||
|
EXPECT_EQ(observedRenderLayer, 4);
|
||||||
|
EXPECT_TRUE(missingChildAfterDestroy);
|
||||||
|
EXPECT_TRUE(foundRootAfterDestroySucceeded);
|
||||||
|
EXPECT_EQ(observedRootChildCountAfterDestroy, 0);
|
||||||
|
|
||||||
|
GameObject* createdRoot = runtimeScene->Find("RuntimeCreatedRoot");
|
||||||
|
ASSERT_NE(createdRoot, nullptr);
|
||||||
|
EXPECT_EQ(runtimeScene->Find("RuntimeCreatedChild"), nullptr);
|
||||||
|
EXPECT_EQ(createdRoot->GetParent(), nullptr);
|
||||||
|
EXPECT_EQ(createdRoot->GetChildCount(), 0u);
|
||||||
|
|
||||||
|
CameraComponent* camera = createdRoot->GetComponent<CameraComponent>();
|
||||||
|
MeshFilterComponent* meshFilter = createdRoot->GetComponent<MeshFilterComponent>();
|
||||||
|
MeshRendererComponent* meshRenderer = createdRoot->GetComponent<MeshRendererComponent>();
|
||||||
|
|
||||||
|
ASSERT_NE(camera, nullptr);
|
||||||
|
ASSERT_NE(meshFilter, nullptr);
|
||||||
|
ASSERT_NE(meshRenderer, nullptr);
|
||||||
|
EXPECT_FLOAT_EQ(camera->GetFieldOfView(), 68.0f);
|
||||||
|
EXPECT_EQ(meshFilter->GetMeshPath(), "Meshes/runtime_created.mesh");
|
||||||
|
ASSERT_EQ(meshRenderer->GetMaterialCount(), 1u);
|
||||||
|
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "Materials/runtime_created.mat");
|
||||||
|
EXPECT_EQ(meshRenderer->GetRenderLayer(), 4u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, RuntimeCreatedScriptComponentCreatesManagedInstanceAfterClassAssignment) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
|
||||||
|
GameObject* spawned = runtimeScene->CreateGameObject("RuntimeSpawned");
|
||||||
|
ScriptComponent* component = spawned->AddComponent<ScriptComponent>();
|
||||||
|
component->GetFieldStorage().SetFieldValue("Label", "RuntimeConfigured");
|
||||||
|
component->SetScriptClass("GameScripts", "Gameplay", "LifecycleProbe");
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->HasManagedInstance(component));
|
||||||
|
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
int32_t awakeCount = 0;
|
||||||
|
int32_t enableCount = 0;
|
||||||
|
int32_t startCount = 0;
|
||||||
|
int32_t updateCount = 0;
|
||||||
|
std::string label;
|
||||||
|
std::string observedGameObjectName;
|
||||||
|
bool wasAwakened = false;
|
||||||
|
bool observedEnabled = false;
|
||||||
|
bool observedActiveSelf = false;
|
||||||
|
bool observedActiveInHierarchy = false;
|
||||||
|
bool observedIsActiveAndEnabled = false;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AwakeCount", awakeCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "EnableCount", enableCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", label));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedGameObjectName", observedGameObjectName));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "WasAwakened", wasAwakened));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEnabled", observedEnabled));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveSelf", observedActiveSelf));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveInHierarchy", observedActiveInHierarchy));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIsActiveAndEnabled", observedIsActiveAndEnabled));
|
||||||
|
|
||||||
|
EXPECT_EQ(awakeCount, 1);
|
||||||
|
EXPECT_EQ(enableCount, 1);
|
||||||
|
EXPECT_EQ(startCount, 1);
|
||||||
|
EXPECT_EQ(updateCount, 1);
|
||||||
|
EXPECT_EQ(label, "RuntimeConfigured|Awake");
|
||||||
|
EXPECT_EQ(observedGameObjectName, "RuntimeSpawned_Managed");
|
||||||
|
EXPECT_TRUE(wasAwakened);
|
||||||
|
EXPECT_TRUE(observedEnabled);
|
||||||
|
EXPECT_TRUE(observedActiveSelf);
|
||||||
|
EXPECT_TRUE(observedActiveInHierarchy);
|
||||||
|
EXPECT_TRUE(observedIsActiveAndEnabled);
|
||||||
|
EXPECT_EQ(spawned->GetName(), "RuntimeSpawned_Managed");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(MonoScriptRuntimeTest, TransformHierarchyApiExposesParentChildAndReparenting) {
|
TEST_F(MonoScriptRuntimeTest, TransformHierarchyApiExposesParentChildAndReparenting) {
|
||||||
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
GameObject* root = runtimeScene->CreateGameObject("Root");
|
GameObject* root = runtimeScene->CreateGameObject("Root");
|
||||||
|
|||||||
@@ -236,4 +236,34 @@ TEST_F(ScriptEngineTest, DestroyingGameObjectWhileRuntimeRunningDestroysTrackedS
|
|||||||
EXPECT_EQ(engine->GetTrackedScriptCount(), 0u);
|
EXPECT_EQ(engine->GetTrackedScriptCount(), 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ScriptEngineTest, RuntimeCreatedScriptComponentIsTrackedImmediatelyAndStartsOnNextUpdate) {
|
||||||
|
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
runtime.Clear();
|
||||||
|
|
||||||
|
GameObject* spawned = runtimeScene->CreateGameObject("Spawned");
|
||||||
|
ScriptComponent* component = AddScriptComponent(spawned, "Gameplay", "RuntimeSpawned");
|
||||||
|
|
||||||
|
EXPECT_EQ(engine->GetTrackedScriptCount(), 1u);
|
||||||
|
EXPECT_TRUE(engine->HasTrackedScriptComponent(component));
|
||||||
|
EXPECT_TRUE(engine->HasRuntimeInstance(component));
|
||||||
|
|
||||||
|
const std::vector<std::string> expectedBeforeUpdate = {
|
||||||
|
"Create:Spawned:Gameplay.RuntimeSpawned",
|
||||||
|
"Awake:Spawned:Gameplay.RuntimeSpawned",
|
||||||
|
"OnEnable:Spawned:Gameplay.RuntimeSpawned"
|
||||||
|
};
|
||||||
|
EXPECT_EQ(runtime.events, expectedBeforeUpdate);
|
||||||
|
|
||||||
|
runtime.Clear();
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
const std::vector<std::string> expectedAfterUpdate = {
|
||||||
|
"Start:Spawned:Gameplay.RuntimeSpawned",
|
||||||
|
"Update:Spawned:Gameplay.RuntimeSpawned"
|
||||||
|
};
|
||||||
|
EXPECT_EQ(runtime.events, expectedAfterUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
Reference in New Issue
Block a user