연결 구축4: 논리 추가

22014 단어 gamedevreactjavascript
이 시리즈의 두 번째 부분에서, 우리는 두 사용자가 로컬에서 게임을 할 수 있도록 하는 논리를 추가할 것이다.
만약 당신이 우리가 어떻게 이 프로젝트를 구축하고 디자인했는지 보고 싶다면, 반드시 첫 번째 부분을 찾아서 더욱 잘 이해하도록 해야 한다.

그리고 오늘 우리가 구축하고 있는 최종 버전click here을 놀고 싶다면 이렇게 하세요!

우리의 심지 모형을 발전시키다


우리가 코드를 작성하기 전에, 중요한 것은 게임 데이터가 파일에서 어떻게 이동하는지 이해하는 것이다.
다음은 몇 가지 중요한 개념으로 게이머들이 행동을 취하기 전에 기억해야 한다.
1) 게임보드는 6개의 내부 어레이로 구성된 어레이입니다.각 어레이는 Connect4 보드의 행을 나타냅니다.
board: [
    [null, null, null, null, null, null, null],
    [null, null, null, null, null, null, null],
    [null, null, null, null, null, null, null],
    [null, null, null, null, null, null, null],
    [null, null, null, null, null, null, null],
    [null, null, null, null, null, null, null],
  ]
2) 기본적으로 각 배열에는 7개null의 값이 있습니다.이것은 게이머들이 기호화폐를 배치할 수 있는 구역을 대표한다.우리는 빈 값 (0-6) 의 인덱스를 사용하여 열을 지정합니다.
// Connect4.js
// iterate over each array to create a Row

<tbody>
  {gameState.board.map((row, i) => (
     <Row key={i} row={row} play={play} />
   ))}
</tbody>
//Row.js
// row ex) [null, null, null, null, null, null, null, null]
// iterate over each column, cell= the value, i = column index
<tr>
 {row.map((cell, i) => (
   <Cell key={i} value={cell} columnIndex={i} play={play} />
 ))}
