연습 - 게임 논리

완료됨

이 연습에서는 게임 논리를 앱에 추가하여 게임이 완전히 작동하도록 합니다.

이 자습서에서 Blazor에 대한 교육을 진행할 수 있도록 게임 관리 논리가 포함된 GameState이라는 클래스를 제공합니다.

게임 상태 추가

GameState 클래스를 프로젝트에 추가한 다음, 종속성 주입을 통해 구성 요소를 싱글톤 서비스로 사용할 수 있도록 하겠습니다.

  1. GameState.cs 파일을 프로젝트의 루트에 복사합니다.

  2. 프로젝트의 루트에서 Program.cs 파일을 열고 앱에서 GameState 단일 서비스로 구성하는 이 문을 추가합니다.

    builder.Services.AddSingleton<GameState>();
    

    이제 GameState 클래스의 인스턴스를 Board 구성 요소에 삽입할 수 있습니다.

  3. Board.razor파일의 맨 위에 다음 @inject 지시문을 추가합니다. 지시문은 게임의 현재 상태를 구성 요소에 삽입합니다.

    @inject GameState State
    

    이제 Board 구성 요소를 게임 상태에 연결할 수 있습니다.

상태 다시 설정

먼저 Board 구성 요소가 화면에 처음 그려질 때 게임 상태를 다시 설정해 보겠습니다. 구성 요소가 초기화될 때 게임 상태를 다시 설정하는 코드를 추가합니다.

  1. 다음과 같이 Board.razor 파일 하단의 @code 블록 안에 ResetBoard 호출을 사용하여 OnInitialized 메서드를 추가합니다.

    @code {
        protected override void OnInitialized()
        {
            State.ResetBoard();
        }
    }
    

    보드가 사용자에게 처음 표시되면 상태가 게임 시작 부분으로 다시 설정됩니다.

게임 조각 만들기

다음으로, 플레이할 수 있는 42개의 게임 조각을 할당해 보겠습니다. 보드의 42개 HTML 요소에서 참조하는 배열로 게임 조각을 나타낼 수 있습니다. 열 및 행 위치가 있는 CSS 클래스 집합을 할당하여 이러한 조각을 이동하고 배치할 수 있습니다.

  1. 게임 조각을 보관하기 위해 코드 블록에 문자열 배열 필드를 정의합니다.

    private string[] pieces = new string[42];
    
  2. 동일한 구성 요소에서 각 게임 조각에 대해 하나씩 42개의 span 태그를 만드는 HTML 섹션에 코드를 추가합니다.

    @for (var i = 0; i < 42; i++)
    {
       <span class="@pieces[i]"></span>
    }
    

    전체 코드는 다음과 같아야 합니다.

    <div>
        <div class="board">
        @for (var i = 0; i < 42; i++)
        {
            <span class="container">
                <span></span>
            </span>
        }
        </div>
        @for (var i = 0; i < 42; i++)
        {
           <span class="@pieces[i]"></span>
        }
    </div>
    @code {
        private string[] pieces = new string[42];
    
        protected override void OnInitialized()
        {
            State.ResetBoard();
        }
    }
    

    그러면 각 게임 조각 범위의 CSS 클래스에 빈 문자열이 할당됩니다. CSS 클래스의 빈 문자열은 스타일이 적용되지 않는 게임 조각이 화면에 나타나지 않도록 합니다.

게임 조각 배치 처리

플레이어가 열에 조각을 배치할 때 처리할 메서드를 추가해 보겠습니다. GameState 클래스는 게임 조각에 올바른 행을 할당하는 방법을 알고 있으며, 해당 행이 놓인 행을 다시 보고합니다. 이 정보를 사용하여 플레이어의 색, 조각의 최종 위치 및 CSS 드롭 애니메이션을 나타내는 CSS 클래스를 할당할 수 있습니다.

PlayPiece 메서드를 호출하고 플레이어가 선택한 열을 지정하는 입력 매개 변수를 허용합니다.

  1. 이전 단계에서 정의한 pieces 배열 아래에 이 코드를 추가합니다.

    private void PlayPiece(byte col)
    {
        var player = State.PlayerTurn;
        var turn = State.CurrentTurn;
        var landingRow = State.PlayPiece(col);
        pieces[turn] = $"player{player} col{col} drop{landingRow}";
    }
    

