[Unreal] 카드게임 - 타일기반 행동

개요

개발중인 게임은 카드게임이지만 보드가 존재하여 타일기반의 맵에서 게임을 진행한다.

게임에서 보드를 기반으로 할 수 있는 행동은 크게 3가지 이다.

  • 카드를 소모해 유닛 소환
    • 현재 내 유닛의 주변 타일에만 유닛 소환가능 
  • 유닛 이동
    • 유닛의 주변 타일로 이동가능
  • 유닛 공격
    • 공격범위에서 상대 유닛이 있을 경우 공격 가능  

각각의 행동들마다 제약이 있어서 구현이 까다로웠다.

내가 생각한 방법은 Tile에 State를 만들고 Tile 클릭시 State마다 다른 행동을 하도록 구현했다.

TileState

  • None : 클릭 시 아무 행동도 안함
  • Spawnable : Spawn 가능한 Tile
  • Moveable: 클릭시 Move
  • Attackable : 클릭시 공격

기본적으로 None 상태로 있다가 Spawn을 원할때 (카드를 drag하고 있을때) 는 Spawn 가능한 Tile을 Set하고 유닛을 클릭했을 때 해당 유닛이 이동가능한, 공격 가능한 Tile을 Set하도록 했다.

 

MapManager

MapManager의 경우 GameInstanceSubsystem으로 만들어서 전역접근이 가능하도록 했다.

멀티플레이가 붙는다면 아마 GameState로 들어가지 않을까 한다. -> 맵상황은 모두가 공유해야하나까

MapManager에서는 각 Tile의 State를 조정할 수 있는함수를 선언했다.

행동을 결정할 때 SetSpawnable, SetMoveable을 먼저 호출하고 그 뒤에 SetAllNone을 호출하여 맵을 초기화하도록 함수를 구성했다.

class THEWILDCARD_API UWildMapManager : public UGameInstanceSubsystem
{
	GENERATED_BODY()

public:
	void GenerateMap(TSubclassOf<AWildMapBase> MapClass);
	TArray<AWildTileBase*> GetInjectTiles(AWildUnitBase* Unit);

	UFUNCTION(BlueprintCallable)
	void SetTileSpawnable();
	UFUNCTION(BlueprintCallable)
	void SetTileMoveable(AWildUnitBase* Unit);
	UFUNCTION(BlueprintCallable)
	void SetTileAttackable(AWildUnitBase* Unit);
	UFUNCTION(BlueprintCallable)
	void SetAllTileNone();
	UFUNCTION(BlueprintCallable)
	AWildTileBase* GetTileWithCord(int x, int y);
void UWildMapManager::SetTileSpawnable()
{
   TArray<AWildUnitBase*> Units =  GetWorld()->GetGameInstance()->GetSubsystem<UWildUnitManager>()->PlayerUnits;

  for (int i = 0; i < Units.Num(); i++)
  {
    TArray<AWildTileBase*> InjectTiles = GetInjectTiles(Units[i]);
    for (int j = 0; j < InjectTiles.Num(); j++)
    {
      InjectTiles[j]->SetTileState(ETileState::Spawnable);
    }
  }
}

//선택한 Unit의 주변만 Moveable로 바꿔줌
void UWildMapManager::SetTileMoveable(AWildUnitBase* Unit)
{
  TArray<AWildTileBase*> InjectTiles = GetInjectTiles(Unit);
  for (int i = 0; i < InjectTiles.Num(); i++)
  {
      if (InjectTiles[i]->SpawnedUnit) continue;
    InjectTiles[i]->SetTileState(ETileState::Moveable);
  }
}

void UWildMapManager::SetTileAttackable(AWildUnitBase* Unit)
{
}

void UWildMapManager::SetAllTileNone()
{
  for (int x = 0; x < CurrentMap->MapSize; x++)
  {
    for (int y = 0; y < CurrentMap->MapSize; y++)
    {
      CurrentMap->Tiles[x].Col[y]->SetTileState(ETileState::None);
    }
  }
}

UnitBase

Move의 경우 Unit의 행동이기 때문에 UnitBase에 선언한다.

CurrentTile을 비워주고 목표타일의 정보로 Unit 정보를 갱신한다. 실제 Actor의 위치도 Tile 위치로 옮겨준다.

void AWildUnitBase::MoveToTile(AWildTileBase* Tile)
{
	CurrentTile->SpawnedUnit = nullptr;
	CurrentTile = Tile;
	CurrentCord = Tile->Cordinate;
	CurrentTile->SpawnedUnit = this;
	SetActorLocation(Tile->GetActorLocation());
	AddActorLocalOffset(FVector(0.f, 50.f, 0.f));

	GetGameInstance()->GetSubsystem<UWildMapManager>()->SetAllTileNone();
}

TileBase

실제 Move를 어디서 호출할지 생각해보면 Moveable인 Tile을 클릭했을때 실제 Move가 이뤄져야한다.

OnClick에서 State가 Moveable일때 Move를 실행하도록 한다.

결과

Spawnable은 카드를 드래그 했을때 활성화 되도록 했다.