Add Unity-style GetComponents scripting API

This commit is contained in:
2026-04-03 14:51:52 +08:00
parent 5225faff1d
commit 0f51f553c8
8 changed files with 240 additions and 10 deletions

View File

@@ -262,6 +262,15 @@ bool HasNativeComponent(Components::GameObject* gameObject, ManagedComponentKind
return FindNativeComponent(gameObject, componentKind) != nullptr;
}
bool IsMatchingScriptComponent(const ScriptComponent* component, const ManagedComponentTypeInfo& typeInfo) {
return component
&& component->HasScriptClass()
&& typeInfo.kind == ManagedComponentKind::Script
&& component->GetAssemblyName() == typeInfo.assemblyName
&& component->GetNamespaceName() == typeInfo.namespaceName
&& component->GetClassName() == typeInfo.className;
}
Components::Component* AddOrGetNativeComponent(Components::GameObject* gameObject, ManagedComponentKind componentKind) {
if (!gameObject) {
return nullptr;
@@ -302,13 +311,7 @@ ScriptComponent* FindMatchingScriptComponent(
}
for (ScriptComponent* component : gameObject->GetComponents<ScriptComponent>()) {
if (!component || !component->HasScriptClass()) {
continue;
}
if (component->GetAssemblyName() == typeInfo.assemblyName
&& component->GetNamespaceName() == typeInfo.namespaceName
&& component->GetClassName() == typeInfo.className) {
if (IsMatchingScriptComponent(component, typeInfo)) {
return component;
}
}
@@ -423,6 +426,28 @@ Components::Space ResolveManagedSpace(int32_t value) {
: Components::Space::Self;
}
MonoArray* CreateManagedComponentArray(MonoClass* componentClass, const std::vector<MonoObject*>& components) {
if (!componentClass) {
return nullptr;
}
MonoDomain* domain = mono_domain_get();
if (!domain) {
return nullptr;
}
MonoArray* array = mono_array_new(domain, componentClass, static_cast<uintptr_t>(components.size()));
if (!array) {
return nullptr;
}
for (uintptr_t index = 0; index < components.size(); ++index) {
mono_array_setref(array, index, components[index]);
}
return array;
}
void InternalCall_Debug_Log(MonoString* message) {
XCEngine::Debug::Logger::Get().Info(
XCEngine::Debug::LogCategory::Scripting,
@@ -608,6 +633,74 @@ MonoObject* InternalCall_GameObject_GetComponent(uint64_t gameObjectUUID, MonoRe
return runtime->CreateManagedComponentWrapper(typeInfo.monoClass, gameObjectUUID);
}
MonoArray* InternalCall_GameObject_GetComponents(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.monoClass) {
return nullptr;
}
std::vector<MonoObject*> managedComponents;
auto appendNativeComponents = [&](const auto& nativeComponents) {
for (const auto* component : nativeComponents) {
if (!component || !component->GetGameObject()) {
continue;
}
if (MonoObject* managedObject =
runtime->CreateManagedComponentWrapper(typeInfo.monoClass, component->GetGameObject()->GetUUID())) {
managedComponents.push_back(managedObject);
}
}
};
switch (typeInfo.kind) {
case ManagedComponentKind::Script:
for (ScriptComponent* component : gameObject->GetComponents<ScriptComponent>()) {
if (!IsMatchingScriptComponent(component, typeInfo)) {
continue;
}
if (!runtime->HasManagedInstance(component)) {
ScriptEngine::Get().OnScriptComponentEnabled(component);
}
if (MonoObject* managedObject = runtime->GetManagedInstanceObject(component)) {
managedComponents.push_back(managedObject);
}
}
break;
case ManagedComponentKind::Transform:
appendNativeComponents(gameObject->GetComponents<Components::TransformComponent>());
break;
case ManagedComponentKind::Camera:
appendNativeComponents(gameObject->GetComponents<Components::CameraComponent>());
break;
case ManagedComponentKind::Light:
appendNativeComponents(gameObject->GetComponents<Components::LightComponent>());
break;
case ManagedComponentKind::MeshFilter:
appendNativeComponents(gameObject->GetComponents<Components::MeshFilterComponent>());
break;
case ManagedComponentKind::MeshRenderer:
appendNativeComponents(gameObject->GetComponents<Components::MeshRendererComponent>());
break;
case ManagedComponentKind::Unknown:
return nullptr;
}
return CreateManagedComponentArray(typeInfo.monoClass, managedComponents);
}
MonoObject* InternalCall_GameObject_GetComponentInChildren(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
if (!gameObject) {
@@ -1404,6 +1497,7 @@ 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_GetComponents", reinterpret_cast<const void*>(&InternalCall_GameObject_GetComponents));
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));