PlayPiece 코드에서 수행하는 일은 다음과 같습니다.

  1. 제출한 열 col에서 조각을 움직이고 조각이 놓여진 행을 캡처하도록 게임 상태에 지시합니다.
  2. 그런 다음 게임 조각에 할당할 3개의 CSS 클래스를 정의하여 현재 어떤 플레이어가 조작하고 있는지, 조각이 배치된 열과 랜딩 행을 식별할 수 있습니다.
  3. 메서드의 마지막 줄은 이러한 클래스를 pieces 배열의 해당 게임 조각에 할당합니다.

제공된 Board.razor.css에서 열, 행 및 플레이어 턴과 일치하는 CSS 클래스를 찾을 수 있습니다.

그 결과 이 메서드가 호출되면 게임 조각이 열에 배치되고 맨 아래 행에 놓이도록 애니메이션됩니다.

열 선택

다음으로 플레이어가 열을 선택하고 새로운 PlayPiece 메서드를 호출할 수 있는 몇 가지 컨트롤을 배치해야 합니다. 이 열에 조각을 놓을 수 있음을 나타내기 위해 이 문자 "🔽"를 사용합니다.

  1. 시작 <div> 태그 위에 클릭 가능한 단추 행을 추가합니다.

    <nav>
        @for (byte i = 0; i < 7; i++)
        {
            var col = i;
            <span title="Click to play a piece" @onclick="() => PlayPiece(col)">🔽</span>
        }
    </nav>
    

    @onclick 특성은 클릭 이벤트에 대한 이벤트 처리기를 지정합니다. 그러나 UI 이벤트를 처리하려면 대화형 렌더링 모드를 사용하여 Blazor 구성 요소를 렌더링해야 합니다. 기본적으로 Blazor 구성 요소는 서버에서 정적으로 렌더링됩니다. @rendermode 특성을 사용하여 구성 요소에 대화형 렌더링 모드를 적용할 수 있습니다.

  2. InteractiveServer 렌더링 모드를 사용할 수 있도록 Home 페이지에서 Board 구성 요소를 업데이트합니다.

    <Board @rendermode="InteractiveServer" />
    

    InteractiveServer 렌더링 모드는 브라우저와의 WebSocket 연결을 통해 서버의 구성 요소에 대한 UI 이벤트를 처리합니다.

  3. 이러한 변경 내용으로 앱을 실행합니다. 이제 다음과 같이 표시됩니다.

    Connect Four 보드의 스크린샷.

    상단에 있는 드롭 단추 중 하나를 선택하면 다음과 같은 동작이 관찰됩니다.

    Connect Four 애니메이션의 스크린샷.

잘하셨습니다. 이제 보드에 조각을 추가할 수 있습니다. GameState 개체는 두 플레이어 간에 앞뒤로 피벗팅할 수 있을 만큼 지능적입니다. 더 많은 드롭 버튼을 선택하고 결과를 확인하세요.

승리 및 오류 처리

현재 구성에서 게임을 플레이하면 동일한 열에 너무 많은 조각을 넣으려고 할 때와 한 플레이어가 게임에서 이길 때 오류가 발생합니다.

