feat(scripting): add runtime gameobject lifecycle api
This commit is contained in:
@@ -65,6 +65,8 @@ private:
|
||||
ScriptEngine() = default;
|
||||
|
||||
void CollectScriptComponents(Components::GameObject* gameObject);
|
||||
void EnsureTrackedScriptsReady(Components::GameObject* gameObject);
|
||||
void HandleGameObjectCreated(Components::GameObject* gameObject);
|
||||
ScriptInstanceState* TrackScriptComponent(ScriptComponent* component);
|
||||
ScriptInstanceState* FindState(const ScriptComponent* component);
|
||||
const ScriptInstanceState* FindState(const ScriptComponent* component) const;
|
||||
@@ -79,6 +81,7 @@ private:
|
||||
IScriptRuntime* m_runtime = &m_nullRuntime;
|
||||
Components::Scene* m_runtimeScene = nullptr;
|
||||
bool m_runtimeRunning = false;
|
||||
uint64_t m_runtimeSceneCreatedSubscription = 0;
|
||||
|
||||
std::unordered_map<ScriptInstanceKey, ScriptInstanceState, ScriptInstanceKeyHasher> m_scriptStates;
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
ScriptComponent* component = FindScriptComponentByUUID(scriptComponentUUID);
|
||||
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_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_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_SetEnabled", reinterpret_cast<const void*>(&InternalCall_Behaviour_SetEnabled));
|
||||
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) {
|
||||
const bool hadScriptClass = HasScriptClass();
|
||||
m_namespaceName = namespaceName;
|
||||
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) {
|
||||
const bool hadScriptClass = HasScriptClass();
|
||||
m_assemblyName = assemblyName;
|
||||
m_namespaceName = namespaceName;
|
||||
m_className = className;
|
||||
if (!hadScriptClass && HasScriptClass()) {
|
||||
ScriptEngine::Get().OnScriptComponentEnabled(this);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ScriptComponent::GetFullClassName() const {
|
||||
|
||||
@@ -28,6 +28,10 @@ void ScriptEngine::OnRuntimeStart(Components::Scene* scene) {
|
||||
m_runtimeScene = scene;
|
||||
m_runtimeRunning = true;
|
||||
m_runtime->OnRuntimeStart(scene);
|
||||
m_runtimeSceneCreatedSubscription = scene->OnGameObjectCreated().Subscribe(
|
||||
[this](Components::GameObject* gameObject) {
|
||||
HandleGameObjectCreated(gameObject);
|
||||
});
|
||||
|
||||
for (Components::GameObject* root : scene->GetRootGameObjects()) {
|
||||
CollectScriptComponents(root);
|
||||
@@ -49,6 +53,12 @@ void ScriptEngine::OnRuntimeStart(Components::Scene* scene) {
|
||||
}
|
||||
|
||||
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) {
|
||||
m_runtimeScene = nullptr;
|
||||
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) {
|
||||
if (!component || !component->GetGameObject()) {
|
||||
return nullptr;
|
||||
|
||||
@@ -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/RuntimeGameObjectProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/HierarchyProbe.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/LifecycleProbe.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 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
|
||||
{
|
||||
get => InternalCalls.GameObject_GetName(UUID) ?? string.Empty;
|
||||
@@ -32,6 +49,11 @@ namespace XCEngine
|
||||
InternalCalls.GameObject_SetActive(UUID, value);
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
InternalCalls.GameObject_Destroy(UUID);
|
||||
}
|
||||
|
||||
public Transform Transform => GetComponent<Transform>();
|
||||
public Transform transform => Transform;
|
||||
|
||||
|
||||
@@ -41,6 +41,15 @@ namespace XCEngine
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
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)]
|
||||
internal static extern bool Behaviour_GetEnabled(ulong scriptComponentUUID);
|
||||
|
||||
|
||||
@@ -562,6 +562,146 @@ TEST_F(MonoScriptRuntimeTest, GameObjectAddComponentApiCreatesBuiltinComponentsA
|
||||
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) {
|
||||
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||
GameObject* root = runtimeScene->CreateGameObject("Root");
|
||||
|
||||
@@ -236,4 +236,34 @@ TEST_F(ScriptEngineTest, DestroyingGameObjectWhileRuntimeRunningDestroysTrackedS
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user