편집기 환경에서 문자열에서 클래스 얻기

11473 단어 위 4UnrealEngine
오랜만의 투고입니다. 원격 작업 환경이 갖추어질 때까지 여가가 생겼기 때문에 편집기 환경에서 사용할 수있는 작은 재료를 하나

문자열에서 UClass를 얻고 싶습니다.



에디터 환경에서 자동 배치나, 정기적인 처리를 달릴 때에 캐릭터 라인으로부터 UClass를 원할 경우가 있습니다.
예를 들어 외부에서 가져온 CSV에 클래스 이름과 위치 정보를 비롯한 정보가 있으며, 그것을 기반으로 에디터 월드에 자동 배치하고 싶을 때 등입니다.

UE4와 리플렉션



UE4에서는 리플렉션이 채용되고 있으므로, 캐릭터 라인으로부터 클래스를 취해 오거나 함수를 호출해 프로퍼티에 액세스 하는 등의 것이 RTTi를 사용하지 않고 실현할 수 있습니다. 이것이 BP와 cpp를 연결하는 키모의 부분이기도 합니다.
※게임에 있어서의 리플렉션에 대해서, UE 빼기로의 설명은 오마에씨의 MagicReflection의 슬라이드와 코드를 보는 것을 알기 쉽다고 생각합니다.
슬라이드
GitHub

구현



이번에는 FString을 전달하면 UClass를 반환하는 함수를 BP 함수 라이브러리 중 하나로 만듭니다. 이것만 할 수 있으면 후에는 어떻게 되든지 하므로, 예에 나타낸 것 같은 테이블을 보면서 배치하는 실전적인 실장은 단단히 접습니다.

이 구현을 생각할 때, FindObject 하는 것만으로 끝나는 것이라고 생각하거나 해 버립니다만, 그것이 잘 되는 것은, 예를 들어 컨텐츠 브라우저상으로부터 더블 클릭으로 열리는 등해, 에디터 기동 후의 어딘가입니다에서 패키지를 읽는 클래스입니다.

실제로는 다음을 고려하지 않으면 대부분의 경우에 의도한 동작이 되지 않습니다.
  • 네이티브가 아닌, BP 클래스를 Cpp 측에서 취급하는 경우, _C 가 클래스명의 말미에 대해 있는 것을 고려해야 한다.
  • 이름 바꾸기 후 Fixup되지 않은 리디렉터에 얽힌 클래스의 경우
  • 로드되지 않은 경우 (시작 후 편집기에서 열리지 않음)

  • 그것들을 고려한 것이 다음의 함수입니다.
    ※3/28 리팩토링&고속화, 함수명은 from보다 by라고 깨닫는 것도 화상 촬영이 맨드이므로 그대로 하고 있습니다.
    UClass* UMyBlueprintFunctionLibrary::GetClassFromName(const FString& name) {
        UClass* ret = nullptr;
    #if UE_EDITOR
        auto findClass = [](const FString& name) {
            UClass* result = nullptr;
            result = FindObject<UClass>(ANY_PACKAGE, *name);
            if (nullptr == result) {
                // 見つからなかった場合、BPクラスの可能性があるので_Cを付けて再検索.
                FString bpname = name;
                bpname.Append(TEXT("_C"));
                result = FindObject<UClass>(ANY_PACKAGE, *bpname);
                if (nullptr == result) {
                    // それでも見つからない場合、リダイレクタの可能性を探る.
                    UObjectRedirector* objRedirector = FindObject<UObjectRedirector>(ANY_PACKAGE, *name);
                    if (nullptr != objRedirector && nullptr != objRedirector->DestinationObject) {
                        result = Cast<UClass>(objRedirector->DestinationObject);
                    }
                    else {
                        objRedirector = FindObject<UObjectRedirector>(ANY_PACKAGE, *bpname);
                        if (nullptr != objRedirector && nullptr != objRedirector->DestinationObject) {
                            result = Cast<UClass>(objRedirector->DestinationObject);
                        }
                    }
                }
            }
            return result;
        };
    
        // まずはロード済みの物から検索.
        ret = findClass(name);
    
        if (nullptr == ret) {
            // 最後に読み込まれていない可能性があるので、ロードを試みる.
            UPackage* package = LoadPackage(nullptr, *name, LOAD_Verify | LOAD_NoWarn);
            if (nullptr != package) {
                package->FullyLoad();
                // もう一回検索.
                ret = findClass(name);
            }       
        }
    #endif
        return ret;
    }
    

    동작 테스트



    테스트를 위해 다음과 같은 클래스 이름의 문자열, 위치, 레이블을 지정하여 배치하는 간단한 위젯을 만들었습니다.

    위젯의 구현은, 작성한 “GetClassFromName” 함수로부터 클래스를 취득해 한편 클래스가 액터였을 경우 스폰 하도록(듯이) 하고 있습니다.

    스폰 함수는 C++로 쓰고 있지만 테스트에 적합합니다. 본격적으로 한다면 UWorld::SpawnActor가 아니라 UWorld::SpawnActorDeferred를 사용하여 세밀한 속성을 설정하는 것이 좋습니다.
     AActor* UMyBlueprintFunctionLibrary::SpawnActor(UObject* worldObj, UClass* actorClass, const FVector& location, const FName& label) {
        AActor* ret = nullptr;
        ret = worldObj->GetWorld()->SpawnActor<AActor>(actorClass, location, FRotator());
        ret->SetActorLabel(label.ToString());
        return ret;
    }
    

    시도에 StaticMeshActor를 배치해 보았습니다.
    클래스명의 캐릭터 라인으로부터, 지정한 클래스, 위치, 라벨로 올바르게 스폰 되었습니다.

    다음에 에디터 기동 후 한번도 로드되어 있지 않은 BP 클래스의 배치를 시도했습니다.
    이쪽도 지정대로 배치할 수 있었습니다.


    마지막으로



    이번에는 4.24.3에서 움직이는 코드로 썼습니다. 4.25부터 Preview가 벗어났을 때에 다시 확인하려고 합니다.
    옛날 실장했을 때는 좀 더 심플하게 쓸 수 있다고 생각하기 때문에, 그 근처의 돌진 있으면 기다리고 있습니다.

    좋은 웹페이지 즐겨찾기