Align Unity-style object and hierarchy scripting APIs

This commit is contained in:
2026-04-03 14:31:07 +08:00
parent 9edf378085
commit 5225faff1d
10 changed files with 765 additions and 22 deletions

View File

@@ -109,18 +109,8 @@ MonoScriptRuntime* GetActiveMonoScriptRuntime() {
return dynamic_cast<MonoScriptRuntime*>(ScriptEngine::Get().GetRuntime());
}
ManagedComponentTypeInfo ResolveManagedComponentTypeInfo(MonoReflectionType* reflectionType) {
ManagedComponentTypeInfo ResolveManagedComponentTypeInfo(MonoClass* monoClass) {
ManagedComponentTypeInfo typeInfo;
if (!reflectionType) {
return typeInfo;
}
MonoType* monoType = mono_reflection_type_get_type(reflectionType);
if (!monoType) {
return typeInfo;
}
MonoClass* monoClass = mono_class_from_mono_type(monoType);
if (!monoClass) {
return typeInfo;
}
@@ -163,6 +153,19 @@ ManagedComponentTypeInfo ResolveManagedComponentTypeInfo(MonoReflectionType* ref
return typeInfo;
}
ManagedComponentTypeInfo ResolveManagedComponentTypeInfo(MonoReflectionType* reflectionType) {
if (!reflectionType) {
return {};
}
MonoType* monoType = mono_reflection_type_get_type(reflectionType);
if (!monoType) {
return {};
}
return ResolveManagedComponentTypeInfo(mono_class_from_mono_type(monoType));
}
Components::GameObject* FindGameObjectByUUIDRecursive(Components::GameObject* gameObject, uint64_t uuid) {
if (!gameObject) {
return nullptr;
@@ -231,28 +234,32 @@ ScriptComponent* FindScriptComponentByUUID(uint64_t scriptComponentUUID) {
return nullptr;
}
bool HasNativeComponent(Components::GameObject* gameObject, ManagedComponentKind componentKind) {
Components::Component* FindNativeComponent(Components::GameObject* gameObject, ManagedComponentKind componentKind) {
if (!gameObject) {
return false;
return nullptr;
}
switch (componentKind) {
case ManagedComponentKind::Transform:
return gameObject->GetTransform() != nullptr;
return gameObject->GetTransform();
case ManagedComponentKind::Camera:
return gameObject->GetComponent<Components::CameraComponent>() != nullptr;
return gameObject->GetComponent<Components::CameraComponent>();
case ManagedComponentKind::Light:
return gameObject->GetComponent<Components::LightComponent>() != nullptr;
return gameObject->GetComponent<Components::LightComponent>();
case ManagedComponentKind::MeshFilter:
return gameObject->GetComponent<Components::MeshFilterComponent>() != nullptr;
return gameObject->GetComponent<Components::MeshFilterComponent>();
case ManagedComponentKind::MeshRenderer:
return gameObject->GetComponent<Components::MeshRendererComponent>() != nullptr;
return gameObject->GetComponent<Components::MeshRendererComponent>();
case ManagedComponentKind::Script:
case ManagedComponentKind::Unknown:
return false;
return nullptr;
}
return false;
return nullptr;
}
bool HasNativeComponent(Components::GameObject* gameObject, ManagedComponentKind componentKind) {
return FindNativeComponent(gameObject, componentKind) != nullptr;
}
Components::Component* AddOrGetNativeComponent(Components::GameObject* gameObject, ManagedComponentKind componentKind) {
@@ -309,6 +316,87 @@ ScriptComponent* FindMatchingScriptComponent(
return nullptr;
}
ScriptComponent* FindMatchingScriptComponentInChildren(
Components::GameObject* gameObject,
const ManagedComponentTypeInfo& typeInfo) {
if (!gameObject) {
return nullptr;
}
if (ScriptComponent* component = FindMatchingScriptComponent(gameObject, typeInfo)) {
return component;
}
for (Components::GameObject* child : gameObject->GetChildren()) {
if (ScriptComponent* component = FindMatchingScriptComponentInChildren(child, typeInfo)) {
return component;
}
}
return nullptr;
}
ScriptComponent* FindMatchingScriptComponentInParent(
Components::GameObject* gameObject,
const ManagedComponentTypeInfo& typeInfo) {
while (gameObject) {
if (ScriptComponent* component = FindMatchingScriptComponent(gameObject, typeInfo)) {
return component;
}
gameObject = gameObject->GetParent();
}
return nullptr;
}
Components::Component* FindNativeComponentInChildren(
Components::GameObject* gameObject,
ManagedComponentKind componentKind) {
if (!gameObject) {
return nullptr;
}
if (Components::Component* component = FindNativeComponent(gameObject, componentKind)) {
return component;
}
for (Components::GameObject* child : gameObject->GetChildren()) {
if (Components::Component* component = FindNativeComponentInChildren(child, componentKind)) {
return component;
}
}
return nullptr;
}
Components::Component* FindNativeComponentInParent(
Components::GameObject* gameObject,
ManagedComponentKind componentKind) {
while (gameObject) {
if (Components::Component* component = FindNativeComponent(gameObject, componentKind)) {
return component;
}
gameObject = gameObject->GetParent();
}
return nullptr;
}
bool DestroyNativeComponentInstance(Components::GameObject* gameObject, Components::Component* component) {
if (!gameObject || !component || component == gameObject->GetTransform()) {
return false;
}
if (component->IsEnabled() && gameObject->IsActiveInHierarchy()) {
component->OnDisable();
}
component->OnDestroy();
return gameObject->RemoveComponent(component);
}
Components::CameraComponent* FindCameraComponent(uint64_t gameObjectUUID) {
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
return gameObject ? gameObject->GetComponent<Components::CameraComponent>() : nullptr;
@@ -520,6 +608,72 @@ MonoObject* InternalCall_GameObject_GetComponent(uint64_t gameObjectUUID, MonoRe
return runtime->CreateManagedComponentWrapper(typeInfo.monoClass, gameObjectUUID);
}
MonoObject* InternalCall_GameObject_GetComponentInChildren(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
if (!gameObject) {
return nullptr;
}
MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime();
if (!runtime) {
return nullptr;
}
const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(componentType);
if (typeInfo.kind == ManagedComponentKind::Script) {
ScriptComponent* component = FindMatchingScriptComponentInChildren(gameObject, typeInfo);
if (!component) {
return nullptr;
}
if (!runtime->HasManagedInstance(component)) {
ScriptEngine::Get().OnScriptComponentEnabled(component);
}
return runtime->GetManagedInstanceObject(component);
}
Components::Component* component = FindNativeComponentInChildren(gameObject, typeInfo.kind);
if (!component || !typeInfo.monoClass) {
return nullptr;
}
return runtime->CreateManagedComponentWrapper(typeInfo.monoClass, component->GetGameObject()->GetUUID());
}
MonoObject* InternalCall_GameObject_GetComponentInParent(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
if (!gameObject) {
return nullptr;
}
MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime();
if (!runtime) {
return nullptr;
}
const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(componentType);
if (typeInfo.kind == ManagedComponentKind::Script) {
ScriptComponent* component = FindMatchingScriptComponentInParent(gameObject, typeInfo);
if (!component) {
return nullptr;
}
if (!runtime->HasManagedInstance(component)) {
ScriptEngine::Get().OnScriptComponentEnabled(component);
}
return runtime->GetManagedInstanceObject(component);
}
Components::Component* component = FindNativeComponentInParent(gameObject, typeInfo.kind);
if (!component || !typeInfo.monoClass) {
return nullptr;
}
return runtime->CreateManagedComponentWrapper(typeInfo.monoClass, component->GetGameObject()->GetUUID());
}
MonoObject* InternalCall_GameObject_AddComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
if (!gameObject) {
@@ -588,6 +742,15 @@ void InternalCall_GameObject_Destroy(uint64_t gameObjectUUID) {
scene->DestroyGameObject(gameObject);
}
void InternalCall_Object_Destroy(MonoObject* object) {
MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime();
if (!runtime || !object) {
return;
}
runtime->DestroyManagedObject(object);
}
mono_bool InternalCall_Behaviour_GetEnabled(uint64_t scriptComponentUUID) {
ScriptComponent* component = FindScriptComponentByUUID(scriptComponentUUID);
return (component && component->IsEnabled()) ? 1 : 0;
@@ -1241,10 +1404,13 @@ void RegisterInternalCalls() {
mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetActive", reinterpret_cast<const void*>(&InternalCall_GameObject_SetActive));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_HasComponent", reinterpret_cast<const void*>(&InternalCall_GameObject_HasComponent));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponent", reinterpret_cast<const void*>(&InternalCall_GameObject_GetComponent));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponentInChildren", reinterpret_cast<const void*>(&InternalCall_GameObject_GetComponentInChildren));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponentInParent", reinterpret_cast<const void*>(&InternalCall_GameObject_GetComponentInParent));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_AddComponent", reinterpret_cast<const void*>(&InternalCall_GameObject_AddComponent));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_Find", reinterpret_cast<const void*>(&InternalCall_GameObject_Find));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_Create", reinterpret_cast<const void*>(&InternalCall_GameObject_Create));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_Destroy", reinterpret_cast<const void*>(&InternalCall_GameObject_Destroy));
mono_add_internal_call("XCEngine.InternalCalls::Object_Destroy", reinterpret_cast<const void*>(&InternalCall_Object_Destroy));
mono_add_internal_call("XCEngine.InternalCalls::Behaviour_GetEnabled", reinterpret_cast<const void*>(&InternalCall_Behaviour_GetEnabled));
mono_add_internal_call("XCEngine.InternalCalls::Behaviour_SetEnabled", reinterpret_cast<const void*>(&InternalCall_Behaviour_SetEnabled));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetLocalPosition", reinterpret_cast<const void*>(&InternalCall_Transform_GetLocalPosition));
@@ -2224,6 +2390,73 @@ MonoObject* MonoScriptRuntime::CreateManagedGameObject(uint64_t gameObjectUUID)
return managedObject;
}
bool MonoScriptRuntime::DestroyManagedObject(MonoObject* managedObject) {
if (!m_initialized || !managedObject) {
return false;
}
SetCurrentDomain();
MonoClass* monoClass = mono_object_get_class(managedObject);
if (!monoClass) {
return false;
}
if (monoClass == m_gameObjectClass) {
uint64_t gameObjectUUID = 0;
mono_field_get_value(managedObject, m_managedGameObjectUUIDField, &gameObjectUUID);
Components::Scene* scene = GetInternalCallScene();
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
if (!scene || !gameObject || gameObject->GetScene() != scene) {
return false;
}
scene->DestroyGameObject(gameObject);
return true;
}
if (monoClass != m_componentClass && !mono_class_is_subclass_of(monoClass, m_componentClass, false)) {
return false;
}
uint64_t gameObjectUUID = 0;
mono_field_get_value(managedObject, m_gameObjectUUIDField, &gameObjectUUID);
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
if (!gameObject) {
return false;
}
if (monoClass == m_behaviourClass || mono_class_is_subclass_of(monoClass, m_behaviourClass, false)) {
uint64_t scriptComponentUUID = 0;
mono_field_get_value(managedObject, m_scriptComponentUUIDField, &scriptComponentUUID);
if (scriptComponentUUID != 0) {
ScriptComponent* component = FindScriptComponentByUUID(scriptComponentUUID);
return DestroyNativeComponentInstance(gameObject, component);
}
}
const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(monoClass);
switch (typeInfo.kind) {
case ManagedComponentKind::Camera:
return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent<Components::CameraComponent>());
case ManagedComponentKind::Light:
return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent<Components::LightComponent>());
case ManagedComponentKind::MeshFilter:
return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent<Components::MeshFilterComponent>());
case ManagedComponentKind::MeshRenderer:
return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent<Components::MeshRendererComponent>());
case ManagedComponentKind::Transform:
case ManagedComponentKind::Script:
case ManagedComponentKind::Unknown:
return false;
}
return false;
}
bool MonoScriptRuntime::TryExtractGameObjectReference(
MonoObject* managedObject,
GameObjectReference& outReference) const {