보드에 몇 가지 오류 처리 및 지표를 추가하여 게임의 현재 상태를 명확하게 만들어 보겠습니다. 보드 상단과 드롭 단추 아래에 상태 영역을 추가합니다.

  1. nav 요소 뒤에 다음 태그를 삽입합니다.

    <article>
        @winnerMessage  <button style="@ResetStyle" @onclick="ResetGame">Reset the game</button>
        <br />
        <span class="alert-danger">@errorMessage</span>
        <span class="alert-info">@CurrentTurn</span>
    </article>
    

    이 태그를 사용하면 다음의 표시기를 표시할 수 있습니다.

    • 게임 우승자 발표
    • 게임을 다시 시작할 수 있는 단추
    • 오류 메시지
    • 현재 플레이어의 차례

    이제 이러한 값을 설정하는 몇 가지 논리를 입력해 보겠습니다.

  2. 조각 배열 다음에 다음 코드를 추가합니다.

    private string[] pieces = new string[42];
    private string winnerMessage = string.Empty;
    private string errorMessage = string.Empty;
    
    private string CurrentTurn => (winnerMessage == string.Empty) ? $"Player {State.PlayerTurn}'s Turn" : "";
    private string ResetStyle => (winnerMessage == string.Empty) ? "display: none;" : "";
    
    • CurrentTurn 속성은 winnerMessage의 상태 및 GameStatePlayerTurn 속성을 기반으로 자동으로 계산됩니다.
    • ResetStyleWinnerMessage의 콘텐츠를 기반으로 계산됩니다. winnerMessage가 있는 경우 다시 설정 단추가 화면에 나타납니다.
  3. 조각을 플레이할 때 오류 메시지를 처리해 보겠습니다. 선을 추가하여 오류 메시지를 지우고 PlayPiece 메서드의 코드를 try...catch 블록으로 래핑하여 예외가 발생한 경우 errorMessage를 설정합니다.

    errorMessage = string.Empty;
    try
    {
        var player = State.PlayerTurn;
        var turn = State.CurrentTurn;
        var landingRow = State.PlayPiece(col);
        pieces[turn] = $"player{player} col{col} drop{landingRow}";
    }
    catch (ArgumentException ex)
    {
        errorMessage = ex.Message;
    }
    

    오류 처리기 표시기는 간단하며 부트스트랩 CSS 프레임워크를 사용하여 위험 모드에서 오류를 표시합니다.

    보드와 조각이 있는 현재 게임 스크린샷.

  4. 다음으로, 게임을 다시 시작하기 위해 단추가 트리거하는 ResetGame 메서드를 추가해 보겠습니다. 현재 게임을 다시 시작하는 유일한 방법은 페이지를 새로 고치는 것입니다. 이 코드를 사용하면 동일한 페이지를 유지할 수 있습니다.

    void ResetGame()
    {
        State.ResetBoard();
        winnerMessage = string.Empty;
        errorMessage = string.Empty;
        pieces = new string[42];
    }
    

    이제 ResetGame 메서드에는 다음과 같은 논리가 있습니다.

    • 보드의 상태를 다시 설정합니다.
    • 지표를 숨깁니다.
    • 조각 배열을 42개 문자열의 빈 배열로 다시 설정합니다.

    이 업데이트를 통해 게임을 다시 플레이할 수 있으며, 이제 보드 바로 위에 플레이어의 차례와 최종적으로 게임이 완료되었음을 알리는 표시기가 표시됩니다.

    게임 완료를 표시하는 스크린샷.

    여전히 다시 설정 버튼을 선택할 수 없는 상황이 있습니다. PlayPiece 메서드에 몇 가지 논리를 추가하여 게임의 종료를 확인해 보겠습니다.

  5. PlayPiecetry...catch 블록 뒤에 스위치 식을 추가하여 게임에 우승자가 있는지 확인합니다.

    winnerMessage = State.CheckForWin() switch
    {
        GameState.WinState.Player1_Wins => "Player 1 Wins!",
        GameState.WinState.Player2_Wins => "Player 2 Wins!",
        GameState.WinState.Tie => "It's a tie!",
        _ => ""
    };
    

    CheckForWin 메서드는 어떤 플레이어가 게임에서 이겼는지 또는 게임이 동률인지를 보고하는 열거형을 반환합니다. 이 스위치 식은 게임 오버 상태가 발생하는 경우 winnerMessage 필드를 적절하게 설정합니다.

    이제 게임을 플레이하고 게임 종료 시나리오에 도달하면 다음 지표가 표시됩니다.

    게임 다시 설정을 표시하는 스크린샷.

요약

우리는 Blazor에 대해 많은 것을 배웠고 작은 게임을 만들었습니다. 학습한 몇 가지 기술은 다음과 같습니다.

  • 구성 요소 만들기
  • 해당 구성 요소를 홈페이지에 추가하기
  • 종속성 주입을 사용하여 게임 상태 관리하기
  • 게임을 이벤트 처리기와 상호 작용하여 조각을 배치하고 게임 다시 설정하기
  • 게임 상태를 보고하는 오류 처리기 작성하기
  • 구성 요소에 매개 변수 추가하기

우리가 만든 프로젝트는 단순한 게임이지만 여러분이 할 수 있는 일은 훨씬 더 많습니다. 개선 방법에 대한 문제를 찾고 계신가요?

과제

다음 문제를 고려해 보세요.

  • 앱을 더 작게 만들려면 기본 레이아웃 및 추가 페이지를 제거합니다.
  • 유효한 CSS 색상 값을 전달할 수 있도록 매개 변수를 Board 구성 요소로 개선합니다.
  • 일부 CSS 및 HTML 레이아웃을 사용하여 표시기 모양을 개선합니다.
  • 음향 효과를 도입합니다.
  • 시각적 표시기를 추가하고 열이 가득 찼을 때 놓기 단추가 사용되지 않도록 방지합니다.
  • 브라우저에서 친구를 재생할 수 있도록 네트워킹 기능을 추가합니다.
  • Blazor 애플리케이션을 사용하여 .NET MAUI에 게임을 삽입하고 휴대폰 또는 태블릿에서 플레이합니다.

즐겁게 코딩하고 행복한 시간 보내세요!