본문 바로가기

Unreal

[04] Actor Spawn, Algo/Sort

외형과 수치를 가지게 된 유닛을 월드에 소환할 생각이다.

그러기 전에, 유닛이 어디에 소환될 지에 대한 정보가 없으므로 유닛 소환 위치를 지정하는 방에 대해 먼저 작성해보자.

20x20x15 m 크기의 방. 이 방이 5x5로 총 25개 배열된 곳을 월드처럼 사용할 것이다.

그리고 각 방에 Spawn Point를 저장하고, 저장한 Point에 유닛을 소환시키는게 목표가 된다.

그러니 RoomBase.cpp 파일을 만들어서 SpawnLocations를 저장할 수 있는 벡터를 만들어주자.

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Room 관련", meta = (AllowPrivateAccess = "true"))
TArray<FVector> SpawnLocations;

이제 각 방은 SpawnLoactions를 가지게 되고, Spawner를 하나 따로 만들어서 모든 방에 대한 정보를 읽어온 다음 소환을 시키면 되는 일이다. 

그러기 위해서는 우선 월드에 배치하고, 연동될 UI를 만들어야한다.

5x5로 배치한 Room들. 그리고 각 Room과 연동시킬 UI이미지다. 왼쪽 상단의 UI에서 룸의 ID와 X, Y 값을 누르고 버튼을 누르면 Room의 구성을 변경시킬 예정이다. 그러기 위해서는 새로운 Room이 필요하니, 색깔만 다른 Room을 하나 만들어서 교체해보자.

20x20x15로 받은 줄 알았는데, 21x21x10.5였다. 내부 칸을 얘기하는거였구나. 방끼리 겹치는 곳은 나중에 해결해야겠다. 크기를 다시 재거나.

 

그럼 이제 필요한 것을 생각해보자.

1. 지정된 위치에 Unit 스폰시키기.

2. 지정된 위치의 Room을 다른 Room으로 변경시키기.

3. 1, 2번 기능을 UI를 통해 작동 가능하게 하기.

4. 각 Room에 배치된 Unit을 Room UI에 띄우기.

 

1번부터 만들어보자. 우선 여기의 '지정된 위치' 라는 곳은 Room의 SpawnLocation이므로, RoomBase 코드에 몬스터를 소환하는 로직을 만들면 될 것이다.

Spawner를 만들어서 Unit을 관리하는 것도 생각해보았지만, 각각의 Unit이 Room에 귀속되는 형태로 게임을 만들 예정이기에, Room 자체에서 Monster를 관리하는 것이 좋다는 생각이 들었다. 

 

//RoomBase.h
TArray<AUnit*> UnitsInRoom;
//RoomBase.cpp
void ARoomBase::SpawnUnitInRoom(TSubclassOf<AUnit> UnitClass, FVector Location, FRotator Rotation)
AUnit* SpawnedUnit = GetWorld()->SpawnActor<AUnit>(UnitClass, Location, Rotation, SpawnParams);

그러니 RoomBase.h에 유닛들이 방에 귀속될 때 들어갈 배열을 선언해주고, Unit 클래스를 방 안에 소환하는 함수를 작성해주자. 이후 SpawnParms는 FActorSpawnParameters 형태의 구조체인데, 나중에 방 안에 HUD로 전투 진행상황을 띄우고, 유닛끼리 안 겹치게 스폰하고 싶어서 넣었다.

SpawnParams.Owner = this;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
SpawnParams.Instigator = GetInstigator();

 

 

나는 이 Owner와 Instigator의 개념이 서로 같은 줄 알았는데, 소유자와 선동자로 구분되고 있었다. 이 부분은 추가적으로 공부가 필요할 것 같다. 

 

내가 이해한 것으로는 방의 함정이 있다고 했을 때,

방에 함정을 설치한 사람 -> 나(Owner)

방의 함정을 발동시킨 사람 -> 함정을 밟은 유닛(Instigator)이라고 이해했다.

 

