저번 포스팅은 난이도에 따라 열쇠갯수를 정하고 전부다 먹었을 경우 게임 클리어 화면과 함게 메인메뉴로 돌아가는 기능을 구현했는데요
이번엔 메인메뉴에서 게임시작, 게임종료, 난이도설정을 이용해 많이 부족하지만 데모버전인 게임같은 게임느낌을 내보겠습니다.
먼저 메인메뉴라 정보를 표시해주는 텍스트와 이벤트를 가지고 있는 버튼을 선언해 주겠습니다.
[Header("게임 Panel")]
[SerializeField] private GameObject mainPanel;
[SerializeField] private GameObject difficultyPanel;
[Header("메인 화면 버튼")]
[SerializeField] private Button startButton;
[SerializeField] private Button difficultyButton;
[SerializeField] private Button quitButton;
[Header("난이도 선택 버튼")]
[SerializeField] private Button easyButton;
[SerializeField] private Button normalButton;
[SerializeField] private Button hardButton;
[SerializeField] private Button backButton;
[Header("난이도 표시 텍스트")]
[SerializeField] private TextMeshProUGUI difficultyText;
private Difficulty selectedDifficulty = Difficulty.Normal;
깔끔한 편은 아니지만 Header를 이용해 파트를 나눴습니다.
메인메뉴에서 볼 수 있는 패널은 딱 2종류 입니다.
기본적으로 메인패널이 가장먼저 보이고 난이도설정 버튼을 눌러 난이도설정 패널로 진입할 수 있습니다. 물론 난이도설정에서 메인메뉴로 돌아갈 수 있게 구현해 두었습니다.
void Start()
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
//메인 메뉴 버튼
if(startButton != null)
{
startButton.onClick.AddListener(StartGame);
}
if(difficultyButton != null)
{
difficultyButton.onClick.AddListener(OpenDifficultyPanel);
}
if(quitButton != null)
{
quitButton.onClick.AddListener(QuitGame);
}
//난이도 선택 버튼
if(easyButton != null) easyButton.onClick.AddListener(() => SelectDifficulty(Difficulty.Easy));
if(normalButton != null) normalButton.onClick.AddListener(() => SelectDifficulty(Difficulty.Normal));
if(hardButton != null) hardButton.onClick.AddListener(() => SelectDifficulty(Difficulty.Hard));
if(backButton != null) backButton.onClick.AddListener(OpenMainPanel);
//초기 설정 메인 패널만 보이게 하기
ShowMainPanel();
UpdateDifficultyDisplay();
}
startButton 같은 경우엔 StartGame메서드를 호출, difficultyButton엔 난이도 설정 패널을 열 수 있게 해주는 메서드를 호출 등등 각각 버튼에 따른 이벤트를 연결해 주었습니다.
이후 난이도 선택 버튼을 구현해 주었는데 개발중 한가지 문제가 생겼습니다.
난이도 선택까지는 되는데 Easy나 Hard같은 버튼을 클릭하면 바로 게임이 실행되는 버그였습니다.
if(easyButton != null) easyButton.onClick.AddListener(() => SelectDifficulty(Difficulty.Easy));
해당 코드를 보면 람다식 ()=> 를 이용해서 익명함수를 바로 만들어 실행하게 되는 구조입니다.

Easy,Normal,Hard 버튼에 OnClick() 이벤트가 추가되어 중복실행이 되는 경우였습니다. (예시를 위해 그냥 추가만 해놓음)
난이도 조절 버튼 인스펙터내에 OnClick()이벤트를 전부 제거하니까 정상 작동하는 모습을 보였습니다.
//메인 패널로 돌아가기
void OpenMainPanel()
{
ShowMainPanel();
}
void SelectDifficulty(Difficulty difficulty)
{
selectedDifficulty = difficulty;
UpdateDifficultyDisplay();
HighLightButton();
}
람다를 통해 SelectDifficulty 메서드로 사용자가 선택한 난이도 저장과 UI업데이트를 구현했습니다.
void ShowMainPanel()
{
if(mainPanel != null) mainPanel.SetActive(true);
if(difficultyPanel != null) difficultyPanel.SetActive(false);
}
// 난이도 설정 패널 열기
void OpenDifficultyPanel()
{
if(mainPanel != null) mainPanel.SetActive(false);
if(difficultyPanel != null) difficultyPanel.SetActive(true);
UpdateDifficultyDisplay();
}
해당 메서드를 호출해 메인패널과 난이도설정 패널을 보이게 구현해 두었습니다. 처음엔 씬을 두개 만들어 놓고 씬을 전환하면서 하는게 좋을까 싶었지만 패널을 만들어 전환하는게 가장 편하고 빠르다는걸 알았습니다.

