오늘 배운 것
1. 자투리 지식
2. 코드 유지보수
3. 알고리즘 만들어보기
1. 자투리 지식
다른 프로그램 진행중 | ||
랜덤 생성 |
이런 식으로 메모리가 있다면 , 우리가 만들 수 있는 객체가 어디 생성될 지 모른다.
그래서 객체를 변수에 담고(주소/위치를 저장하고) 그 주소를 이용하는 것
하이얼아키창에 있다고 메모리에 올라온 것이 아니다.
씬이 로드되었을 때 메모리 상에 올라간다.
프리팹으로 만든 파일은 계속 존재한다
Ram에 올라간 것과 SSD에 있는 것의 차이를 잘 생각해야한다.
2. 코드 유지보수
지금 현재 코드의 문제점
씬을 분리하게 되면서 패널을 두 번 구현해야 하는 문제가 생김.
게임 씬과 메인 씬
여러가지 방법이 있지만 지금은 패널 매니저를 없에고, 패널 컨트롤러를 단독으로 생성되도록 바꿀 예정
패널 매니저 없이 혼자서 생성되는 형태 + 프리팹 형태로 저장 >> 메인 씬에서든 게임 씬에서든 호출 할 수 있게
추가적인 이슈는 게임 매니저 관련 이슈
지금 게임 매니저를 싱글톤으로 만들었음
메인씬에서 게임씬으로 갔다가 다시 메인씬으로 가면, 블럭컨트롤러 UI컨트롤러의 연결이 끊어짐
그리고 다시 시작하면 끊어진 상태가 유지됨.
그래서 게임씬으로 넘어 갔을 때 블럭컨트롤러와 게임유아이 컨트롤러를 찾아서 할당해야함
protected override void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (scene.name == "Game")
{
blockController = GameObject.FindObjectOfType<BlockController>();
gameUIController = GameObject.FindObjectOfType<GameUIController>();
StartGame();
}
}
원래 게임 매니저 코드에서 다음과 같이 고침.
Start에서 호출하면 blockController 등이 할당되지 않았는데 할당되어버릴 수 있음
순서가 중요하다는 것. StartGame()이 실행되기 전에 blockController, gameUIController를 설정해 두어야 한다.
지금 버튼을 누르면 바로 메인 창으로 넘어가버림.
우리는 DoTwin을 사용해서 패널 창이 열고 닫힐 때 애니메이션을 설정했기 때문에 이게 끝나고 씬이동이 되게 하고 싶음
애니메이션이 끝나고 할 수 있게 여러 방법이 있음
1. Hide에 델리게이트를 인자로 받는 오버라이드
public void Hide(OnConfirmButtonClick onConfirmButtonClick)
{
_backgroundCanvasGroup.DOFade(0, 0.2f).SetEase(Ease.Linear);
panelRectTransform.DOScale(0, 0.2f).SetEase(Ease.OutBack).OnComplete(() => onConfirmButtonClick?.Invoke());
}
2. 그냥 인보크 할 떄 시간을 줌
public void OnClickConfirmButton()
{
Invoke(nameof(InvokeConfirmButton), 1f);
Hide();
}
private void InvokeConfirmButton()
{
_onConfirmButtonClick?.Invoke();
}
3. 비슷한 방법으로 딜레이를 줌 애니메이션 시간에 맞게
cs public async void OnClickConfirmButton()
{
Hide();
await Task.Delay(500);
onConfirmButtonClick?.Invoke();
}
이런 방식도 가능, 넣어줄게 없으면 기본적으로 null을 넣던가 기본값을 null로 설정하면 된다 .
4. 강사님 방식, Hide에 델리게이터를 인자로 받아서 바로 실행되는 것이 아닌 OnComplete가 끝나고 실행 되게 만든다.
public void Hide(PanelControllerHideDelegate onHideDelegate = null)
{
_backgroundCanvasGroup.alpha = 1;
panelRectTransform.localScale = Vector3.one;
_backgroundCanvasGroup.DOFade(0, 0.3f).SetEase(Ease.Linear);
panelRectTransform.DOScale(0, 0.3f)
.SetEase(Ease.InBack).OnComplete(() =>
{
onHideDelegate?.Invoke();
Destroy(gameObject);
});
}
틱택토 로직 만들기
AI가 판단하는 로직을 스스로 만들어 봤다.
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
//만일 특정 타일에 플레이어 A타입이 놓였을 경우
if (checkBoard[row, col] == GameManager.PlayerType.None)
{
checkBoard[row, col] = GameManager.PlayerType.PlayerA;
}
var gameResult = GameManager.CheckGameResult(checkBoard);
if (gameResult == GameManager.GameResult.Check)
{
PrintBoard(checkBoard);
Debug.Log("플레이어가 이길 것 같아서 놨습니다.");
return (row, col);
}
checkBoard[row, col] = GameManager.PlayerType.None;
}
여기서 모든 것을 널로 만들어서 틀렸었다.
즉, 바꾼 것만 null, 원래 것으로 바꿨어야 했는데 모든 것을 null로 만들어서 틀렸었다.
using System;
using UnityEngine;
using UnityEngine.SceneManagement;
public class AIController : Singleton<AIController>
{
public GameManager GameManager;
// 체크할 새로운 보드를 만듬
public GameManager.PlayerType[,] checkBoard = new GameManager.PlayerType[3, 3];
private void PrintBoard(GameManager.PlayerType[,] board)
{
string boardString = "";
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
boardString += $"{(int)board[row, col]} "; // PlayerType 값을 숫자로 출력
}
boardString += "\n"; // 한 줄 출력 후 줄 바꿈
}
Debug.Log("\n" + boardString); // 콘솔 창에 보드 출력
}
public (int row, int col) FindNextMove(GameManager.PlayerType[,] board)
{
// TODO: board의 내용을 보고 다음 수를 계산 후 반환
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
checkBoard[row, col] = board[row, col]; // 개별적으로 복사
}
}
// 킬각을 먼저 잡는다.
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
bool move = false;
//만일 특정 타일에 플레이어 B타입이 놓였을 경우
if (checkBoard[row, col] == GameManager.PlayerType.None)
{
move = true;
checkBoard[row, col] = GameManager.PlayerType.PlayerB;
}
var gameResult = GameManager.CheckGameResult(checkBoard);
if (gameResult == GameManager.GameResult.Check)
{
PrintBoard(checkBoard);
Debug.Log("AI가 이길 것 같아서 놨습니다.");
return (row, col);
}
if (move) checkBoard[row, col] = GameManager.PlayerType.None;
}
}
// 모든 타일에 하나씩 놓이는 경우를 조사.
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
bool move = false;
//만일 특정 타일에 플레이어 A타입이 놓였을 경우
if (checkBoard[row, col] == GameManager.PlayerType.None)
{
move = true;
checkBoard[row, col] = GameManager.PlayerType.PlayerA;
}
var gameResult = GameManager.CheckGameResult(checkBoard);
if (gameResult == GameManager.GameResult.Check)
{
Debug.Log("플레이어가 이길 것 같아서 놨습니다.");
return (row, col);
}
if (move) checkBoard[row, col] = GameManager.PlayerType.None;
}
}
// 가운데는 전략적 요충지이므로, 당장 이기거나 지는 상황이 아닐 때는 중간을 선점
if (board[1, 1] == GameManager.PlayerType.None)
{
return (1, 1);
}
//일단 테스트하려고 했는데 빈 곳에 둠
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
if (board[row, col] == GameManager.PlayerType.None)
{
return (row, col);
}
}
}
return (0, 0);
}
protected override void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
}
}
정상작동한다 ! 약 2시간 소요.... 중간에 문제되었던 점은 위의 null로 만드는 것과
어려웠던 점
public GameResult CheckGameResult(PlayerType[,] board)
{
if (CheckGameWin(PlayerType.PlayerA, board) && board == _board)
{
return GameResult.Win;
}
if (CheckGameWin(PlayerType.PlayerA, board) && board == aiController.checkBoard)
{
return GameResult.Check;
}
if (CheckGameWin(PlayerType.PlayerB, board) && board == _board)
{
return GameResult.Lose;
}
if (CheckGameWin(PlayerType.PlayerB, board) && board == aiController.checkBoard)
{
return GameResult.Check;
}
if (IsAllBlocksPlaced())
{
return GameResult.Draw;
}
return GameResult.None;
}
다음 코드에서 중간에 board 분기를 안해줘서 킬각을 못잡았던 것이 있다.
결국 PrintBoard로 만들어 파악하고 성공했다.
이런 로직을 짜는 것이 처음이다보니 너무 빠르게 짜고 예외처리를 하지 않으려 해서 모든걸 null로 바꾸는 문제가 제일 오래 걸렸던 것 같다. 원래 한 번 틀리면 다시 봐도 안보이는게 맞다...
그런데 이제 아직 코드 정리도 하지 않았고, AIController를 static으로 만들고 싶다.
단순히 데이터만 받아서 계산하는 역할로 ! 그런데 Static으로 만들면 그 안에서 스태틱 메서드가 아닌 메서드, 즉 GameManager의 함수들을 사용하지 못한다. 그래서 GameManager에 있는 함수들을 옮겨서 윈체크도 다 하게 해야한다.
시간이 오래 걸릴 것 같으므로 일단 패스...
또 자꾸 AIController가 게임매니저를 못 찾는 문제가 있었는데, 계속해서 생성되며 길을 찾지 못한 것이었다.
오늘의 목표
1. 코테 목표지점까지 3문제
2. 만들어보고 싶은 게임 (간단한) 리스트 정렬하기
3. 멋사 강의 2강 듣기
'TIL' 카테고리의 다른 글
[멋쟁이사자처럼 부트캠프 TIL회고] 71일차 : 틱택토 빌드 (0) | 2025.02.11 |
---|---|
[멋쟁이사자처럼 부트캠프 TIL] 70일차 : 틱택토 자동로직 완성 (0) | 2025.02.07 |
[멋쟁이사자처럼 부트캠프TIL회고] 68일차 : 패널창 만들기 (0) | 2025.02.05 |
[멋쟁이사자처럼 부트캠프 TIL회고] 67일차 : 틱택토 (0) | 2025.02.04 |
[멋쟁이사자처럼 부트캠프 TIL회고] 66일차 : 연휴 끝 (1) | 2025.02.03 |