원래 UE가 FPS를 기준으로 만들어졌다는 것을 생각해보면 총알의 Owner는 총알을 생성한 총. Instigator는 총을 발사한 사람(발사라는 행동을 실행했으므로) 이 된다. (개발에서 총은 그냥 총알 생성기라고 봐야하니까.)

 

게임 시작할 때 유닛 자동 스폰되게 하기.

방 자체에 게임이 시작할 때 -> 몬스터 소환. 이라는 이벤트를 설정하고, 각각 잘 실행하는지 로그를 찍어봤다.

이제 유닛은 각 방에 소환되고, 각 방에 귀속된 상태가 된다.

 

첫 번째 단계가 잘 진행되는 것을 확인했으니, 두 번째 단계인 깔려있는 기본 방을 내가 원하는 방으로 바꿀 수 있도록 변경해보자. 

 

이걸 어떤 식으로 접근할 지 고민인데, 일단 가장 먼저 떠오른 방식은 새로운 걸 똑같은 위치에 만든 다음 기존의 것을 비활성화 하는 것이다. 두 번째는 방은 그대로 내버려두고, 방이 바뀔 때 변하는 것만 직접 바꿔주는 것. 

 

아마 첫 번째를 택하면 Room을 관리하는 Spawner에서 진행하고,  두 번째를 택하면 Room 자체에 함수로 다른 방으로 변하는 것을 집어넣어야 하지 않을까? 

 

우선 간단하다고 생각되는 새로운 것을 똑같은 위치에 만들어서 기존의 것을 비활성화 하는 방식을 진행해보자. 이 경우는 Room에 들어있는 유닛을 어떻게 할 지 고민이지만, 그건 나중에 생각하고 방만 변경해보자.

 

게임이 시작하면 -> 방들을 찾고 -> Spawner에 들어있는 방 배열에서 0번 방을 변경하는 블루프린트

이렇게 0번 방을 바꾸는 블루프린트를 실행해보면, 어떤 방이 바뀌는 것을 확인할 수 있다. 다만 방의 위치에 상관없이 랜덤하게 하나의 방이 바뀌므로, 중간에 RoomsInWorld 배열을 정렬할 필요가 있다. 

Location값을 받아와서 정렬 후 다시 삽입하면, 이제서야 원하는 위치에 있는 방을 원하는 방으로 변경 가능해졌다.

 

그런데 사실 여기 정렬 과정을 찾는데 너무 오랜 시간이 걸렸다. 다음에 또 같은 실수를 할까봐 몇가지 적어놓자면,

https://forums.unrealengine.com/t/how-to-sort-tarray-in-c/465585

 

How to sort TArray in c++?

If i have an array of pointers: TArray<OfClassPointers*> TheArray; I cant do like this : TheArray.Sort([](const OfClassPointers& ip1, const OfClassPointers& ip2) { return ip1.level < ip2.level; // or ip1-> or even *ip-> }); Documentation says use Algo::Sor

forums.unrealengine.com

이 게시글에 답변된 것들을 참고해서

//null 제거
RoomsInWorld.RemoveAll([](const ARoomBase* Room){ return Room == nullptr; });

Algo::SortBy(RoomsInWorld, [](const ARoomBase* Room) -> FVector
{
    return Room->GetActorLocation();
}, [](const FVector& LocationA, const FVector& LocationB)
{
    if (LocationA.Y == LocationB.Y)
    {
       return LocationA.X < LocationB.X;
    }
    return LocationA.Y < LocationB.Y;
});

UE에서 제공하는 Algo/sort.h 를 include 한 이후에 위처럼 해결했다. 생각보다 머리아픈 시간이었다.

이제 정렬이 끝났으니, 다음은 UI연결을 할 시간이다.

'Unreal' 카테고리의 다른 글

[06] 프로젝트 공유랑 방향  (0) 2024.11.20
[05] Unreal Singleton/Get Actor of Class  (2) 2024.09.04
[03] Pawn C++ Class  (0) 2024.08.02
[02] Unreal Engine Learning  (1) 2024.05.16
[01] Unreal Engine Learning  (0) 2024.05.01