메인 패널에 버튼 3가지 이미지 입니다.
난이도 설정 패널안에 난이도를 설정할때 현재 상황을 보여주게 만든 메서드 입니다.
void UpdateDifficultyDisplay()
{
if(difficultyText != null)
{
string difficultyInfo = "";
switch(selectedDifficulty)
{
case Difficulty.Easy:
difficultyInfo = "Easy - Key : 3";
break;
case Difficulty.Normal:
difficultyInfo = "Normal - Key : 5";
break;
case Difficulty.Hard:
difficultyInfo = "Hard - Key : 8";
break;
}
difficultyText.text = $"Mode : {difficultyInfo}";
}
}
난이도에 따라서 난이도 이름, 획득해야 되는 열쇠 개수를 작성해 놓았습니다.

해당 난이도 설정 패널에서 난이도 선택시 변경 사항이 우측 상단에 표시됩니다.
void HighLightButton()
{
ResetButtonColor();
ColorBlock highLightColors = new ColorBlock
{
normalColor = Color.yellow,
highlightedColor = Color.yellow,
pressedColor = new Color(1f, 0.8f, 0f),
selectedColor = Color.yellow,
disabledColor = Color.gray,
colorMultiplier = 1,
fadeDuration = 0.1f
};
switch(selectedDifficulty)
{
case Difficulty.Easy:
if(easyButton != null) easyButton.colors = highLightColors;
break;
case Difficulty.Normal:
if(normalButton != null) normalButton.colors = highLightColors;
break;
case Difficulty.Hard :
if(hardButton != null) hardButton.colors = highLightColors;
break;
}
}
void ResetButtonColor() //버튼 색상 기본으로 복구
{
ColorBlock defaultColors = ColorBlock.defaultColorBlock;
if(easyButton != null) easyButton.colors = defaultColors;
if(normalButton != null) normalButton.colors = defaultColors;
if(hardButton != null) hardButton.colors = defaultColors;
}
겉보기엔 단순 UI코드 같지만, 실제로는 선택 상태를 시각적을 보여주는 UX로직입니다.
ResetButtonColor( ); 을 먼저 호출하는 이유는 모든 버튼을 기본으로 되돌리기 때문입니다. 선택은 하나인데 화면에 여러개의 버튼에 하이라이트가 있으면 전부다 선택된 것 처럼 되기 때문에 초반에 초기화를 해준 것 입니다.
Unity Button 컴포넌트가 상태별 색상을 관리하는 구조체입니다.
상태 : 의미
normalColor : 기본 상태
highlightedColor : 마우스 오버
pressedColor : 클릭 중
selectedColor : 선택 상태
disabledColor : 비활성
결론적으로 이 부분을 바꾸면 버튼이 다른 스타일로 변하게 됩니다.
현재 사용자가 선택한 난이도에 따라 해당 버튼만 하이라이트 색상을 적용합니다.

해당 코드를 적용해 실제 게임 난이도를 설정하면 적용된 난이도가 하이라이트 표시가 됩니다.

마지막으로 시작과 나가기 버튼 연결을 위한 메서드 입니다.
마지막 QuitGame 메서드를 보면 #if UNITY_EDITOR라는 #이 들어간 조건문이 있습니다. 이것은 C# 문법이 아닌 전처리 지시문입니다
코드를 컴파일 할때 조건에 따라 포함, 제외 시키는 문법입니다.
#if -> 조건이 참이면 아래 코드 포함
#else -> 아니면 이쪽 코드 포함
#endif -> 조건문 끝
지금 빌드가 어떤 환경인가에 따라 다른 코드로 컴파일 됩니다.
#IF 조건문에 들어가는 UNITY_EDITER는 유니티가 자동으로 정의하는 컴파일 상수입니다.
유니티 에디터에서 실행되면 True
빌드된 게임에서 실행되면 False 입니다.
유니티 에디터에서 실행해 QuitGame을 하면 Play모드가 종료 되지만
빌드된 실제 게임에서 QuitGame을 실행하면 프로그램 종료가 됩니다.
해당내용 커밋사항 URL 주소:
https://github.com/Junamgyu/PAT_Project/commit/45a5812d9223b1cf99977b6e7de9a1e97d5626fc