🎮비밀 단어 설정

⚙️비밀 단어 설정

BullCowCartridge의 OnInput 함수는 사용자가 문자를 입력하고 엔터 키를 누르면 호출되는 함수이다. OnInput 함수의 내부 구현을 간단하게 알아보려고 한다.

OnInput 함수는 Cartridge 클래스에서 순수 가상 함수로 선언되어 있다.(Cartridge 클래스는 BullCowCartridge 클래스의 부모 클래스이다.) 따라서 이 함수는 Cartridge 클래스에 정의되어 있지 않고 자식 클래스들에서 정의될 수 있다.

우리는 터미널이라는 곳에 문자를 입력하고 출력하는 게임을 만들고 있기 때문에 Terminal 클래스에서 OnInput 함수를 사용하는 것을 알 수 있다. Terminal 클래스에서 어떻게 OnInput 함수가 사용되는지 알아보자.

Terminal 클래스의 ActivateTerminal 함수에서 키 바인딩에 의해 아무 키나 눌렀을 때 OnKeyDown 함수가 호출된다. 그 다음 OnKeyDown 함수에서 입력된 키가 Enter라면 AcceptInputLine 함수가 호출되고 Cartridge 클래스의 OnInput 함수가 AcceptInputLine 함수 안에서 호출된다.

Cartridge 클래스의 OnInput 함수는 앞에서 말했듯이 순수 가상 함수이기 때문에 오버라이딩된 자식들의 OnInput 함수가 터미널 클래스에서 호출될 수 있다.

Input 함수에 비밀 단어를 설정해보고 맞고 틀림에 따라 특정 메시지를 출력하는 기능을 구현하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// BullCowCartridge.cpp

void UBullCowCartridge::OnInput(const FString& PlayerInput)
{
    ClearScreen();

    FString HiddenWord = TEXT("dogs");

    if(PlayerInput == HiddenWord)
    {
      PrintLine(TEXT("You Win!"));
    }
    else
    {
      PrintLine(TEXT("Game Over!"));
    }
}



⚙️Isogram 체크

Isogram은 한 단어에 같은 글자가 없는 단어를 의미한다. 비밀 단어가 Isogram인지 체크하여 참 또는 거짓을 반환하는 함수를 만들어 보자.

함수의 원형은 bool UBullCowCartridge::IsIsogram(const FString& Word) const 로 만들어 볼 수 있다. 함수에 들어오는 인자의 값이 변하지 않도록 매개변수 앞에 const 키워드를 사용하였고 이 멤버함수가 멤버변수들의 값을 변하게 하지 않기 위해 const 키워드를 사용해 const 멤버함수로 만들었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// BullCowCartridge.h

#pragma once

#include "CoreMinimal.h"
#include "Console/Cartridge.h"
#include "BullCowCartridge.generated.h"

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class BULLCOWGAME_API UBullCowCartridge : public UCartridge
{
	GENERATED_BODY()

public:
	virtual void BeginPlay() override;
	virtual void OnInput(const FString& PlayerInput) override;
	bool IsIsogram(const FString& Word) const;
};


문자열이 Isogram인지 체크하는 함수는 인자로 들어온 비밀 문자열의 첫 번째 문자 부터 끝 문자 앞(null문자 앞)까지 순회하면서 첫 번째 인덱스의 문자와 그 다음 인덱스의 문자들을 순차적으로 비교하고 만약 같은 문자가 있으면 거짓을 반환하고 같은 문자가 없다면 참을 반환한다.

예를 들어, 문자열 Word의 첫 번째 인덱스 번호 0인 문자 Word[0]을 Word[1]과 비교한다. 그리고 Word[0]과 Word[2]를 비교한다. 이런 식으로 순차적으로 첫 번째 문자와 나머지 문자들을 비교하는 방법이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// BullCowCartridge.cpp

bool UBullCowCartridge::IsIsogram(const FString& Word) const
{
    for (int32 i = 0; i < Word.Len(); ++i)
    {
        for (int32 j = i + 1; j < Word.Len(); ++j)
        {
            if (Word[i] == Word[j])
            {
                return false;
            }
        }
    }

    return true;
}



⚙️TArray에 비밀 단어 담아서 사용

언리얼에서 배열을 사용하고자 할 때 TArray라는 동적 배열을 사용한다. TArray를 사용하여 비밀 단어들을 여러 개 넣어서 순차적으로 사용해보고자 한다. 우선 TArray를 멤버변수로 선언해준다. 비밀 문자열을 저장해줄 멤버변수도 헤더파일에 선언해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// BullCowCartridge.h

#pragma once

#include "CoreMinimal.h"
#include "Console/Cartridge.h"
#include "BullCowCartridge.generated.h"

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class BULLCOWGAME_API UBullCowCartridge : public UCartridge
{
	GENERATED_BODY()

public:
	virtual void BeginPlay() override;
	virtual void OnInput(const FString& PlayerInput) override;
	bool IsIsogram(const FString& Word) const;
  TArray<FString> Words;
  TArray<FString> GetValidWords(const TArray<FString>& WordList) const;

private:
  FString HiddenWord;
};


