feat(scripting): add mono csharp runtime foundation
This commit is contained in:
@@ -6,8 +6,16 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
|
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
|
option(XCENGINE_ENABLE_MONO_SCRIPTING "Build the Mono-based C# scripting runtime" ON)
|
||||||
|
set(
|
||||||
|
XCENGINE_MONO_ROOT_DIR
|
||||||
|
"${CMAKE_SOURCE_DIR}/参考/Fermion/Fermion/external/mono"
|
||||||
|
CACHE PATH
|
||||||
|
"Path to the bundled Mono distribution used by the scripting runtime")
|
||||||
|
|
||||||
add_subdirectory(engine)
|
add_subdirectory(engine)
|
||||||
add_subdirectory(editor)
|
add_subdirectory(editor)
|
||||||
|
add_subdirectory(managed)
|
||||||
add_subdirectory(mvs/RenderDoc)
|
add_subdirectory(mvs/RenderDoc)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
add_subdirectory(tests/opengl)
|
add_subdirectory(tests/opengl)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.15)
|
|||||||
project(XCEngineLib)
|
project(XCEngineLib)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set(XCENGINE_VULKAN_SDK_HINT "$ENV{VULKAN_SDK}")
|
set(XCENGINE_VULKAN_SDK_HINT "$ENV{VULKAN_SDK}")
|
||||||
if(NOT EXISTS "${XCENGINE_VULKAN_SDK_HINT}/Lib/vulkan-1.lib")
|
if(NOT EXISTS "${XCENGINE_VULKAN_SDK_HINT}/Lib/vulkan-1.lib")
|
||||||
file(GLOB XCENGINE_VULKAN_SDK_DIRS "D:/VulkanSDK/*")
|
file(GLOB XCENGINE_VULKAN_SDK_DIRS "D:/VulkanSDK/*")
|
||||||
@@ -18,8 +20,6 @@ endif()
|
|||||||
|
|
||||||
find_package(Vulkan REQUIRED)
|
find_package(Vulkan REQUIRED)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
add_library(XCEngine STATIC
|
add_library(XCEngine STATIC
|
||||||
# Core (Types, RefCounted, SmartPtr, Event)
|
# Core (Types, RefCounted, SmartPtr, Event)
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Types.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Types.h
|
||||||
@@ -149,6 +149,7 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/D3D12/D3D12ResourceView.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/D3D12/D3D12ResourceView.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/D3D12/D3D12QueryHeap.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/D3D12/D3D12QueryHeap.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/D3D12/D3D12RenderPass.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/D3D12/D3D12RenderPass.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/D3D12/D3D12Framebuffer.cpp
|
||||||
|
|
||||||
# Vulkan RHI
|
# Vulkan RHI
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanCommon.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanCommon.h
|
||||||
@@ -171,7 +172,6 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanSwapChain.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanSwapChain.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanDevice.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanDevice.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanScreenshot.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanScreenshot.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/D3D12/D3D12Framebuffer.cpp
|
|
||||||
|
|
||||||
# OpenGL RHI
|
# OpenGL RHI
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/OpenGL/OpenGLBuffer.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/OpenGL/OpenGLBuffer.h
|
||||||
@@ -332,8 +332,10 @@ add_library(XCEngine STATIC
|
|||||||
|
|
||||||
# Scene
|
# Scene
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/Scene.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/Scene.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/SceneRuntime.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/SceneManager.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/SceneManager.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/Scene.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/Scene.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/SceneRuntime.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/SceneManager.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/SceneManager.cpp
|
||||||
|
|
||||||
# Platform
|
# Platform
|
||||||
@@ -413,6 +415,49 @@ target_link_libraries(XCEngine PUBLIC
|
|||||||
Vulkan::Vulkan
|
Vulkan::Vulkan
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(XCENGINE_ENABLE_MONO_SCRIPTING)
|
||||||
|
set(XCENGINE_MONO_INCLUDE_DIR "${XCENGINE_MONO_ROOT_DIR}/include")
|
||||||
|
set(XCENGINE_MONO_STATIC_LIBRARY "${XCENGINE_MONO_ROOT_DIR}/lib/libmono-static-sgen.lib")
|
||||||
|
set(XCENGINE_MONO_POSIX_HELPER_LIBRARY "${XCENGINE_MONO_ROOT_DIR}/lib/MonoPosixHelper.lib")
|
||||||
|
|
||||||
|
foreach(XCENGINE_MONO_REQUIRED_PATH
|
||||||
|
"${XCENGINE_MONO_INCLUDE_DIR}"
|
||||||
|
"${XCENGINE_MONO_STATIC_LIBRARY}"
|
||||||
|
"${XCENGINE_MONO_POSIX_HELPER_LIBRARY}")
|
||||||
|
if(NOT EXISTS "${XCENGINE_MONO_REQUIRED_PATH}")
|
||||||
|
message(FATAL_ERROR "Required Mono dependency is missing: ${XCENGINE_MONO_REQUIRED_PATH}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
add_library(XCEMono STATIC IMPORTED)
|
||||||
|
set_target_properties(XCEMono PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${XCENGINE_MONO_STATIC_LIBRARY}"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(XCEngine PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Scripting/Mono/MonoScriptRuntime.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(XCEngine PRIVATE
|
||||||
|
${XCENGINE_MONO_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(XCEngine PUBLIC
|
||||||
|
XCEMono
|
||||||
|
${XCENGINE_MONO_POSIX_HELPER_LIBRARY}
|
||||||
|
ws2_32
|
||||||
|
bcrypt
|
||||||
|
version
|
||||||
|
iphlpapi
|
||||||
|
winmm
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(XCEngine PRIVATE
|
||||||
|
XCENGINE_ENABLE_MONO_SCRIPTING
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
target_compile_options(XCEngine PRIVATE /W3 /FS)
|
target_compile_options(XCEngine PRIVATE /W3 /FS)
|
||||||
else()
|
else()
|
||||||
|
|||||||
26
engine/include/XCEngine/Scene/SceneRuntime.h
Normal file
26
engine/include/XCEngine/Scene/SceneRuntime.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <XCEngine/Scene/Scene.h>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
class SceneRuntime {
|
||||||
|
public:
|
||||||
|
void Start(Scene* scene);
|
||||||
|
void Stop();
|
||||||
|
|
||||||
|
void FixedUpdate(float fixedDeltaTime);
|
||||||
|
void Update(float deltaTime);
|
||||||
|
void LateUpdate(float deltaTime);
|
||||||
|
|
||||||
|
bool IsRunning() const { return m_running; }
|
||||||
|
Scene* GetScene() const { return m_scene; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Scene* m_scene = nullptr;
|
||||||
|
bool m_running = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
185
engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h
Normal file
185
engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <XCEngine/Scripting/IScriptRuntime.h>
|
||||||
|
#include <XCEngine/Scripting/ScriptField.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
typedef struct _MonoDomain MonoDomain;
|
||||||
|
typedef struct _MonoAssembly MonoAssembly;
|
||||||
|
typedef struct _MonoImage MonoImage;
|
||||||
|
typedef struct _MonoClass MonoClass;
|
||||||
|
typedef struct _MonoObject MonoObject;
|
||||||
|
typedef struct _MonoMethod MonoMethod;
|
||||||
|
typedef struct _MonoClassField MonoClassField;
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Scripting {
|
||||||
|
|
||||||
|
class ScriptComponent;
|
||||||
|
|
||||||
|
class MonoScriptRuntime : public IScriptRuntime {
|
||||||
|
public:
|
||||||
|
struct Settings {
|
||||||
|
std::filesystem::path assemblyDirectory;
|
||||||
|
std::filesystem::path corlibDirectory;
|
||||||
|
std::filesystem::path coreAssemblyPath;
|
||||||
|
std::filesystem::path appAssemblyPath;
|
||||||
|
std::string coreAssemblyName = "XCEngine.ScriptCore";
|
||||||
|
std::string appAssemblyName = "GameScripts";
|
||||||
|
std::string baseNamespace = "XCEngine";
|
||||||
|
std::string baseClassName = "MonoBehaviour";
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit MonoScriptRuntime(Settings settings = {});
|
||||||
|
~MonoScriptRuntime() override;
|
||||||
|
|
||||||
|
bool Initialize();
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
bool IsInitialized() const { return m_initialized; }
|
||||||
|
const Settings& GetSettings() const { return m_settings; }
|
||||||
|
const std::string& GetLastError() const { return m_lastError; }
|
||||||
|
|
||||||
|
bool IsClassAvailable(
|
||||||
|
const std::string& assemblyName,
|
||||||
|
const std::string& namespaceName,
|
||||||
|
const std::string& className) const;
|
||||||
|
std::vector<std::string> GetScriptClassNames(const std::string& assemblyName = std::string()) const;
|
||||||
|
|
||||||
|
bool HasManagedInstance(const ScriptComponent* component) const;
|
||||||
|
size_t GetManagedInstanceCount() const { return m_instances.size(); }
|
||||||
|
|
||||||
|
bool TryGetFieldValue(
|
||||||
|
const ScriptComponent* component,
|
||||||
|
const std::string& fieldName,
|
||||||
|
ScriptFieldValue& outValue) const;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool TryGetFieldValue(const ScriptComponent* component, const std::string& fieldName, T& outValue) const {
|
||||||
|
ScriptFieldValue value;
|
||||||
|
if (!TryGetFieldValue(component, fieldName, value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* typedValue = std::get_if<T>(&value);
|
||||||
|
if (!typedValue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outValue = *typedValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnRuntimeStart(Components::Scene* scene) override;
|
||||||
|
void OnRuntimeStop(Components::Scene* scene) override;
|
||||||
|
bool CreateScriptInstance(const ScriptRuntimeContext& context) override;
|
||||||
|
void DestroyScriptInstance(const ScriptRuntimeContext& context) override;
|
||||||
|
void InvokeMethod(const ScriptRuntimeContext& context, ScriptLifecycleMethod method, float deltaTime) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr size_t LifecycleMethodCount = 8;
|
||||||
|
|
||||||
|
struct FieldMetadata {
|
||||||
|
ScriptFieldType type = ScriptFieldType::None;
|
||||||
|
MonoClassField* field = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ClassMetadata {
|
||||||
|
std::string assemblyName;
|
||||||
|
std::string namespaceName;
|
||||||
|
std::string className;
|
||||||
|
std::string fullName;
|
||||||
|
MonoClass* monoClass = nullptr;
|
||||||
|
std::array<MonoMethod*, LifecycleMethodCount> lifecycleMethods{};
|
||||||
|
std::unordered_map<std::string, FieldMetadata> fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InstanceKey {
|
||||||
|
uint64_t gameObjectUUID = 0;
|
||||||
|
uint64_t scriptComponentUUID = 0;
|
||||||
|
|
||||||
|
bool operator==(const InstanceKey& other) const {
|
||||||
|
return gameObjectUUID == other.gameObjectUUID
|
||||||
|
&& scriptComponentUUID == other.scriptComponentUUID;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InstanceKeyHasher {
|
||||||
|
size_t operator()(const InstanceKey& key) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InstanceData {
|
||||||
|
const ClassMetadata* classMetadata = nullptr;
|
||||||
|
uint32_t gcHandle = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResolveSettings();
|
||||||
|
bool InitializeRootDomain();
|
||||||
|
bool CreateAppDomain();
|
||||||
|
void DestroyAppDomain();
|
||||||
|
void SetCurrentDomain() const;
|
||||||
|
|
||||||
|
bool LoadAssemblies();
|
||||||
|
bool DiscoverScriptClasses();
|
||||||
|
void DiscoverScriptClassesInImage(const std::string& assemblyName, MonoImage* image);
|
||||||
|
bool IsMonoBehaviourSubclass(MonoClass* monoClass) const;
|
||||||
|
|
||||||
|
ScriptFieldType MapMonoFieldType(MonoClassField* field) const;
|
||||||
|
|
||||||
|
static const char* ToLifecycleMethodName(ScriptLifecycleMethod method);
|
||||||
|
|
||||||
|
const ClassMetadata* FindClassMetadata(
|
||||||
|
const std::string& assemblyName,
|
||||||
|
const std::string& namespaceName,
|
||||||
|
const std::string& className) const;
|
||||||
|
|
||||||
|
InstanceData* FindInstance(const ScriptRuntimeContext& context);
|
||||||
|
const InstanceData* FindInstance(const ScriptRuntimeContext& context) const;
|
||||||
|
const InstanceData* FindInstance(const ScriptComponent* component) const;
|
||||||
|
|
||||||
|
MonoObject* GetManagedObject(const InstanceData& instanceData) const;
|
||||||
|
|
||||||
|
bool ApplyContextFields(const ScriptRuntimeContext& context, MonoObject* instance);
|
||||||
|
bool ApplyStoredFields(const ScriptRuntimeContext& context, const ClassMetadata& metadata, MonoObject* instance);
|
||||||
|
MonoObject* CreateManagedGameObject(uint64_t gameObjectUUID);
|
||||||
|
bool TryExtractGameObjectReference(MonoObject* managedObject, GameObjectReference& outReference) const;
|
||||||
|
bool TrySetFieldValue(MonoObject* instance, const FieldMetadata& fieldMetadata, const ScriptFieldValue& value);
|
||||||
|
bool TryReadFieldValue(MonoObject* instance, const FieldMetadata& fieldMetadata, ScriptFieldValue& outValue) const;
|
||||||
|
|
||||||
|
void ClearManagedInstances();
|
||||||
|
void ClearClassCache();
|
||||||
|
bool InvokeManagedMethod(MonoObject* instance, MonoMethod* method);
|
||||||
|
void RecordException(MonoObject* exception);
|
||||||
|
void SetError(const std::string& error);
|
||||||
|
|
||||||
|
Settings m_settings;
|
||||||
|
Components::Scene* m_activeScene = nullptr;
|
||||||
|
|
||||||
|
bool m_initialized = false;
|
||||||
|
std::string m_lastError;
|
||||||
|
|
||||||
|
MonoDomain* m_appDomain = nullptr;
|
||||||
|
MonoAssembly* m_coreAssembly = nullptr;
|
||||||
|
MonoAssembly* m_appAssembly = nullptr;
|
||||||
|
MonoImage* m_coreImage = nullptr;
|
||||||
|
MonoImage* m_appImage = nullptr;
|
||||||
|
MonoClass* m_componentClass = nullptr;
|
||||||
|
MonoClass* m_behaviourClass = nullptr;
|
||||||
|
MonoClass* m_gameObjectClass = nullptr;
|
||||||
|
MonoClass* m_monoBehaviourClass = nullptr;
|
||||||
|
MonoMethod* m_gameObjectConstructor = nullptr;
|
||||||
|
MonoClassField* m_managedGameObjectUUIDField = nullptr;
|
||||||
|
MonoClassField* m_gameObjectUUIDField = nullptr;
|
||||||
|
MonoClassField* m_scriptComponentUUIDField = nullptr;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, ClassMetadata> m_classes;
|
||||||
|
std::unordered_map<InstanceKey, InstanceData, InstanceKeyHasher> m_instances;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Scripting
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -228,30 +228,27 @@ void TransformComponent::LookAt(const Math::Vector3& target, const Math::Vector3
|
|||||||
void TransformComponent::Rotate(const Math::Vector3& eulers, Space relativeTo) {
|
void TransformComponent::Rotate(const Math::Vector3& eulers, Space relativeTo) {
|
||||||
Math::Quaternion rotation = Math::Quaternion::FromEulerAngles(eulers * Math::DEG_TO_RAD);
|
Math::Quaternion rotation = Math::Quaternion::FromEulerAngles(eulers * Math::DEG_TO_RAD);
|
||||||
if (relativeTo == Space::Self) {
|
if (relativeTo == Space::Self) {
|
||||||
m_localRotation = m_localRotation * rotation;
|
SetRotation(GetRotation() * rotation);
|
||||||
} else {
|
} else {
|
||||||
m_localRotation = rotation * m_localRotation;
|
SetRotation(rotation * GetRotation());
|
||||||
}
|
}
|
||||||
SetDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransformComponent::Rotate(const Math::Vector3& axis, float angle, Space relativeTo) {
|
void TransformComponent::Rotate(const Math::Vector3& axis, float angle, Space relativeTo) {
|
||||||
Math::Quaternion rotation = Math::Quaternion::FromAxisAngle(axis, angle * Math::DEG_TO_RAD);
|
Math::Quaternion rotation = Math::Quaternion::FromAxisAngle(axis, angle * Math::DEG_TO_RAD);
|
||||||
if (relativeTo == Space::Self) {
|
if (relativeTo == Space::Self) {
|
||||||
m_localRotation = m_localRotation * rotation;
|
SetRotation(GetRotation() * rotation);
|
||||||
} else {
|
} else {
|
||||||
m_localRotation = rotation * m_localRotation;
|
SetRotation(rotation * GetRotation());
|
||||||
}
|
}
|
||||||
SetDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransformComponent::Translate(const Math::Vector3& translation, Space relativeTo) {
|
void TransformComponent::Translate(const Math::Vector3& translation, Space relativeTo) {
|
||||||
if (relativeTo == Space::Self) {
|
if (relativeTo == Space::Self) {
|
||||||
m_localPosition = m_localPosition + translation;
|
SetPosition(GetPosition() + TransformDirection(translation));
|
||||||
} else {
|
} else {
|
||||||
m_localPosition = m_localPosition + translation;
|
SetPosition(GetPosition() + translation);
|
||||||
}
|
}
|
||||||
SetDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Math::Vector3 TransformComponent::TransformPoint(const Math::Vector3& point) const {
|
Math::Vector3 TransformComponent::TransformPoint(const Math::Vector3& point) const {
|
||||||
|
|||||||
64
engine/src/Scene/SceneRuntime.cpp
Normal file
64
engine/src/Scene/SceneRuntime.cpp
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#include "Scene/SceneRuntime.h"
|
||||||
|
|
||||||
|
#include "Scripting/ScriptEngine.h"
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
void SceneRuntime::Start(Scene* scene) {
|
||||||
|
if (m_running && m_scene == scene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stop();
|
||||||
|
|
||||||
|
if (!scene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_scene = scene;
|
||||||
|
m_running = true;
|
||||||
|
Scripting::ScriptEngine::Get().OnRuntimeStart(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneRuntime::Stop() {
|
||||||
|
if (!m_running) {
|
||||||
|
m_scene = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scripting::ScriptEngine::Get().OnRuntimeStop();
|
||||||
|
m_running = false;
|
||||||
|
m_scene = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneRuntime::FixedUpdate(float fixedDeltaTime) {
|
||||||
|
if (!m_running || !m_scene || !m_scene->IsActive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scripts run first so their state changes are visible to native components in the same frame.
|
||||||
|
Scripting::ScriptEngine::Get().OnFixedUpdate(fixedDeltaTime);
|
||||||
|
m_scene->FixedUpdate(fixedDeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneRuntime::Update(float deltaTime) {
|
||||||
|
if (!m_running || !m_scene || !m_scene->IsActive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scripting::ScriptEngine::Get().OnUpdate(deltaTime);
|
||||||
|
m_scene->Update(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneRuntime::LateUpdate(float deltaTime) {
|
||||||
|
if (!m_running || !m_scene || !m_scene->IsActive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scripting::ScriptEngine::Get().OnLateUpdate(deltaTime);
|
||||||
|
m_scene->LateUpdate(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
1794
engine/src/Scripting/Mono/MonoScriptRuntime.cpp
Normal file
1794
engine/src/Scripting/Mono/MonoScriptRuntime.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -50,14 +50,30 @@ foreach(XCENGINE_REQUIRED_PATH
|
|||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
set(XCENGINE_SCRIPT_CORE_SOURCES
|
set(XCENGINE_SCRIPT_CORE_SOURCES
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Camera.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Behaviour.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Component.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Debug.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/GameObject.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/InternalCalls.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Light.cs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/MonoBehaviour.cs
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/MonoBehaviour.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Quaternion.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Space.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Time.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Transform.cs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Vector2.cs
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Vector2.cs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Vector3.cs
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Vector3.cs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Vector4.cs
|
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Vector4.cs
|
||||||
)
|
)
|
||||||
|
|
||||||
set(XCENGINE_GAME_SCRIPT_SOURCES
|
set(XCENGINE_GAME_SCRIPT_SOURCES
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/BuiltinComponentProbe.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/TransformConversionProbe.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/TransformMotionProbe.cs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/TransformSpaceProbe.cs
|
||||||
)
|
)
|
||||||
|
|
||||||
set(XCENGINE_MANAGED_FRAMEWORK_REFERENCES
|
set(XCENGINE_MANAGED_FRAMEWORK_REFERENCES
|
||||||
|
|||||||
60
managed/GameScripts/BuiltinComponentProbe.cs
Normal file
60
managed/GameScripts/BuiltinComponentProbe.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
using XCEngine;
|
||||||
|
|
||||||
|
namespace Gameplay
|
||||||
|
{
|
||||||
|
public sealed class BuiltinComponentProbe : MonoBehaviour
|
||||||
|
{
|
||||||
|
public bool HasCamera;
|
||||||
|
public bool HasLight;
|
||||||
|
public bool CameraLookupSucceeded;
|
||||||
|
public bool LightLookupSucceeded;
|
||||||
|
|
||||||
|
public float ObservedFieldOfView;
|
||||||
|
public float ObservedNearClipPlane;
|
||||||
|
public float ObservedFarClipPlane;
|
||||||
|
public float ObservedDepth;
|
||||||
|
public bool ObservedPrimary;
|
||||||
|
|
||||||
|
public float ObservedIntensity;
|
||||||
|
public float ObservedRange;
|
||||||
|
public float ObservedSpotAngle;
|
||||||
|
public bool ObservedCastsShadows;
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
HasCamera = HasComponent<Camera>();
|
||||||
|
HasLight = HasComponent<Light>();
|
||||||
|
|
||||||
|
CameraLookupSucceeded = TryGetComponent(out Camera camera);
|
||||||
|
LightLookupSucceeded = TryGetComponent(out Light light);
|
||||||
|
|
||||||
|
if (camera != null)
|
||||||
|
{
|
||||||
|
ObservedFieldOfView = camera.fieldOfView;
|
||||||
|
ObservedNearClipPlane = camera.nearClipPlane;
|
||||||
|
ObservedFarClipPlane = camera.farClipPlane;
|
||||||
|
ObservedDepth = camera.depth;
|
||||||
|
ObservedPrimary = camera.primary;
|
||||||
|
|
||||||
|
camera.fieldOfView = 75.0f;
|
||||||
|
camera.nearClipPlane = 0.3f;
|
||||||
|
camera.farClipPlane = 500.0f;
|
||||||
|
camera.depth = 3.0f;
|
||||||
|
camera.primary = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (light != null)
|
||||||
|
{
|
||||||
|
ObservedIntensity = light.intensity;
|
||||||
|
ObservedRange = light.range;
|
||||||
|
ObservedSpotAngle = light.spotAngle;
|
||||||
|
ObservedCastsShadows = light.castsShadows;
|
||||||
|
|
||||||
|
light.intensity = 2.5f;
|
||||||
|
light.range = 42.0f;
|
||||||
|
light.spotAngle = 55.0f;
|
||||||
|
light.castsShadows = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
managed/GameScripts/HierarchyProbe.cs
Normal file
45
managed/GameScripts/HierarchyProbe.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using XCEngine;
|
||||||
|
|
||||||
|
namespace Gameplay
|
||||||
|
{
|
||||||
|
public sealed class HierarchyProbe : MonoBehaviour
|
||||||
|
{
|
||||||
|
public GameObject ReparentTarget;
|
||||||
|
public string ObservedParentName = string.Empty;
|
||||||
|
public string ObservedFirstChildName = string.Empty;
|
||||||
|
public string ReparentedChildParentName = string.Empty;
|
||||||
|
public bool ParentLookupSucceeded;
|
||||||
|
public bool ChildLookupSucceeded;
|
||||||
|
public int ObservedChildCount;
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
Transform currentParent = transform.parent;
|
||||||
|
ParentLookupSucceeded = currentParent != null;
|
||||||
|
if (currentParent != null)
|
||||||
|
{
|
||||||
|
ObservedParentName = currentParent.gameObject.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObservedChildCount = transform.childCount;
|
||||||
|
|
||||||
|
Transform firstChild = transform.GetChild(0);
|
||||||
|
ChildLookupSucceeded = firstChild != null;
|
||||||
|
if (firstChild != null)
|
||||||
|
{
|
||||||
|
ObservedFirstChildName = firstChild.gameObject.name;
|
||||||
|
|
||||||
|
if (ReparentTarget != null)
|
||||||
|
{
|
||||||
|
firstChild.SetParent(ReparentTarget.transform, true);
|
||||||
|
|
||||||
|
Transform newParent = firstChild.parent;
|
||||||
|
if (newParent != null)
|
||||||
|
{
|
||||||
|
ReparentedChildParentName = newParent.gameObject.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,14 @@ namespace Gameplay
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class UnsupportedManagedComponent : Component
|
||||||
|
{
|
||||||
|
private UnsupportedManagedComponent(ulong gameObjectUUID)
|
||||||
|
: base(gameObjectUUID)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class LifecycleProbe : MonoBehaviour
|
public sealed class LifecycleProbe : MonoBehaviour
|
||||||
{
|
{
|
||||||
public int AwakeCount;
|
public int AwakeCount;
|
||||||
@@ -18,14 +26,47 @@ namespace Gameplay
|
|||||||
public int DestroyCount;
|
public int DestroyCount;
|
||||||
|
|
||||||
public float Speed;
|
public float Speed;
|
||||||
|
public float ObservedFixedDeltaTime;
|
||||||
|
public float ObservedUpdateDeltaTime;
|
||||||
|
public float ObservedLateDeltaTime;
|
||||||
public string Label = string.Empty;
|
public string Label = string.Empty;
|
||||||
|
public string ObservedGameObjectName = string.Empty;
|
||||||
|
public string ObservedTargetName = string.Empty;
|
||||||
public bool WasAwakened;
|
public bool WasAwakened;
|
||||||
|
public bool WarningLogged;
|
||||||
|
public bool ErrorLogged;
|
||||||
|
public bool HasTransform;
|
||||||
|
public bool TransformLookupSucceeded;
|
||||||
|
public bool HasUnsupportedComponent;
|
||||||
|
public bool UnsupportedComponentLookupReturnedNull;
|
||||||
|
public bool TargetResolved;
|
||||||
|
public bool RotationAccessed;
|
||||||
|
public bool ScaleAccessed;
|
||||||
|
public bool TransformAccessed;
|
||||||
|
public bool ObservedEnabled;
|
||||||
|
public bool ObservedActiveSelf;
|
||||||
|
public bool ObservedActiveInHierarchy;
|
||||||
|
public bool ObservedIsActiveAndEnabled;
|
||||||
|
public bool DisableSelfOnFirstUpdate;
|
||||||
|
public bool DeactivateGameObjectOnFirstUpdate;
|
||||||
|
public GameObject Target;
|
||||||
|
public GameObject SelfReference;
|
||||||
|
public Vector4 ObservedLocalRotation;
|
||||||
|
public Vector3 ObservedLocalPosition;
|
||||||
|
public Vector3 ObservedLocalScale;
|
||||||
public Vector3 SpawnPoint;
|
public Vector3 SpawnPoint;
|
||||||
|
|
||||||
public void Awake()
|
public void Awake()
|
||||||
{
|
{
|
||||||
AwakeCount += 1;
|
AwakeCount += 1;
|
||||||
WasAwakened = true;
|
WasAwakened = true;
|
||||||
|
gameObject.name = gameObject.name + "_Managed";
|
||||||
|
ObservedGameObjectName = gameObject.name;
|
||||||
|
Debug.Log(ObservedGameObjectName);
|
||||||
|
Debug.LogWarning(ObservedGameObjectName);
|
||||||
|
WarningLogged = true;
|
||||||
|
Debug.LogError(ObservedGameObjectName);
|
||||||
|
ErrorLogged = true;
|
||||||
Label = Label + "|Awake";
|
Label = Label + "|Awake";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,23 +78,80 @@ namespace Gameplay
|
|||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
StartCount += 1;
|
StartCount += 1;
|
||||||
|
HasTransform = HasComponent<Transform>();
|
||||||
|
HasUnsupportedComponent = HasComponent<UnsupportedManagedComponent>();
|
||||||
|
|
||||||
|
SelfReference = gameObject;
|
||||||
|
TargetResolved = Target != null;
|
||||||
|
if (Target != null)
|
||||||
|
{
|
||||||
|
ObservedTargetName = Target.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransformLookupSucceeded = TryGetComponent(out Transform resolvedTransform);
|
||||||
|
UnsupportedComponentLookupReturnedNull = !gameObject.TryGetComponent(out UnsupportedManagedComponent unsupportedComponent);
|
||||||
|
|
||||||
|
if (resolvedTransform != null)
|
||||||
|
{
|
||||||
|
resolvedTransform.localPosition = new Vector3(7.0f, 8.0f, 9.0f);
|
||||||
|
resolvedTransform.localRotation = new Quaternion(0.0f, 0.5f, 0.0f, 0.8660254f);
|
||||||
|
resolvedTransform.localScale = new Vector3(2.0f, 3.0f, 4.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FixedUpdate()
|
public void FixedUpdate()
|
||||||
{
|
{
|
||||||
FixedUpdateCount += 1;
|
FixedUpdateCount += 1;
|
||||||
|
ObservedFixedDeltaTime = Time.deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
UpdateCount += 1;
|
UpdateCount += 1;
|
||||||
Speed += 1.0f;
|
Speed += 1.0f;
|
||||||
|
ObservedUpdateDeltaTime = Time.deltaTime;
|
||||||
|
ObservedLocalPosition = transform.localPosition;
|
||||||
|
Quaternion rotation = transform.localRotation;
|
||||||
|
ObservedLocalRotation = new Vector4(rotation.x, rotation.y, rotation.z, rotation.w);
|
||||||
|
ObservedLocalScale = transform.localScale;
|
||||||
|
ObservedEnabled = enabled;
|
||||||
|
ObservedActiveSelf = gameObject.activeSelf;
|
||||||
|
ObservedActiveInHierarchy = gameObject.activeInHierarchy;
|
||||||
|
ObservedIsActiveAndEnabled = isActiveAndEnabled;
|
||||||
|
RotationAccessed = true;
|
||||||
|
ScaleAccessed = true;
|
||||||
|
TransformAccessed = true;
|
||||||
|
|
||||||
|
if (UpdateCount == 1)
|
||||||
|
{
|
||||||
|
if (DisableSelfOnFirstUpdate)
|
||||||
|
{
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DeactivateGameObjectOnFirstUpdate)
|
||||||
|
{
|
||||||
|
gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LateUpdate()
|
public void LateUpdate()
|
||||||
{
|
{
|
||||||
LateUpdateCount += 1;
|
LateUpdateCount += 1;
|
||||||
SpawnPoint = new Vector3(SpawnPoint.X + 1.0f, SpawnPoint.Y, SpawnPoint.Z);
|
ObservedLateDeltaTime = Time.deltaTime;
|
||||||
|
|
||||||
|
Vector3 position = transform.localPosition;
|
||||||
|
position.x = position.x + 1.0f;
|
||||||
|
transform.localPosition = position;
|
||||||
|
ObservedLocalPosition = transform.localPosition;
|
||||||
|
|
||||||
|
Vector3 scale = transform.localScale;
|
||||||
|
scale.x = scale.x + 1.0f;
|
||||||
|
transform.localScale = scale;
|
||||||
|
ObservedLocalScale = transform.localScale;
|
||||||
|
|
||||||
|
SpawnPoint.x = SpawnPoint.x + 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnDisable()
|
public void OnDisable()
|
||||||
|
|||||||
23
managed/GameScripts/TransformConversionProbe.cs
Normal file
23
managed/GameScripts/TransformConversionProbe.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using XCEngine;
|
||||||
|
|
||||||
|
namespace Gameplay
|
||||||
|
{
|
||||||
|
public sealed class TransformConversionProbe : MonoBehaviour
|
||||||
|
{
|
||||||
|
public Vector3 ObservedWorldPoint;
|
||||||
|
public Vector3 ObservedLocalPoint;
|
||||||
|
public Vector3 ObservedWorldDirection;
|
||||||
|
public Vector3 ObservedLocalDirection;
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
transform.localPosition = new Vector3(5.0f, 0.0f, 1.0f);
|
||||||
|
transform.localEulerAngles = new Vector3(0.0f, 90.0f, 0.0f);
|
||||||
|
|
||||||
|
ObservedWorldPoint = transform.TransformPoint(new Vector3(0.0f, 0.0f, 2.0f));
|
||||||
|
ObservedLocalPoint = transform.InverseTransformPoint(new Vector3(7.0f, 0.0f, 1.0f));
|
||||||
|
ObservedWorldDirection = transform.TransformDirection(new Vector3(0.0f, 0.0f, 1.0f));
|
||||||
|
ObservedLocalDirection = transform.InverseTransformDirection(new Vector3(1.0f, 0.0f, 0.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
managed/GameScripts/TransformMotionProbe.cs
Normal file
41
managed/GameScripts/TransformMotionProbe.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using XCEngine;
|
||||||
|
|
||||||
|
namespace Gameplay
|
||||||
|
{
|
||||||
|
public sealed class TransformMotionProbe : MonoBehaviour
|
||||||
|
{
|
||||||
|
public Vector3 ObservedForward;
|
||||||
|
public Vector3 ObservedRight;
|
||||||
|
public Vector3 ObservedUp;
|
||||||
|
public Vector3 ObservedPositionAfterSelfTranslate;
|
||||||
|
public Vector3 ObservedPositionAfterWorldTranslate;
|
||||||
|
public Vector3 ObservedForwardAfterWorldRotate;
|
||||||
|
public Vector3 ObservedForwardAfterSelfRotate;
|
||||||
|
public Vector3 ObservedForwardAfterLookAt;
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
transform.localPosition = new Vector3(0.0f, 0.0f, 0.0f);
|
||||||
|
transform.localEulerAngles = new Vector3(0.0f, 90.0f, 0.0f);
|
||||||
|
|
||||||
|
ObservedForward = transform.forward;
|
||||||
|
ObservedRight = transform.right;
|
||||||
|
ObservedUp = transform.up;
|
||||||
|
|
||||||
|
transform.Translate(new Vector3(0.0f, 0.0f, 2.0f));
|
||||||
|
ObservedPositionAfterSelfTranslate = transform.position;
|
||||||
|
|
||||||
|
transform.Translate(new Vector3(0.0f, 0.0f, 3.0f), Space.World);
|
||||||
|
ObservedPositionAfterWorldTranslate = transform.position;
|
||||||
|
|
||||||
|
transform.Rotate(new Vector3(0.0f, -90.0f, 0.0f), Space.World);
|
||||||
|
ObservedForwardAfterWorldRotate = transform.forward;
|
||||||
|
|
||||||
|
transform.Rotate(new Vector3(0.0f, 90.0f, 0.0f));
|
||||||
|
ObservedForwardAfterSelfRotate = transform.forward;
|
||||||
|
|
||||||
|
transform.LookAt(new Vector3(2.0f, 0.0f, 8.0f));
|
||||||
|
ObservedForwardAfterLookAt = transform.forward;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
managed/GameScripts/TransformSpaceProbe.cs
Normal file
33
managed/GameScripts/TransformSpaceProbe.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using XCEngine;
|
||||||
|
|
||||||
|
namespace Gameplay
|
||||||
|
{
|
||||||
|
public sealed class TransformSpaceProbe : MonoBehaviour
|
||||||
|
{
|
||||||
|
public Vector3 ObservedInitialWorldPosition;
|
||||||
|
public Vector3 ObservedInitialWorldScale;
|
||||||
|
public Vector3 ObservedInitialLocalEulerAngles;
|
||||||
|
public Vector3 ObservedEulerAfterSet;
|
||||||
|
public Vector3 ObservedWorldPositionAfterSet;
|
||||||
|
public Vector3 ObservedWorldScaleAfterSet;
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
ObservedInitialWorldPosition = transform.position;
|
||||||
|
ObservedInitialWorldScale = transform.scale;
|
||||||
|
ObservedInitialLocalEulerAngles = transform.localEulerAngles;
|
||||||
|
|
||||||
|
transform.localEulerAngles = new Vector3(0.0f, 45.0f, 0.0f);
|
||||||
|
ObservedEulerAfterSet = transform.localEulerAngles;
|
||||||
|
|
||||||
|
transform.position = new Vector3(20.0f, 6.0f, 10.0f);
|
||||||
|
ObservedWorldPositionAfterSet = transform.position;
|
||||||
|
|
||||||
|
Quaternion targetRotation = new Quaternion(0.0f, 0.5f, 0.0f, 0.8660254f);
|
||||||
|
transform.rotation = targetRotation;
|
||||||
|
|
||||||
|
transform.scale = new Vector3(6.0f, 8.0f, 10.0f);
|
||||||
|
ObservedWorldScaleAfterSet = transform.scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
managed/XCEngine.ScriptCore/Behaviour.cs
Normal file
21
managed/XCEngine.ScriptCore/Behaviour.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
public class Behaviour : Component
|
||||||
|
{
|
||||||
|
internal ulong m_scriptComponentUUID = 0;
|
||||||
|
|
||||||
|
protected Behaviour()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong ScriptComponentUUID => m_scriptComponentUUID;
|
||||||
|
|
||||||
|
public bool enabled
|
||||||
|
{
|
||||||
|
get => InternalCalls.Behaviour_GetEnabled(m_scriptComponentUUID);
|
||||||
|
set => InternalCalls.Behaviour_SetEnabled(m_scriptComponentUUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool isActiveAndEnabled => enabled && GameObject.activeInHierarchy;
|
||||||
|
}
|
||||||
|
}
|
||||||
70
managed/XCEngine.ScriptCore/Camera.cs
Normal file
70
managed/XCEngine.ScriptCore/Camera.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
public sealed class Camera : Component
|
||||||
|
{
|
||||||
|
internal Camera(ulong gameObjectUUID)
|
||||||
|
: base(gameObjectUUID)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public float FieldOfView
|
||||||
|
{
|
||||||
|
get => InternalCalls.Camera_GetFieldOfView(GameObjectUUID);
|
||||||
|
set => InternalCalls.Camera_SetFieldOfView(GameObjectUUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float fieldOfView
|
||||||
|
{
|
||||||
|
get => FieldOfView;
|
||||||
|
set => FieldOfView = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float NearClipPlane
|
||||||
|
{
|
||||||
|
get => InternalCalls.Camera_GetNearClipPlane(GameObjectUUID);
|
||||||
|
set => InternalCalls.Camera_SetNearClipPlane(GameObjectUUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float nearClipPlane
|
||||||
|
{
|
||||||
|
get => NearClipPlane;
|
||||||
|
set => NearClipPlane = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float FarClipPlane
|
||||||
|
{
|
||||||
|
get => InternalCalls.Camera_GetFarClipPlane(GameObjectUUID);
|
||||||
|
set => InternalCalls.Camera_SetFarClipPlane(GameObjectUUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float farClipPlane
|
||||||
|
{
|
||||||
|
get => FarClipPlane;
|
||||||
|
set => FarClipPlane = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Depth
|
||||||
|
{
|
||||||
|
get => InternalCalls.Camera_GetDepth(GameObjectUUID);
|
||||||
|
set => InternalCalls.Camera_SetDepth(GameObjectUUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float depth
|
||||||
|
{
|
||||||
|
get => Depth;
|
||||||
|
set => Depth = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Primary
|
||||||
|
{
|
||||||
|
get => InternalCalls.Camera_GetPrimary(GameObjectUUID);
|
||||||
|
set => InternalCalls.Camera_SetPrimary(GameObjectUUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool primary
|
||||||
|
{
|
||||||
|
get => Primary;
|
||||||
|
set => Primary = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
managed/XCEngine.ScriptCore/Component.cs
Normal file
70
managed/XCEngine.ScriptCore/Component.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
public abstract class Component
|
||||||
|
{
|
||||||
|
internal ulong m_gameObjectUUID;
|
||||||
|
|
||||||
|
protected Component()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Component(ulong gameObjectUUID)
|
||||||
|
{
|
||||||
|
m_gameObjectUUID = gameObjectUUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong GameObjectUUID => m_gameObjectUUID;
|
||||||
|
|
||||||
|
public GameObject GameObject => new GameObject(m_gameObjectUUID);
|
||||||
|
public GameObject gameObject => GameObject;
|
||||||
|
|
||||||
|
public Transform Transform => GameObject.Transform;
|
||||||
|
public Transform transform => Transform;
|
||||||
|
|
||||||
|
public bool HasComponent<T>() where T : Component
|
||||||
|
{
|
||||||
|
return GameObject.HasComponent<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetComponent<T>() where T : Component
|
||||||
|
{
|
||||||
|
return GameObject.GetComponent<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetComponent<T>(out T component) where T : Component
|
||||||
|
{
|
||||||
|
component = GetComponent<T>();
|
||||||
|
return component != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T Create<T>(ulong gameObjectUUID) where T : Component
|
||||||
|
{
|
||||||
|
return Create(typeof(T), gameObjectUUID) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Component Create(Type componentType, ulong gameObjectUUID)
|
||||||
|
{
|
||||||
|
if (componentType == null || gameObjectUUID == 0 || !typeof(Component).IsAssignableFrom(componentType))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Activator.CreateInstance(
|
||||||
|
componentType,
|
||||||
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
||||||
|
binder: null,
|
||||||
|
args: new object[] { gameObjectUUID },
|
||||||
|
culture: null) as Component;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
managed/XCEngine.ScriptCore/Debug.cs
Normal file
20
managed/XCEngine.ScriptCore/Debug.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
public static class Debug
|
||||||
|
{
|
||||||
|
public static void Log(string message)
|
||||||
|
{
|
||||||
|
InternalCalls.Debug_Log(message ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogWarning(string message)
|
||||||
|
{
|
||||||
|
InternalCalls.Debug_LogWarning(message ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogError(string message)
|
||||||
|
{
|
||||||
|
InternalCalls.Debug_LogError(message ?? string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
managed/XCEngine.ScriptCore/GameObject.cs
Normal file
55
managed/XCEngine.ScriptCore/GameObject.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
public sealed class GameObject
|
||||||
|
{
|
||||||
|
private readonly ulong m_uuid;
|
||||||
|
|
||||||
|
internal GameObject(ulong uuid)
|
||||||
|
{
|
||||||
|
m_uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong UUID => m_uuid;
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => InternalCalls.GameObject_GetName(UUID) ?? string.Empty;
|
||||||
|
set => InternalCalls.GameObject_SetName(UUID, value ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string name
|
||||||
|
{
|
||||||
|
get => Name;
|
||||||
|
set => Name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool activeSelf => InternalCalls.GameObject_GetActiveSelf(UUID);
|
||||||
|
|
||||||
|
public bool activeInHierarchy => InternalCalls.GameObject_GetActiveInHierarchy(UUID);
|
||||||
|
|
||||||
|
public void SetActive(bool value)
|
||||||
|
{
|
||||||
|
InternalCalls.GameObject_SetActive(UUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transform Transform => GetComponent<Transform>();
|
||||||
|
public Transform transform => Transform;
|
||||||
|
|
||||||
|
public bool HasComponent<T>() where T : Component
|
||||||
|
{
|
||||||
|
return InternalCalls.GameObject_HasComponent(UUID, typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetComponent<T>() where T : Component
|
||||||
|
{
|
||||||
|
ulong componentOwnerUUID = InternalCalls.GameObject_GetComponent(UUID, typeof(T));
|
||||||
|
return Component.Create<T>(componentOwnerUUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetComponent<T>(out T component) where T : Component
|
||||||
|
{
|
||||||
|
component = GetComponent<T>();
|
||||||
|
return component != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
185
managed/XCEngine.ScriptCore/InternalCalls.cs
Normal file
185
managed/XCEngine.ScriptCore/InternalCalls.cs
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
internal static class InternalCalls
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Debug_Log(string message);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Debug_LogWarning(string message);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Debug_LogError(string message);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern float Time_GetDeltaTime();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern string GameObject_GetName(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void GameObject_SetName(ulong gameObjectUUID, string name);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern bool GameObject_GetActiveSelf(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern bool GameObject_GetActiveInHierarchy(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void GameObject_SetActive(ulong gameObjectUUID, bool active);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern bool GameObject_HasComponent(ulong gameObjectUUID, Type componentType);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern ulong GameObject_GetComponent(ulong gameObjectUUID, Type componentType);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern bool Behaviour_GetEnabled(ulong scriptComponentUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Behaviour_SetEnabled(ulong scriptComponentUUID, bool enabled);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_GetLocalPosition(ulong gameObjectUUID, out Vector3 position);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_SetLocalPosition(ulong gameObjectUUID, ref Vector3 position);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_GetLocalRotation(ulong gameObjectUUID, out Quaternion rotation);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_SetLocalRotation(ulong gameObjectUUID, ref Quaternion rotation);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_GetLocalScale(ulong gameObjectUUID, out Vector3 scale);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_SetLocalScale(ulong gameObjectUUID, ref Vector3 scale);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_GetLocalEulerAngles(ulong gameObjectUUID, out Vector3 eulerAngles);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_SetLocalEulerAngles(ulong gameObjectUUID, ref Vector3 eulerAngles);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_GetPosition(ulong gameObjectUUID, out Vector3 position);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_SetPosition(ulong gameObjectUUID, ref Vector3 position);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_GetRotation(ulong gameObjectUUID, out Quaternion rotation);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_SetRotation(ulong gameObjectUUID, ref Quaternion rotation);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_GetScale(ulong gameObjectUUID, out Vector3 scale);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_SetScale(ulong gameObjectUUID, ref Vector3 scale);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_GetForward(ulong gameObjectUUID, out Vector3 forward);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_GetRight(ulong gameObjectUUID, out Vector3 right);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_GetUp(ulong gameObjectUUID, out Vector3 up);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_Translate(ulong gameObjectUUID, ref Vector3 translation, int relativeTo);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_Rotate(ulong gameObjectUUID, ref Vector3 eulers, int relativeTo);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_LookAt(ulong gameObjectUUID, ref Vector3 target);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_TransformPoint(ulong gameObjectUUID, ref Vector3 point, out Vector3 transformedPoint);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_InverseTransformPoint(ulong gameObjectUUID, ref Vector3 point, out Vector3 transformedPoint);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_TransformDirection(ulong gameObjectUUID, ref Vector3 direction, out Vector3 transformedDirection);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_InverseTransformDirection(ulong gameObjectUUID, ref Vector3 direction, out Vector3 transformedDirection);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern ulong Transform_GetParent(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Transform_SetParent(ulong gameObjectUUID, ulong parentGameObjectUUID, bool worldPositionStays);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern int Transform_GetChildCount(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern ulong Transform_GetChild(ulong gameObjectUUID, int index);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern float Camera_GetFieldOfView(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Camera_SetFieldOfView(ulong gameObjectUUID, float value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern float Camera_GetNearClipPlane(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Camera_SetNearClipPlane(ulong gameObjectUUID, float value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern float Camera_GetFarClipPlane(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Camera_SetFarClipPlane(ulong gameObjectUUID, float value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern float Camera_GetDepth(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Camera_SetDepth(ulong gameObjectUUID, float value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern bool Camera_GetPrimary(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Camera_SetPrimary(ulong gameObjectUUID, bool value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern float Light_GetIntensity(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Light_SetIntensity(ulong gameObjectUUID, float value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern float Light_GetRange(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Light_SetRange(ulong gameObjectUUID, float value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern float Light_GetSpotAngle(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Light_SetSpotAngle(ulong gameObjectUUID, float value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern bool Light_GetCastsShadows(ulong gameObjectUUID);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Light_SetCastsShadows(ulong gameObjectUUID, bool value);
|
||||||
|
}
|
||||||
|
}
|
||||||
58
managed/XCEngine.ScriptCore/Light.cs
Normal file
58
managed/XCEngine.ScriptCore/Light.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
public sealed class Light : Component
|
||||||
|
{
|
||||||
|
internal Light(ulong gameObjectUUID)
|
||||||
|
: base(gameObjectUUID)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Intensity
|
||||||
|
{
|
||||||
|
get => InternalCalls.Light_GetIntensity(GameObjectUUID);
|
||||||
|
set => InternalCalls.Light_SetIntensity(GameObjectUUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float intensity
|
||||||
|
{
|
||||||
|
get => Intensity;
|
||||||
|
set => Intensity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Range
|
||||||
|
{
|
||||||
|
get => InternalCalls.Light_GetRange(GameObjectUUID);
|
||||||
|
set => InternalCalls.Light_SetRange(GameObjectUUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float range
|
||||||
|
{
|
||||||
|
get => Range;
|
||||||
|
set => Range = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float SpotAngle
|
||||||
|
{
|
||||||
|
get => InternalCalls.Light_GetSpotAngle(GameObjectUUID);
|
||||||
|
set => InternalCalls.Light_SetSpotAngle(GameObjectUUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float spotAngle
|
||||||
|
{
|
||||||
|
get => SpotAngle;
|
||||||
|
set => SpotAngle = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CastsShadows
|
||||||
|
{
|
||||||
|
get => InternalCalls.Light_GetCastsShadows(GameObjectUUID);
|
||||||
|
set => InternalCalls.Light_SetCastsShadows(GameObjectUUID, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool castsShadows
|
||||||
|
{
|
||||||
|
get => CastsShadows;
|
||||||
|
set => CastsShadows = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
namespace XCEngine
|
namespace XCEngine
|
||||||
{
|
{
|
||||||
public class MonoBehaviour
|
public class MonoBehaviour : Behaviour
|
||||||
{
|
{
|
||||||
public ulong GameObjectUUID;
|
|
||||||
public ulong ScriptComponentUUID;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
45
managed/XCEngine.ScriptCore/Quaternion.cs
Normal file
45
managed/XCEngine.ScriptCore/Quaternion.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Quaternion
|
||||||
|
{
|
||||||
|
public float X;
|
||||||
|
public float Y;
|
||||||
|
public float Z;
|
||||||
|
public float W;
|
||||||
|
|
||||||
|
public float x
|
||||||
|
{
|
||||||
|
get => X;
|
||||||
|
set => X = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float y
|
||||||
|
{
|
||||||
|
get => Y;
|
||||||
|
set => Y = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float z
|
||||||
|
{
|
||||||
|
get => Z;
|
||||||
|
set => Z = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float w
|
||||||
|
{
|
||||||
|
get => W;
|
||||||
|
set => W = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quaternion(float x, float y, float z, float w)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Z = z;
|
||||||
|
W = w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
managed/XCEngine.ScriptCore/Space.cs
Normal file
8
managed/XCEngine.ScriptCore/Space.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
public enum Space
|
||||||
|
{
|
||||||
|
Self = 0,
|
||||||
|
World = 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
7
managed/XCEngine.ScriptCore/Time.cs
Normal file
7
managed/XCEngine.ScriptCore/Time.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
public static class Time
|
||||||
|
{
|
||||||
|
public static float deltaTime => InternalCalls.Time_GetDeltaTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
263
managed/XCEngine.ScriptCore/Transform.cs
Normal file
263
managed/XCEngine.ScriptCore/Transform.cs
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
public sealed class Transform : Component
|
||||||
|
{
|
||||||
|
internal Transform(ulong gameObjectUUID)
|
||||||
|
: base(gameObjectUUID)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 LocalPosition
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_GetLocalPosition(GameObjectUUID, out Vector3 position);
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_SetLocalPosition(GameObjectUUID, ref value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 localPosition
|
||||||
|
{
|
||||||
|
get => LocalPosition;
|
||||||
|
set => LocalPosition = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quaternion LocalRotation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_GetLocalRotation(GameObjectUUID, out Quaternion rotation);
|
||||||
|
return rotation;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_SetLocalRotation(GameObjectUUID, ref value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quaternion localRotation
|
||||||
|
{
|
||||||
|
get => LocalRotation;
|
||||||
|
set => LocalRotation = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 LocalScale
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_GetLocalScale(GameObjectUUID, out Vector3 scale);
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_SetLocalScale(GameObjectUUID, ref value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 localScale
|
||||||
|
{
|
||||||
|
get => LocalScale;
|
||||||
|
set => LocalScale = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 LocalEulerAngles
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_GetLocalEulerAngles(GameObjectUUID, out Vector3 eulerAngles);
|
||||||
|
return eulerAngles;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_SetLocalEulerAngles(GameObjectUUID, ref value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 localEulerAngles
|
||||||
|
{
|
||||||
|
get => LocalEulerAngles;
|
||||||
|
set => LocalEulerAngles = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 Position
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_GetPosition(GameObjectUUID, out Vector3 position);
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_SetPosition(GameObjectUUID, ref value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 position
|
||||||
|
{
|
||||||
|
get => Position;
|
||||||
|
set => Position = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quaternion Rotation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_GetRotation(GameObjectUUID, out Quaternion rotation);
|
||||||
|
return rotation;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_SetRotation(GameObjectUUID, ref value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quaternion rotation
|
||||||
|
{
|
||||||
|
get => Rotation;
|
||||||
|
set => Rotation = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 Scale
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_GetScale(GameObjectUUID, out Vector3 scale);
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_SetScale(GameObjectUUID, ref value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 scale
|
||||||
|
{
|
||||||
|
get => Scale;
|
||||||
|
set => Scale = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 Forward
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_GetForward(GameObjectUUID, out Vector3 forward);
|
||||||
|
return forward;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 forward => Forward;
|
||||||
|
|
||||||
|
public Vector3 Right
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_GetRight(GameObjectUUID, out Vector3 right);
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 right => Right;
|
||||||
|
|
||||||
|
public Vector3 Up
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_GetUp(GameObjectUUID, out Vector3 up);
|
||||||
|
return up;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 up => Up;
|
||||||
|
|
||||||
|
public Transform Parent
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ulong parentUUID = InternalCalls.Transform_GetParent(GameObjectUUID);
|
||||||
|
return Create<Transform>(parentUUID);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_SetParent(GameObjectUUID, value?.GameObjectUUID ?? 0, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transform parent
|
||||||
|
{
|
||||||
|
get => Parent;
|
||||||
|
set => Parent = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ChildCount => InternalCalls.Transform_GetChildCount(GameObjectUUID);
|
||||||
|
public int childCount => ChildCount;
|
||||||
|
|
||||||
|
public Transform GetChild(int index)
|
||||||
|
{
|
||||||
|
ulong childUUID = InternalCalls.Transform_GetChild(GameObjectUUID, index);
|
||||||
|
return Create<Transform>(childUUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParent(Transform parent)
|
||||||
|
{
|
||||||
|
SetParent(parent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParent(Transform parent, bool worldPositionStays)
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_SetParent(GameObjectUUID, parent?.GameObjectUUID ?? 0, worldPositionStays);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Translate(Vector3 translation)
|
||||||
|
{
|
||||||
|
Translate(translation, Space.Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Translate(Vector3 translation, Space relativeTo)
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_Translate(GameObjectUUID, ref translation, (int)relativeTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Rotate(Vector3 eulers)
|
||||||
|
{
|
||||||
|
Rotate(eulers, Space.Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Rotate(Vector3 eulers, Space relativeTo)
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_Rotate(GameObjectUUID, ref eulers, (int)relativeTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LookAt(Vector3 worldPosition)
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_LookAt(GameObjectUUID, ref worldPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 TransformPoint(Vector3 point)
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_TransformPoint(GameObjectUUID, ref point, out Vector3 transformedPoint);
|
||||||
|
return transformedPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 InverseTransformPoint(Vector3 point)
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_InverseTransformPoint(GameObjectUUID, ref point, out Vector3 transformedPoint);
|
||||||
|
return transformedPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 TransformDirection(Vector3 direction)
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_TransformDirection(GameObjectUUID, ref direction, out Vector3 transformedDirection);
|
||||||
|
return transformedDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 InverseTransformDirection(Vector3 direction)
|
||||||
|
{
|
||||||
|
InternalCalls.Transform_InverseTransformDirection(GameObjectUUID, ref direction, out Vector3 transformedDirection);
|
||||||
|
return transformedDirection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
managed/XCEngine.ScriptCore/Vector2.cs
Normal file
29
managed/XCEngine.ScriptCore/Vector2.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Vector2
|
||||||
|
{
|
||||||
|
public float X;
|
||||||
|
public float Y;
|
||||||
|
|
||||||
|
public float x
|
||||||
|
{
|
||||||
|
get => X;
|
||||||
|
set => X = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float y
|
||||||
|
{
|
||||||
|
get => Y;
|
||||||
|
set => Y = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2(float x, float y)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
managed/XCEngine.ScriptCore/Vector3.cs
Normal file
37
managed/XCEngine.ScriptCore/Vector3.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Vector3
|
||||||
|
{
|
||||||
|
public float X;
|
||||||
|
public float Y;
|
||||||
|
public float Z;
|
||||||
|
|
||||||
|
public float x
|
||||||
|
{
|
||||||
|
get => X;
|
||||||
|
set => X = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float y
|
||||||
|
{
|
||||||
|
get => Y;
|
||||||
|
set => Y = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float z
|
||||||
|
{
|
||||||
|
get => Z;
|
||||||
|
set => Z = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3(float x, float y, float z)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Z = z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
managed/XCEngine.ScriptCore/Vector4.cs
Normal file
45
managed/XCEngine.ScriptCore/Vector4.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace XCEngine
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Vector4
|
||||||
|
{
|
||||||
|
public float X;
|
||||||
|
public float Y;
|
||||||
|
public float Z;
|
||||||
|
public float W;
|
||||||
|
|
||||||
|
public float x
|
||||||
|
{
|
||||||
|
get => X;
|
||||||
|
set => X = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float y
|
||||||
|
{
|
||||||
|
get => Y;
|
||||||
|
set => Y = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float z
|
||||||
|
{
|
||||||
|
get => Z;
|
||||||
|
set => Z = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float w
|
||||||
|
{
|
||||||
|
get => W;
|
||||||
|
set => W = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector4(float x, float y, float z, float w)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Z = z;
|
||||||
|
W = w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ project(XCEngine_SceneTests)
|
|||||||
|
|
||||||
set(SCENE_TEST_SOURCES
|
set(SCENE_TEST_SOURCES
|
||||||
test_scene.cpp
|
test_scene.cpp
|
||||||
|
test_scene_runtime.cpp
|
||||||
test_scene_manager.cpp
|
test_scene_manager.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
253
tests/Scene/test_scene_runtime.cpp
Normal file
253
tests/Scene/test_scene_runtime.cpp
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <XCEngine/Scene/SceneRuntime.h>
|
||||||
|
#include <XCEngine/Scripting/IScriptRuntime.h>
|
||||||
|
#include <XCEngine/Scripting/ScriptComponent.h>
|
||||||
|
#include <XCEngine/Scripting/ScriptEngine.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace XCEngine::Components;
|
||||||
|
using namespace XCEngine::Scripting;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string LifecycleMethodToString(ScriptLifecycleMethod method) {
|
||||||
|
switch (method) {
|
||||||
|
case ScriptLifecycleMethod::Awake: return "Awake";
|
||||||
|
case ScriptLifecycleMethod::OnEnable: return "OnEnable";
|
||||||
|
case ScriptLifecycleMethod::Start: return "Start";
|
||||||
|
case ScriptLifecycleMethod::FixedUpdate: return "FixedUpdate";
|
||||||
|
case ScriptLifecycleMethod::Update: return "Update";
|
||||||
|
case ScriptLifecycleMethod::LateUpdate: return "LateUpdate";
|
||||||
|
case ScriptLifecycleMethod::OnDisable: return "OnDisable";
|
||||||
|
case ScriptLifecycleMethod::OnDestroy: return "OnDestroy";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderedObserverComponent : public Component {
|
||||||
|
public:
|
||||||
|
explicit OrderedObserverComponent(std::vector<std::string>* events)
|
||||||
|
: m_events(events) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetName() const override { return "OrderedObserver"; }
|
||||||
|
|
||||||
|
void Start() override {
|
||||||
|
if (m_events) {
|
||||||
|
m_events->push_back("NativeStart:" + GetGameObject()->GetName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedUpdate() override {
|
||||||
|
if (m_events) {
|
||||||
|
m_events->push_back("NativeFixedUpdate:" + GetGameObject()->GetName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update(float deltaTime) override {
|
||||||
|
(void)deltaTime;
|
||||||
|
if (m_events) {
|
||||||
|
m_events->push_back("NativeUpdate:" + GetGameObject()->GetName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LateUpdate(float deltaTime) override {
|
||||||
|
(void)deltaTime;
|
||||||
|
if (m_events) {
|
||||||
|
m_events->push_back("NativeLateUpdate:" + GetGameObject()->GetName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::string>* m_events = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RecordingScriptRuntime : public IScriptRuntime {
|
||||||
|
public:
|
||||||
|
explicit RecordingScriptRuntime(std::vector<std::string>* events)
|
||||||
|
: m_events(events) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnRuntimeStart(Scene* scene) override {
|
||||||
|
if (m_events) {
|
||||||
|
m_events->push_back("RuntimeStart:" + (scene ? scene->GetName() : std::string("null")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnRuntimeStop(Scene* scene) override {
|
||||||
|
if (m_events) {
|
||||||
|
m_events->push_back("RuntimeStop:" + (scene ? scene->GetName() : std::string("null")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateScriptInstance(const ScriptRuntimeContext& context) override {
|
||||||
|
if (m_events) {
|
||||||
|
m_events->push_back("Create:" + Describe(context));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroyScriptInstance(const ScriptRuntimeContext& context) override {
|
||||||
|
if (m_events) {
|
||||||
|
m_events->push_back("Destroy:" + Describe(context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvokeMethod(
|
||||||
|
const ScriptRuntimeContext& context,
|
||||||
|
ScriptLifecycleMethod method,
|
||||||
|
float deltaTime) override {
|
||||||
|
(void)deltaTime;
|
||||||
|
if (m_events) {
|
||||||
|
m_events->push_back(LifecycleMethodToString(method) + ":" + Describe(context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::string Describe(const ScriptRuntimeContext& context) {
|
||||||
|
const std::string gameObjectName = context.gameObject ? context.gameObject->GetName() : "null";
|
||||||
|
const std::string className = context.component ? context.component->GetFullClassName() : "null";
|
||||||
|
return gameObjectName + ":" + className;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>* m_events = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SceneRuntimeTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
scriptEngine = &ScriptEngine::Get();
|
||||||
|
scriptEngine->OnRuntimeStop();
|
||||||
|
runtimeImpl = std::make_unique<RecordingScriptRuntime>(&events);
|
||||||
|
scriptEngine->SetRuntime(runtimeImpl.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
runtime.Stop();
|
||||||
|
scriptEngine->OnRuntimeStop();
|
||||||
|
scriptEngine->SetRuntime(nullptr);
|
||||||
|
runtimeImpl.reset();
|
||||||
|
scene.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene* CreateScene(const std::string& sceneName) {
|
||||||
|
scene = std::make_unique<Scene>(sceneName);
|
||||||
|
return scene.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptComponent* AddScript(GameObject* gameObject, const std::string& namespaceName, const std::string& className) {
|
||||||
|
ScriptComponent* component = gameObject->AddComponent<ScriptComponent>();
|
||||||
|
component->SetScriptClass("GameScripts", namespaceName, className);
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> events;
|
||||||
|
std::unique_ptr<Scene> scene;
|
||||||
|
std::unique_ptr<RecordingScriptRuntime> runtimeImpl;
|
||||||
|
ScriptEngine* scriptEngine = nullptr;
|
||||||
|
SceneRuntime runtime;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(SceneRuntimeTest, StartAndStopForwardToScriptEngine) {
|
||||||
|
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
AddScript(host, "Gameplay", "Bootstrap");
|
||||||
|
|
||||||
|
runtime.Start(runtimeScene);
|
||||||
|
runtime.Stop();
|
||||||
|
|
||||||
|
const std::vector<std::string> expected = {
|
||||||
|
"RuntimeStart:RuntimeScene",
|
||||||
|
"Create:Host:Gameplay.Bootstrap",
|
||||||
|
"Awake:Host:Gameplay.Bootstrap",
|
||||||
|
"OnEnable:Host:Gameplay.Bootstrap",
|
||||||
|
"OnDisable:Host:Gameplay.Bootstrap",
|
||||||
|
"OnDestroy:Host:Gameplay.Bootstrap",
|
||||||
|
"Destroy:Host:Gameplay.Bootstrap",
|
||||||
|
"RuntimeStop:RuntimeScene"
|
||||||
|
};
|
||||||
|
EXPECT_EQ(events, expected);
|
||||||
|
EXPECT_FALSE(runtime.IsRunning());
|
||||||
|
EXPECT_EQ(runtime.GetScene(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneRuntimeTest, FrameOrderRunsScriptLifecycleBeforeNativeComponents) {
|
||||||
|
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
host->AddComponent<OrderedObserverComponent>(&events);
|
||||||
|
AddScript(host, "Gameplay", "Mover");
|
||||||
|
|
||||||
|
runtime.Start(runtimeScene);
|
||||||
|
events.clear();
|
||||||
|
|
||||||
|
runtime.FixedUpdate(0.02f);
|
||||||
|
runtime.Update(0.016f);
|
||||||
|
runtime.LateUpdate(0.016f);
|
||||||
|
|
||||||
|
const std::vector<std::string> expected = {
|
||||||
|
"FixedUpdate:Host:Gameplay.Mover",
|
||||||
|
"NativeFixedUpdate:Host",
|
||||||
|
"Start:Host:Gameplay.Mover",
|
||||||
|
"Update:Host:Gameplay.Mover",
|
||||||
|
"NativeStart:Host",
|
||||||
|
"NativeUpdate:Host",
|
||||||
|
"LateUpdate:Host:Gameplay.Mover",
|
||||||
|
"NativeLateUpdate:Host"
|
||||||
|
};
|
||||||
|
EXPECT_EQ(events, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneRuntimeTest, InactiveSceneSkipsFrameExecution) {
|
||||||
|
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
host->AddComponent<OrderedObserverComponent>(&events);
|
||||||
|
AddScript(host, "Gameplay", "PausedScript");
|
||||||
|
runtimeScene->SetActive(false);
|
||||||
|
|
||||||
|
runtime.Start(runtimeScene);
|
||||||
|
events.clear();
|
||||||
|
|
||||||
|
runtime.FixedUpdate(0.02f);
|
||||||
|
runtime.Update(0.016f);
|
||||||
|
runtime.LateUpdate(0.016f);
|
||||||
|
|
||||||
|
EXPECT_TRUE(events.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneRuntimeTest, StartingNewSceneStopsPreviousRuntimeFirst) {
|
||||||
|
Scene* firstScene = CreateScene("FirstScene");
|
||||||
|
GameObject* firstHost = firstScene->CreateGameObject("FirstHost");
|
||||||
|
AddScript(firstHost, "Gameplay", "FirstScript");
|
||||||
|
|
||||||
|
runtime.Start(firstScene);
|
||||||
|
|
||||||
|
std::unique_ptr<Scene> secondScene = std::make_unique<Scene>("SecondScene");
|
||||||
|
GameObject* secondHost = secondScene->CreateGameObject("SecondHost");
|
||||||
|
ScriptComponent* secondScript = AddScript(secondHost, "Gameplay", "SecondScript");
|
||||||
|
|
||||||
|
events.clear();
|
||||||
|
runtime.Start(secondScene.get());
|
||||||
|
|
||||||
|
const std::vector<std::string> expected = {
|
||||||
|
"OnDisable:FirstHost:Gameplay.FirstScript",
|
||||||
|
"OnDestroy:FirstHost:Gameplay.FirstScript",
|
||||||
|
"Destroy:FirstHost:Gameplay.FirstScript",
|
||||||
|
"RuntimeStop:FirstScene",
|
||||||
|
"RuntimeStart:SecondScene",
|
||||||
|
"Create:SecondHost:Gameplay.SecondScript",
|
||||||
|
"Awake:SecondHost:Gameplay.SecondScript",
|
||||||
|
"OnEnable:SecondHost:Gameplay.SecondScript"
|
||||||
|
};
|
||||||
|
EXPECT_EQ(events, expected);
|
||||||
|
EXPECT_EQ(runtime.GetScene(), secondScene.get());
|
||||||
|
EXPECT_TRUE(scriptEngine->HasRuntimeInstance(secondScript));
|
||||||
|
|
||||||
|
runtime.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
@@ -8,6 +8,12 @@ set(SCRIPTING_TEST_SOURCES
|
|||||||
test_script_engine.cpp
|
test_script_engine.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(XCENGINE_ENABLE_MONO_SCRIPTING)
|
||||||
|
list(APPEND SCRIPTING_TEST_SOURCES
|
||||||
|
test_mono_script_runtime.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(scripting_tests ${SCRIPTING_TEST_SOURCES})
|
add_executable(scripting_tests ${SCRIPTING_TEST_SOURCES})
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
@@ -26,5 +32,27 @@ target_include_directories(scripting_tests PRIVATE
|
|||||||
${CMAKE_SOURCE_DIR}/engine/include
|
${CMAKE_SOURCE_DIR}/engine/include
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(TARGET xcengine_managed_assemblies)
|
||||||
|
add_dependencies(scripting_tests xcengine_managed_assemblies)
|
||||||
|
|
||||||
|
file(TO_CMAKE_PATH "${XCENGINE_MANAGED_OUTPUT_DIR}" XCENGINE_MANAGED_OUTPUT_DIR_CMAKE)
|
||||||
|
file(TO_CMAKE_PATH "${XCENGINE_SCRIPT_CORE_DLL}" XCENGINE_SCRIPT_CORE_DLL_CMAKE)
|
||||||
|
file(TO_CMAKE_PATH "${XCENGINE_GAME_SCRIPTS_DLL}" XCENGINE_GAME_SCRIPTS_DLL_CMAKE)
|
||||||
|
|
||||||
|
target_compile_definitions(scripting_tests PRIVATE
|
||||||
|
XCENGINE_TEST_MANAGED_OUTPUT_DIR=\"${XCENGINE_MANAGED_OUTPUT_DIR_CMAKE}\"
|
||||||
|
XCENGINE_TEST_SCRIPT_CORE_DLL=\"${XCENGINE_SCRIPT_CORE_DLL_CMAKE}\"
|
||||||
|
XCENGINE_TEST_GAME_SCRIPTS_DLL=\"${XCENGINE_GAME_SCRIPTS_DLL_CMAKE}\"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WIN32 AND EXISTS "${CMAKE_SOURCE_DIR}/engine/third_party/assimp/bin/assimp-vc143-mt.dll")
|
||||||
|
add_custom_command(TARGET scripting_tests POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
${CMAKE_SOURCE_DIR}/engine/third_party/assimp/bin/assimp-vc143-mt.dll
|
||||||
|
$<TARGET_FILE_DIR:scripting_tests>/assimp-vc143-mt.dll
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
include(GoogleTest)
|
include(GoogleTest)
|
||||||
gtest_discover_tests(scripting_tests)
|
gtest_discover_tests(scripting_tests)
|
||||||
|
|||||||
581
tests/scripting/test_mono_script_runtime.cpp
Normal file
581
tests/scripting/test_mono_script_runtime.cpp
Normal file
@@ -0,0 +1,581 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <XCEngine/Components/CameraComponent.h>
|
||||||
|
#include <XCEngine/Components/LightComponent.h>
|
||||||
|
#include <XCEngine/Core/Math/Vector3.h>
|
||||||
|
#include <XCEngine/Core/Math/Vector4.h>
|
||||||
|
#include <XCEngine/Scene/Scene.h>
|
||||||
|
#include <XCEngine/Scripting/Mono/MonoScriptRuntime.h>
|
||||||
|
#include <XCEngine/Scripting/ScriptComponent.h>
|
||||||
|
#include <XCEngine/Scripting/ScriptEngine.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace XCEngine::Components;
|
||||||
|
using namespace XCEngine::Scripting;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void ExpectVector3Near(const XCEngine::Math::Vector3& actual, const XCEngine::Math::Vector3& expected, float tolerance = 0.001f) {
|
||||||
|
EXPECT_NEAR(actual.x, expected.x, tolerance);
|
||||||
|
EXPECT_NEAR(actual.y, expected.y, tolerance);
|
||||||
|
EXPECT_NEAR(actual.z, expected.z, tolerance);
|
||||||
|
}
|
||||||
|
|
||||||
|
MonoScriptRuntime::Settings CreateMonoSettings() {
|
||||||
|
MonoScriptRuntime::Settings settings;
|
||||||
|
settings.assemblyDirectory = XCENGINE_TEST_MANAGED_OUTPUT_DIR;
|
||||||
|
settings.corlibDirectory = XCENGINE_TEST_MANAGED_OUTPUT_DIR;
|
||||||
|
settings.coreAssemblyPath = XCENGINE_TEST_SCRIPT_CORE_DLL;
|
||||||
|
settings.appAssemblyPath = XCENGINE_TEST_GAME_SCRIPTS_DLL;
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MonoScriptRuntimeTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
engine = &ScriptEngine::Get();
|
||||||
|
engine->OnRuntimeStop();
|
||||||
|
|
||||||
|
runtime = std::make_unique<MonoScriptRuntime>(CreateMonoSettings());
|
||||||
|
ASSERT_TRUE(runtime->Initialize()) << runtime->GetLastError();
|
||||||
|
|
||||||
|
engine->SetRuntime(runtime.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
engine->OnRuntimeStop();
|
||||||
|
engine->SetRuntime(nullptr);
|
||||||
|
runtime.reset();
|
||||||
|
scene.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene* CreateScene(const std::string& sceneName) {
|
||||||
|
scene = std::make_unique<Scene>(sceneName);
|
||||||
|
return scene.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptComponent* AddScript(GameObject* gameObject, const std::string& namespaceName, const std::string& className) {
|
||||||
|
ScriptComponent* component = gameObject->AddComponent<ScriptComponent>();
|
||||||
|
component->SetScriptClass("GameScripts", namespaceName, className);
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptEngine* engine = nullptr;
|
||||||
|
std::unique_ptr<MonoScriptRuntime> runtime;
|
||||||
|
std::unique_ptr<Scene> scene;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, InitializesAndDiscoversConcreteMonoBehaviourClasses) {
|
||||||
|
const std::vector<std::string> classNames = runtime->GetScriptClassNames("GameScripts");
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->IsInitialized());
|
||||||
|
EXPECT_TRUE(runtime->IsClassAvailable("GameScripts", "Gameplay", "LifecycleProbe"));
|
||||||
|
EXPECT_NE(std::find(classNames.begin(), classNames.end(), "Gameplay.LifecycleProbe"), classNames.end());
|
||||||
|
EXPECT_EQ(std::find(classNames.begin(), classNames.end(), "Gameplay.AbstractLifecycleProbe"), classNames.end());
|
||||||
|
EXPECT_EQ(std::find(classNames.begin(), classNames.end(), "Gameplay.UtilityHelper"), classNames.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineAppliesStoredFieldsAndInvokesLifecycleMethods) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
||||||
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
||||||
|
|
||||||
|
component->GetFieldStorage().SetFieldValue("Speed", 5.0f);
|
||||||
|
component->GetFieldStorage().SetFieldValue("Label", "Configured");
|
||||||
|
component->GetFieldStorage().SetFieldValue("Target", GameObjectReference{target->GetUUID()});
|
||||||
|
component->GetFieldStorage().SetFieldValue("SpawnPoint", XCEngine::Math::Vector3(2.0f, 4.0f, 6.0f));
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
engine->OnFixedUpdate(0.02f);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
engine->OnLateUpdate(0.016f);
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->HasManagedInstance(component));
|
||||||
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 1u);
|
||||||
|
|
||||||
|
int32_t awakeCount = 0;
|
||||||
|
int32_t enableCount = 0;
|
||||||
|
int32_t startCount = 0;
|
||||||
|
int32_t fixedUpdateCount = 0;
|
||||||
|
int32_t updateCount = 0;
|
||||||
|
int32_t lateUpdateCount = 0;
|
||||||
|
bool wasAwakened = false;
|
||||||
|
bool warningLogged = false;
|
||||||
|
bool errorLogged = false;
|
||||||
|
bool hasTransform = false;
|
||||||
|
bool transformLookupSucceeded = false;
|
||||||
|
bool hasUnsupportedComponent = true;
|
||||||
|
bool unsupportedComponentLookupReturnedNull = false;
|
||||||
|
bool targetResolved = false;
|
||||||
|
bool rotationAccessed = false;
|
||||||
|
bool scaleAccessed = false;
|
||||||
|
bool transformAccessed = false;
|
||||||
|
bool observedEnabled = false;
|
||||||
|
bool observedActiveSelf = false;
|
||||||
|
bool observedActiveInHierarchy = false;
|
||||||
|
bool observedIsActiveAndEnabled = false;
|
||||||
|
float speed = 0.0f;
|
||||||
|
float observedFixedDeltaTime = 0.0f;
|
||||||
|
float observedUpdateDeltaTime = 0.0f;
|
||||||
|
float observedLateDeltaTime = 0.0f;
|
||||||
|
std::string label;
|
||||||
|
std::string observedGameObjectName;
|
||||||
|
std::string observedTargetName;
|
||||||
|
GameObjectReference targetReference;
|
||||||
|
GameObjectReference selfReference;
|
||||||
|
XCEngine::Math::Vector4 observedLocalRotation;
|
||||||
|
XCEngine::Math::Vector3 observedLocalPosition;
|
||||||
|
XCEngine::Math::Vector3 observedLocalScale;
|
||||||
|
XCEngine::Math::Vector3 spawnPoint;
|
||||||
|
|
||||||
|
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, "FixedUpdateCount", fixedUpdateCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LateUpdateCount", lateUpdateCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "WasAwakened", wasAwakened));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "WarningLogged", warningLogged));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ErrorLogged", errorLogged));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasTransform", hasTransform));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TransformLookupSucceeded", transformLookupSucceeded));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasUnsupportedComponent", hasUnsupportedComponent));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UnsupportedComponentLookupReturnedNull", unsupportedComponentLookupReturnedNull));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetResolved", targetResolved));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "RotationAccessed", rotationAccessed));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ScaleAccessed", scaleAccessed));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TransformAccessed", transformAccessed));
|
||||||
|
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_TRUE(runtime->TryGetFieldValue(component, "Speed", speed));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFixedDeltaTime", observedFixedDeltaTime));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdateDeltaTime", observedUpdateDeltaTime));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLateDeltaTime", observedLateDeltaTime));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", label));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedGameObjectName", observedGameObjectName));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetName", observedTargetName));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Target", targetReference));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SelfReference", selfReference));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalRotation", observedLocalRotation));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalPosition", observedLocalPosition));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalScale", observedLocalScale));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SpawnPoint", spawnPoint));
|
||||||
|
|
||||||
|
EXPECT_EQ(awakeCount, 1);
|
||||||
|
EXPECT_EQ(enableCount, 1);
|
||||||
|
EXPECT_EQ(startCount, 1);
|
||||||
|
EXPECT_EQ(fixedUpdateCount, 1);
|
||||||
|
EXPECT_EQ(updateCount, 1);
|
||||||
|
EXPECT_EQ(lateUpdateCount, 1);
|
||||||
|
EXPECT_TRUE(wasAwakened);
|
||||||
|
EXPECT_TRUE(warningLogged);
|
||||||
|
EXPECT_TRUE(errorLogged);
|
||||||
|
EXPECT_TRUE(hasTransform);
|
||||||
|
EXPECT_TRUE(transformLookupSucceeded);
|
||||||
|
EXPECT_FALSE(hasUnsupportedComponent);
|
||||||
|
EXPECT_TRUE(unsupportedComponentLookupReturnedNull);
|
||||||
|
EXPECT_TRUE(targetResolved);
|
||||||
|
EXPECT_TRUE(rotationAccessed);
|
||||||
|
EXPECT_TRUE(scaleAccessed);
|
||||||
|
EXPECT_TRUE(transformAccessed);
|
||||||
|
EXPECT_TRUE(observedEnabled);
|
||||||
|
EXPECT_TRUE(observedActiveSelf);
|
||||||
|
EXPECT_TRUE(observedActiveInHierarchy);
|
||||||
|
EXPECT_TRUE(observedIsActiveAndEnabled);
|
||||||
|
EXPECT_FLOAT_EQ(speed, 6.0f);
|
||||||
|
EXPECT_FLOAT_EQ(observedFixedDeltaTime, 0.02f);
|
||||||
|
EXPECT_FLOAT_EQ(observedUpdateDeltaTime, 0.016f);
|
||||||
|
EXPECT_FLOAT_EQ(observedLateDeltaTime, 0.016f);
|
||||||
|
EXPECT_EQ(label, "Configured|Awake");
|
||||||
|
EXPECT_EQ(observedGameObjectName, "Host_Managed");
|
||||||
|
EXPECT_EQ(observedTargetName, "Target");
|
||||||
|
EXPECT_EQ(targetReference, GameObjectReference{target->GetUUID()});
|
||||||
|
EXPECT_EQ(selfReference, GameObjectReference{host->GetUUID()});
|
||||||
|
EXPECT_EQ(observedLocalRotation, XCEngine::Math::Vector4(0.0f, 0.5f, 0.0f, 0.8660254f));
|
||||||
|
EXPECT_EQ(observedLocalPosition, XCEngine::Math::Vector3(8.0f, 8.0f, 9.0f));
|
||||||
|
EXPECT_EQ(observedLocalScale, XCEngine::Math::Vector3(3.0f, 3.0f, 4.0f));
|
||||||
|
EXPECT_EQ(spawnPoint, XCEngine::Math::Vector3(3.0f, 4.0f, 6.0f));
|
||||||
|
EXPECT_EQ(host->GetName(), "Host_Managed");
|
||||||
|
EXPECT_EQ(host->GetTransform()->GetLocalPosition(), XCEngine::Math::Vector3(8.0f, 8.0f, 9.0f));
|
||||||
|
EXPECT_EQ(host->GetTransform()->GetLocalScale(), XCEngine::Math::Vector3(3.0f, 3.0f, 4.0f));
|
||||||
|
|
||||||
|
const XCEngine::Math::Quaternion& localRotation = host->GetTransform()->GetLocalRotation();
|
||||||
|
EXPECT_FLOAT_EQ(localRotation.x, 0.0f);
|
||||||
|
EXPECT_FLOAT_EQ(localRotation.y, 0.5f);
|
||||||
|
EXPECT_FLOAT_EQ(localRotation.z, 0.0f);
|
||||||
|
EXPECT_FLOAT_EQ(localRotation.w, 0.8660254f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, GameObjectComponentApiResolvesTransformAndRejectsUnsupportedManagedTypes) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
bool hasTransform = false;
|
||||||
|
bool transformLookupSucceeded = false;
|
||||||
|
bool hasUnsupportedComponent = true;
|
||||||
|
bool unsupportedComponentLookupReturnedNull = false;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasTransform", hasTransform));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TransformLookupSucceeded", transformLookupSucceeded));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasUnsupportedComponent", hasUnsupportedComponent));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UnsupportedComponentLookupReturnedNull", unsupportedComponentLookupReturnedNull));
|
||||||
|
|
||||||
|
EXPECT_TRUE(hasTransform);
|
||||||
|
EXPECT_TRUE(transformLookupSucceeded);
|
||||||
|
EXPECT_FALSE(hasUnsupportedComponent);
|
||||||
|
EXPECT_TRUE(unsupportedComponentLookupReturnedNull);
|
||||||
|
EXPECT_EQ(host->GetTransform()->GetLocalPosition(), XCEngine::Math::Vector3(7.0f, 8.0f, 9.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, ManagedBuiltInComponentWrappersReadAndWriteCameraAndLight) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
CameraComponent* camera = host->AddComponent<CameraComponent>();
|
||||||
|
LightComponent* light = host->AddComponent<LightComponent>();
|
||||||
|
ScriptComponent* component = AddScript(host, "Gameplay", "BuiltinComponentProbe");
|
||||||
|
|
||||||
|
camera->SetFieldOfView(52.0f);
|
||||||
|
camera->SetNearClipPlane(0.2f);
|
||||||
|
camera->SetFarClipPlane(300.0f);
|
||||||
|
camera->SetDepth(1.5f);
|
||||||
|
camera->SetPrimary(true);
|
||||||
|
|
||||||
|
light->SetIntensity(1.25f);
|
||||||
|
light->SetRange(12.0f);
|
||||||
|
light->SetSpotAngle(33.0f);
|
||||||
|
light->SetCastsShadows(false);
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
bool hasCamera = false;
|
||||||
|
bool hasLight = false;
|
||||||
|
bool cameraLookupSucceeded = false;
|
||||||
|
bool lightLookupSucceeded = false;
|
||||||
|
bool observedPrimary = false;
|
||||||
|
bool observedCastsShadows = true;
|
||||||
|
float observedFieldOfView = 0.0f;
|
||||||
|
float observedNearClipPlane = 0.0f;
|
||||||
|
float observedFarClipPlane = 0.0f;
|
||||||
|
float observedDepth = 0.0f;
|
||||||
|
float observedIntensity = 0.0f;
|
||||||
|
float observedRange = 0.0f;
|
||||||
|
float observedSpotAngle = 0.0f;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasCamera", hasCamera));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasLight", hasLight));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CameraLookupSucceeded", cameraLookupSucceeded));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LightLookupSucceeded", lightLookupSucceeded));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFieldOfView", observedFieldOfView));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedNearClipPlane", observedNearClipPlane));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFarClipPlane", observedFarClipPlane));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedDepth", observedDepth));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedPrimary", observedPrimary));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIntensity", observedIntensity));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRange", observedRange));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedSpotAngle", observedSpotAngle));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCastsShadows", observedCastsShadows));
|
||||||
|
|
||||||
|
EXPECT_TRUE(hasCamera);
|
||||||
|
EXPECT_TRUE(hasLight);
|
||||||
|
EXPECT_TRUE(cameraLookupSucceeded);
|
||||||
|
EXPECT_TRUE(lightLookupSucceeded);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(observedFieldOfView, 52.0f);
|
||||||
|
EXPECT_FLOAT_EQ(observedNearClipPlane, 0.2f);
|
||||||
|
EXPECT_FLOAT_EQ(observedFarClipPlane, 300.0f);
|
||||||
|
EXPECT_FLOAT_EQ(observedDepth, 1.5f);
|
||||||
|
EXPECT_TRUE(observedPrimary);
|
||||||
|
EXPECT_FLOAT_EQ(observedIntensity, 1.25f);
|
||||||
|
EXPECT_FLOAT_EQ(observedRange, 12.0f);
|
||||||
|
EXPECT_FLOAT_EQ(observedSpotAngle, 33.0f);
|
||||||
|
EXPECT_FALSE(observedCastsShadows);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(camera->GetFieldOfView(), 75.0f);
|
||||||
|
EXPECT_FLOAT_EQ(camera->GetNearClipPlane(), 0.3f);
|
||||||
|
EXPECT_FLOAT_EQ(camera->GetFarClipPlane(), 500.0f);
|
||||||
|
EXPECT_FLOAT_EQ(camera->GetDepth(), 3.0f);
|
||||||
|
EXPECT_FALSE(camera->IsPrimary());
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(light->GetIntensity(), 2.5f);
|
||||||
|
EXPECT_FLOAT_EQ(light->GetRange(), 42.0f);
|
||||||
|
EXPECT_FLOAT_EQ(light->GetSpotAngle(), 55.0f);
|
||||||
|
EXPECT_TRUE(light->GetCastsShadows());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, TransformHierarchyApiExposesParentChildAndReparenting) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
GameObject* root = runtimeScene->CreateGameObject("Root");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host", root);
|
||||||
|
GameObject* child = runtimeScene->CreateGameObject("Child", host);
|
||||||
|
GameObject* reparentTarget = runtimeScene->CreateGameObject("ReparentTarget");
|
||||||
|
ScriptComponent* component = AddScript(host, "Gameplay", "HierarchyProbe");
|
||||||
|
|
||||||
|
component->GetFieldStorage().SetFieldValue("ReparentTarget", GameObjectReference{reparentTarget->GetUUID()});
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
int32_t observedChildCount = 0;
|
||||||
|
bool parentLookupSucceeded = false;
|
||||||
|
bool childLookupSucceeded = false;
|
||||||
|
std::string observedParentName;
|
||||||
|
std::string observedFirstChildName;
|
||||||
|
std::string reparentedChildParentName;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedChildCount", observedChildCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ParentLookupSucceeded", parentLookupSucceeded));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ChildLookupSucceeded", childLookupSucceeded));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedParentName", observedParentName));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFirstChildName", observedFirstChildName));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ReparentedChildParentName", reparentedChildParentName));
|
||||||
|
|
||||||
|
EXPECT_TRUE(parentLookupSucceeded);
|
||||||
|
EXPECT_TRUE(childLookupSucceeded);
|
||||||
|
EXPECT_EQ(observedChildCount, 1);
|
||||||
|
EXPECT_EQ(observedParentName, "Root");
|
||||||
|
EXPECT_EQ(observedFirstChildName, "Child");
|
||||||
|
EXPECT_EQ(reparentedChildParentName, "ReparentTarget");
|
||||||
|
|
||||||
|
EXPECT_EQ(host->GetParent(), root);
|
||||||
|
EXPECT_EQ(host->GetChildCount(), 0u);
|
||||||
|
EXPECT_EQ(child->GetParent(), reparentTarget);
|
||||||
|
EXPECT_EQ(reparentTarget->GetChildCount(), 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, TransformSpaceApiReadsAndWritesWorldAndLocalValues) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
GameObject* parent = runtimeScene->CreateGameObject("Parent");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host", parent);
|
||||||
|
ScriptComponent* component = AddScript(host, "Gameplay", "TransformSpaceProbe");
|
||||||
|
|
||||||
|
parent->GetTransform()->SetLocalPosition(XCEngine::Math::Vector3(10.0f, 0.0f, 0.0f));
|
||||||
|
parent->GetTransform()->SetLocalScale(XCEngine::Math::Vector3(2.0f, 2.0f, 2.0f));
|
||||||
|
host->GetTransform()->SetLocalPosition(XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f));
|
||||||
|
host->GetTransform()->SetLocalScale(XCEngine::Math::Vector3(1.0f, 1.0f, 1.0f));
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
XCEngine::Math::Vector3 observedInitialWorldPosition;
|
||||||
|
XCEngine::Math::Vector3 observedInitialWorldScale;
|
||||||
|
XCEngine::Math::Vector3 observedInitialLocalEulerAngles;
|
||||||
|
XCEngine::Math::Vector3 observedEulerAfterSet;
|
||||||
|
XCEngine::Math::Vector3 observedWorldPositionAfterSet;
|
||||||
|
XCEngine::Math::Vector3 observedWorldScaleAfterSet;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialWorldPosition", observedInitialWorldPosition));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialWorldScale", observedInitialWorldScale));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialLocalEulerAngles", observedInitialLocalEulerAngles));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEulerAfterSet", observedEulerAfterSet));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedWorldPositionAfterSet", observedWorldPositionAfterSet));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedWorldScaleAfterSet", observedWorldScaleAfterSet));
|
||||||
|
|
||||||
|
EXPECT_EQ(observedInitialWorldPosition, XCEngine::Math::Vector3(12.0f, 4.0f, 6.0f));
|
||||||
|
EXPECT_EQ(observedInitialWorldScale, XCEngine::Math::Vector3(2.0f, 2.0f, 2.0f));
|
||||||
|
EXPECT_EQ(observedInitialLocalEulerAngles, XCEngine::Math::Vector3(0.0f, 0.0f, 0.0f));
|
||||||
|
EXPECT_NEAR(observedEulerAfterSet.x, 0.0f, 0.001f);
|
||||||
|
EXPECT_NEAR(observedEulerAfterSet.y, 45.0f, 0.001f);
|
||||||
|
EXPECT_NEAR(observedEulerAfterSet.z, 0.0f, 0.001f);
|
||||||
|
EXPECT_EQ(observedWorldPositionAfterSet, XCEngine::Math::Vector3(20.0f, 6.0f, 10.0f));
|
||||||
|
EXPECT_EQ(observedWorldScaleAfterSet, XCEngine::Math::Vector3(6.0f, 8.0f, 10.0f));
|
||||||
|
|
||||||
|
EXPECT_EQ(host->GetTransform()->GetLocalPosition(), XCEngine::Math::Vector3(5.0f, 3.0f, 5.0f));
|
||||||
|
EXPECT_EQ(host->GetTransform()->GetPosition(), XCEngine::Math::Vector3(20.0f, 6.0f, 10.0f));
|
||||||
|
EXPECT_EQ(host->GetTransform()->GetLocalScale(), XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f));
|
||||||
|
EXPECT_EQ(host->GetTransform()->GetScale(), XCEngine::Math::Vector3(6.0f, 8.0f, 10.0f));
|
||||||
|
|
||||||
|
const XCEngine::Math::Quaternion& localRotation = host->GetTransform()->GetLocalRotation();
|
||||||
|
EXPECT_FLOAT_EQ(localRotation.x, 0.0f);
|
||||||
|
EXPECT_FLOAT_EQ(localRotation.y, 0.5f);
|
||||||
|
EXPECT_FLOAT_EQ(localRotation.z, 0.0f);
|
||||||
|
EXPECT_FLOAT_EQ(localRotation.w, 0.8660254f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, TransformMotionApiExposesDirectionVectorsAndAppliesOperations) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
ScriptComponent* component = AddScript(host, "Gameplay", "TransformMotionProbe");
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
XCEngine::Math::Vector3 observedForward;
|
||||||
|
XCEngine::Math::Vector3 observedRight;
|
||||||
|
XCEngine::Math::Vector3 observedUp;
|
||||||
|
XCEngine::Math::Vector3 observedPositionAfterSelfTranslate;
|
||||||
|
XCEngine::Math::Vector3 observedPositionAfterWorldTranslate;
|
||||||
|
XCEngine::Math::Vector3 observedForwardAfterWorldRotate;
|
||||||
|
XCEngine::Math::Vector3 observedForwardAfterSelfRotate;
|
||||||
|
XCEngine::Math::Vector3 observedForwardAfterLookAt;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForward", observedForward));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRight", observedRight));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUp", observedUp));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedPositionAfterSelfTranslate", observedPositionAfterSelfTranslate));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedPositionAfterWorldTranslate", observedPositionAfterWorldTranslate));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterWorldRotate", observedForwardAfterWorldRotate));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterSelfRotate", observedForwardAfterSelfRotate));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterLookAt", observedForwardAfterLookAt));
|
||||||
|
|
||||||
|
ExpectVector3Near(observedForward, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
||||||
|
ExpectVector3Near(observedRight, XCEngine::Math::Vector3(0.0f, 0.0f, -1.0f));
|
||||||
|
ExpectVector3Near(observedUp, XCEngine::Math::Vector3(0.0f, 1.0f, 0.0f));
|
||||||
|
ExpectVector3Near(observedPositionAfterSelfTranslate, XCEngine::Math::Vector3(2.0f, 0.0f, 0.0f));
|
||||||
|
ExpectVector3Near(observedPositionAfterWorldTranslate, XCEngine::Math::Vector3(2.0f, 0.0f, 3.0f));
|
||||||
|
ExpectVector3Near(observedForwardAfterWorldRotate, XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
||||||
|
ExpectVector3Near(observedForwardAfterSelfRotate, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
||||||
|
ExpectVector3Near(observedForwardAfterLookAt, XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
||||||
|
|
||||||
|
ExpectVector3Near(host->GetTransform()->GetPosition(), XCEngine::Math::Vector3(2.0f, 0.0f, 3.0f));
|
||||||
|
ExpectVector3Near(host->GetTransform()->GetForward(), XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, TransformConversionApiTransformsPointsAndDirectionsBetweenSpaces) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
ScriptComponent* component = AddScript(host, "Gameplay", "TransformConversionProbe");
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
XCEngine::Math::Vector3 observedWorldPoint;
|
||||||
|
XCEngine::Math::Vector3 observedLocalPoint;
|
||||||
|
XCEngine::Math::Vector3 observedWorldDirection;
|
||||||
|
XCEngine::Math::Vector3 observedLocalDirection;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedWorldPoint", observedWorldPoint));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalPoint", observedLocalPoint));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedWorldDirection", observedWorldDirection));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalDirection", observedLocalDirection));
|
||||||
|
|
||||||
|
ExpectVector3Near(observedWorldPoint, XCEngine::Math::Vector3(7.0f, 0.0f, 1.0f));
|
||||||
|
ExpectVector3Near(observedLocalPoint, XCEngine::Math::Vector3(0.0f, 0.0f, 2.0f));
|
||||||
|
ExpectVector3Near(observedWorldDirection, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
||||||
|
ExpectVector3Near(observedLocalDirection, XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
||||||
|
|
||||||
|
ExpectVector3Near(host->GetTransform()->TransformPoint(XCEngine::Math::Vector3(0.0f, 0.0f, 2.0f)), XCEngine::Math::Vector3(7.0f, 0.0f, 1.0f));
|
||||||
|
ExpectVector3Near(host->GetTransform()->InverseTransformDirection(XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f)), XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, ManagedBehaviourCanDisableItselfThroughEnabledProperty) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
||||||
|
|
||||||
|
component->GetFieldStorage().SetFieldValue("DisableSelfOnFirstUpdate", true);
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
engine->OnLateUpdate(0.016f);
|
||||||
|
|
||||||
|
int32_t updateCount = 0;
|
||||||
|
int32_t lateUpdateCount = 0;
|
||||||
|
int32_t disableCount = 0;
|
||||||
|
bool observedEnabled = false;
|
||||||
|
bool observedIsActiveAndEnabled = false;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LateUpdateCount", lateUpdateCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "DisableCount", disableCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEnabled", observedEnabled));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIsActiveAndEnabled", observedIsActiveAndEnabled));
|
||||||
|
|
||||||
|
EXPECT_FALSE(component->IsEnabled());
|
||||||
|
EXPECT_EQ(updateCount, 1);
|
||||||
|
EXPECT_EQ(lateUpdateCount, 0);
|
||||||
|
EXPECT_EQ(disableCount, 1);
|
||||||
|
EXPECT_TRUE(observedEnabled);
|
||||||
|
EXPECT_TRUE(observedIsActiveAndEnabled);
|
||||||
|
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
||||||
|
EXPECT_EQ(updateCount, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, ManagedScriptCanDeactivateItsHostGameObject) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
||||||
|
|
||||||
|
component->GetFieldStorage().SetFieldValue("DeactivateGameObjectOnFirstUpdate", true);
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
engine->OnLateUpdate(0.016f);
|
||||||
|
|
||||||
|
int32_t updateCount = 0;
|
||||||
|
int32_t lateUpdateCount = 0;
|
||||||
|
int32_t disableCount = 0;
|
||||||
|
bool observedActiveSelf = false;
|
||||||
|
bool observedActiveInHierarchy = false;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LateUpdateCount", lateUpdateCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "DisableCount", disableCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveSelf", observedActiveSelf));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveInHierarchy", observedActiveInHierarchy));
|
||||||
|
|
||||||
|
EXPECT_FALSE(host->IsActive());
|
||||||
|
EXPECT_FALSE(host->IsActiveInHierarchy());
|
||||||
|
EXPECT_TRUE(component->IsEnabled());
|
||||||
|
EXPECT_EQ(updateCount, 1);
|
||||||
|
EXPECT_EQ(lateUpdateCount, 0);
|
||||||
|
EXPECT_EQ(disableCount, 1);
|
||||||
|
EXPECT_TRUE(observedActiveSelf);
|
||||||
|
EXPECT_TRUE(observedActiveInHierarchy);
|
||||||
|
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
||||||
|
EXPECT_EQ(updateCount, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MonoScriptRuntimeTest, DisableEnablePreservesSingleInstanceAndStartOnlyRunsOnce) {
|
||||||
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
||||||
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||||
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
||||||
|
|
||||||
|
engine->OnRuntimeStart(runtimeScene);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
component->SetEnabled(false);
|
||||||
|
|
||||||
|
int32_t disableCount = 0;
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "DisableCount", disableCount));
|
||||||
|
EXPECT_EQ(disableCount, 1);
|
||||||
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 1u);
|
||||||
|
|
||||||
|
component->SetEnabled(true);
|
||||||
|
engine->OnUpdate(0.016f);
|
||||||
|
|
||||||
|
int32_t enableCount = 0;
|
||||||
|
int32_t startCount = 0;
|
||||||
|
int32_t updateCount = 0;
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "EnableCount", enableCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
||||||
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
||||||
|
|
||||||
|
EXPECT_EQ(enableCount, 2);
|
||||||
|
EXPECT_EQ(startCount, 1);
|
||||||
|
EXPECT_EQ(updateCount, 2);
|
||||||
|
|
||||||
|
engine->OnRuntimeStop();
|
||||||
|
|
||||||
|
EXPECT_FALSE(runtime->HasManagedInstance(component));
|
||||||
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
Reference in New Issue
Block a user