</tr>
3) 각 영패 구역(Cell마다 사건 탐지기를 연결한다.따라서 하나의 칸을 눌렀을 때, 우리는 그 칸의 열도 알고, 그 중의 값도 안다.그리고 Connect4->Row->Cell에서 전달된 play 함수를 호출할 수 있습니다.
//Row.js (inside Cell component)
onClick={() => {
  play(columnIndex)
}}

🤔 You might be wondering how we know what row a user clicked in if all a Cell knows is the column and its value (what if someone clicks the very top row when the game just started?) We'll see the logic later on, but because we're working in a grid, we'll just use a for-loop to check the bottom row and work our way up until we find the first available cell in that column.


우리의 논리에 가입하다


우리 프로젝트에서 이 역할은 정의해야 한다.유저가 어떤 칸을 눌렀을 때, 자동으로 호출되며, 어느 열에 영패를 설치해야 하는지만 알려집니다.
그럼 여기서부터 시작합시다.

새로운 이사회를 만들다

play 함수에 다음 행을 추가합니다.
let board = deepCloneBoard(gameState.board)
복습으로 JavaScript 기본 유형을 전달할 때 실제 값을 전달합니다.
let a = 2
let b = a
a = 3
console.log(b) // 2
그러나 객체 및 배열과 같은 복잡한 유형을 전달할 때는 원본 유형에 대한 참조를 전달합니다.
let arr1 = ['hi']
let arr2 = arr1
arr1.push('bye')
console.log(arr2) // ['hi', 'bye']
때때로 이런 행위는 바로 우리가 원하는 것이다...대다수 때는 그렇지 않다.따라서 이 값을 복제하고 값을 통해 전달하거나 인용을 통해 전달하는 것을 걱정하지 않기 위해 우리는 실용 프로그램 함수를 포장하여 우리의 회로판을 수신하고 안전하게 복사본을 되돌려받을 것이다.Connect4.js라는 새 파일을 만들고 다음 코드를 추가합니다.
//connect4/gameUtils.js
export const deepCloneBoard = (board) => [
  [...board[0]],
  [...board[1]],
  [...board[2]],
  [...board[3]],
  [...board[4]],
  [...board[5]],
]
여기에서, 우리는 줄 내부의 수조로 바로 되돌아갑니다. 우리는 spread-operator 주어진 색인에서 회로판의 값을 복사합니다.
이 함수played를 사용할 때는 다음 행을 추가하여 가져오는 것을 잊지 마십시오gameUtils.js.
import { deepCloneBoard } from '../gameUtils'

바둑판에 유저 추가


저희 export 파일로 돌아가면, 사용자가 지정한 열에 영패를 넣을 수 있는 사용자를 처리할 것입니다.나는 위에서 개념적으로 이것이 어떻게 작동하는지 설명했기 때문에 우리는 우리의 판을 깊이 복제한 후에 다음과 같은 코드를 추가했다.
//check if cell is taken by starting at the bottom row (5) and working up
//if a cell is null, add the current player (1 or 2) to it.
for (let r = 5; r >= 0; r--) {
  if (!board[r][c]) {
    board[r][c] = gameState.currentPlayer
    break
   }
}

🗒️ At this point, you should be able to run the app and click on a cell to place a red token from the bottom up...but it won't. That's because we are never updating our state to reflect the added player.


다음 단계는 Connect4.js 함수와 Connect4.js 함수 사이에서 작업하여 플레이어가 영패를 놓을 때 프로그램을 업데이트할 수 있도록 합니다.

게임 상태 사용

gameReducer 파일의 위쪽에는 play 구성 요소의 행이 있습니다.
const [gameState, dispatchGameState] = useReducer(
 gameReducer,
 initialGameState
)
Connect4.js 함수는 react hook 함수로 두 개의 매개 변수를 받아들인다. 하나는 상태 업데이트 방식을 포함하는 함수이고 하나는 기본 상태를 정의하는 값(Connect4이다.
보답으로 우리는 두 필드를 포함하는 그룹을 얻었다. 현재 상태의 모습 (useReducer 과 업데이트 상태의 함수 initalGameState.
게임의 다양한 업데이트 방식을 설명하기 위해 우리의 감속기를 기입합시다.
const gameReducer = (state, action) => {
//1
  switch (action.type) {
//2
    case 'newGame':
      return {
        ...initialGameState,
        board: action.board,
      }
//3
    case 'togglePlayer':
      return {
        ...state,
        currentPlayer: action.nextPlayer,
        board: action.board,
      }
//4
    case 'endGame':
      return {
        ...state,
        gameOver: true,
        message: action.message,
        board: action.board,
      }
//5
    case 'updateMessage':
      return {
        ...state,
        message: action.message,
      }
//6
    default:
      throw Error(`Action "${action.type}" is not a valid action.`)
  }
}
AgameState는 하나의 개념으로 그 중의 한 함수는 여러 가지 다른 값을 받아들일 수 있고 이 값이 무엇인지에 따라 새로운 데이터 세그먼트를 되돌려준다.
더욱 구체적으로 말하면 다음과 같다.
  • 우리의 게임 감속기는 하나의 동작을 실행할 것이다(이것은 대상이다). 우리는 그것의 dispatchGameState 값을 사용하여 무엇을 해야 하는지를 확정할 것이다.
  • 새 게임: 초기 상태가 정의한 새 게임을 되돌려줍니다.
  • 유저 전환: 현재 게임 상태에 있는 모든 값을 반환하고 바둑판과 현재 유저를 전송된 새로운 값으로 업데이트합니다.
  • 게임 종료: 상태의 모든 값을 되돌려주고reducertrue로 설정하여 메시지를 업데이트하고 바둑판을 업데이트합니다.
  • 업데이트 메시지: 메시지를 제외한 모든 것이 변하지 않는 간편한 방법입니다.
  • 만약 type값이 우리가 고려하지 않은 것이라면 오류를 던진다.
  • react의 장점은 우리가 구성 요소의 상태를 업데이트할 때마다 구성 요소는 자동으로 자신과 모든 하위 구성 요소를 다시 렌더링합니다.
    따라서, 우리 상태의 최신 버전은 항상 gameOver 변수에 있습니다.우리는 type를 사용하여 이 완전한 순환을 실현합시다.

    상태 업데이트


    저희gameState가 모든 상태 업데이트를 포함하는 방식과 같이 저희dispatchGameState 기능은 실제 업데이트를 책임질 것입니다.
    우리는 우선 유저들이 그들의 기호화폐를 놓고 번갈아 교체하는 것을 허락할 것이다.이를 위해 for 순환 후 다음 코드 업데이트gameReducer 함수를 사용합니다.
    const nextPlayer =
      gameState.currentPlayer === gameState.player1
        ? gameState.player2
        : gameState.player1
    
    dispatchGameState({ type: 'togglePlayer', nextPlayer, board })
    
    우리는 다음 유저가 누구인지 확인하기 위해 삼원 연산자를 사용하기 시작했다.그리고 우리는 play를 호출하여 게임을 업데이트하고 업데이트된 유저, 업데이트된 바둑판을 전송한다. 가장 중요한 것은 전송play이다.

    🗒️ Recall that the our gameReducer looks at the type value to determine how our state should update.


    게임을 저장하고 실행합니다.당신은 지금 교체 화폐를 배치할 수 있을 것입니다🎉

    경기를 끝내다


    우리의 게임은 곧 완성될 것입니다. 그러나 우리는 승리를 검사하고 유저에게 메시지를 표시하며 게임을 다시 시작하는 기능을 추가해야 합니다!
    다행히도 대부분의 작업이 끝났습니다. 우리는 관련 상태 업데이트만 추가하고 싶습니다.
    업데이트 게임의 논리적 업데이트 게임은 우리가 이미 완성한 것과 매우 비슷합니다. 최종 화면의 외관을 볼 수 있도록 리포에 연결할 것입니다.액세스하려면 click here
    업데이트 게임의 논리가 dispatchGameState 함수로 추상화되었음을 알 수 있습니다.
    이 함수는 순환에 대한 최적화일 뿐입니다. 저는 여기서 복사하거나 붙이지 않고 개념을 이해하는 데 전념하겠습니다.
    이 함수도 제 위에 링크된 리포에 있습니다. type 파일을 열고 this link 의 모든 코드를 붙여넣으십시오.checkForWin 함수와 gameUtils.js 함수를 모두 checkForWin 파일로 가져오는 것만 확인하면 됩니다.
    마지막으로 할 건 새로운 경기를 시작할 수 있는 능력이 있다는 거예요.😎
    이를 위해 GameUtils 파일의 generateNewBoard 함수를 사용해야 합니다.다음과 같이 새 게임 버튼을 업데이트합니다.
    <Button
     colorScheme="purple"
     className={gameStyles.button}
     onClick={() => {
      dispatchGameState({ type: 'newGame', board: generateNewBoard()})
     }}
    >
     New Game
    </Button>
    
    모든 파일이 저장되어 있는지 확인하십시오. 정상적인 Connect4 게임이 있어야 합니다🎉

    결론


    보시다시피 React에서 라운드 기반 게임을 만드는 것은 자바스크립트 개념과 React 프로젝트의 데이터 흐름 방식에 관한 것이 더 많습니다.
    나는 네가 나와 마찬가지로 이 점을 구축하는 데 있어서 매우 즐겁게 놀기를 바란다. 만약 네가 어떤 개선이나 갱신 건의가 있다면 반드시 나에게 알려줘야 한다.

    좋은 웹페이지 즐겨찾기