배열에 넣어 줄 단어들은 BeginPlay 함수에다가 간단하게 몇 개 넣어보자.

1
2
3
4
5
6
void UBullCowCartridge::BeginPlay() // When the game starts
{
    Super::BeginPlay();
    
    Words = {TEXT("trust"), TEXT("coffee"), TEXT("bee"), TEXT("responsible"), TEXT("jazz")};
}


다음으로 TArray에 들어있는 단어들이 사용하기에 적합한 단어인지 체크하고 사용 가능한 단어들이 들어있는 배열을 반환하는 함수를 구현해본다. 헤더 파일에 함수 원형을 선언해주고 소스파일에 함수에 대한 정의를 해준다.

먼저 GetValidWords 함수 안에 사용 가능한 단어들을 담을 임시 배열을 선언해준다. 그 다음 배열의 첫 번째 원소 부터 끝 원소까지 순회하면서 원소의 길이가 3이상 8이하인 문자열을 찾고 그 문자열이 Isogram인지 IsIsogram 함수를 이용하여 확인한다. 해당 문자열이 Isogram이 맞으면 함수 안에 임시로 만들었던 배열의 끝에 삽입(Emplace)한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// BullCowCartridge.cpp

TArray<FString> UBullCowCartridge::GetValidWords(const TArray<FString>& WordList) const
{    
    TArray<FString> ValidWords;
    
    //for (int32 i = 0; i < WordList.Num(); ++i)
    //{
    //    if (4 <= WordList[i].Len() && WordList[i].Len() <= 8)
    //    {
    //        if (IsIsogram(WordList[i]))
    //        {
    //            ValidWords.Emplace(WordList[i]);
    //        }
    //    }
    //}

    // Range based for loop
    for (FString Element : WordList)
    {
		    if (3 <= Element.Len() && Element.Len() <= 8)
		    {
			    if (IsIsogram(Element))
			    {
                ValidWords.Emplace(Element);
			    }
		    }
    }

    return ValidWords;
}


💡Emplace vs Add
TArray 끝에 원소를 삽입할 때 Emplace와 Add 함수를 사용할 수 있다. 두 함수가 배열 끝에 원소를 삽입한다는 기능은 거의 같지만 둘 사이에 살짝 다른 점이 있다.

배열에 문자열을 삽입한다고 했을 때, Add는 스트링 리터럴에서 임시 FString 을 생성한 다음, 그 임시 내용물을 컨테이너 안의 새로운 FString으로 복사한다. 반면 Emplace는 스트링 리터럴을 사용해서 FString을 직접 만든다. 결과는 같지만, Emplace는 임시 변수 생성을 하지 않는다는 점에서 차이가 있다.

일반적으로 Emplace가 호출되는 곳에 인스턴스를 임시 생성 후 컨테이너에 복사 내지 이동하는 불필요한 절차를 피할 수 있기 때문에 Add 보다 좋다.



⚙️배열에 담긴 문자열 램덤하게 사용

배열에 담긴 문자열을 무작위로 사용하려면 배열의 인덱스를 랜덤하게 설정할 수 있으면 된다. 배열의 인덱스를 랜덤하게 설정하기 위해 FMath::RandRange 함수를 사용하였다. FMath::RandRange(int32 Min, int32 Max) 함수는 최소값과 최대값 사이의 수를 무작위로 반환하는 함수이다. 공식 문서를 보면 이 함수는 Math/UnrealMathUtility.h를 포함하여 사용할 수 있다고 나와있다.

BullCowCartridge.h를 보면 CoreMinimal.h를 포함시켰는데 여기에 Math/UnrealMathUtility.h가 포함되어 있어 따로 헤더 파일을 포함시켜줄 필요가 없다. 이제 소스파일의 BeginPlay() 함수에 FMath::RandRange를 사용하여 배열에 담긴 문자열 램덤하게 사용해보겠다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// BullCowCartridge.h

#pragma once

#include "CoreMinimal.h"
#include "Console/Cartridge.h"
#include "BullCowCartridge.generated.h"

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class BULLCOWGAME_API UBullCowCartridge : public UCartridge
{
	GENERATED_BODY()

public:
	virtual void BeginPlay() override;
	virtual void OnInput(const FString& PlayerInput) override;
	bool IsIsogram(const FString& Word) const;
  TArray<FString> Words;
  TArray<FString> GetValidWords(const TArray<FString>& WordList) const;

private:
  FString HiddenWord;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// BullCowCartridge.cpp

#include "BullCowCartridge.h"

void UBullCowCartridge::BeginPlay() // When the game starts
{
    Super::BeginPlay();

    Words = {TEXT("trust"), TEXT("coffee"), TEXT("bee"), TEXT("responsible"), TEXT("jazz")};
    
    // 배열 첫 번째 인덱스 0이 최소값
    // 배열 마지막 인덱스인 (배열 길이 - 1)이 최대값
    HiddenWord = GetValidWord(Words)[FMath::RandRange(0, Words.Num() - 1)];
}



Leave a comment