본문 바로가기

Toy Project/react

Tetris 개발일지 (day 4)

1. userBlock shilhouete 그리기

 

블럭 shilhouete을 그리기 전 현재 위치에서 블럭이 얼마나 떨어질수 있는지를 구해야 한다.

 

1. 위와같이 현재 블럭의 y 좌표값을 기준으로 각각 얼마나 떨어질수 있는지 구하고 

2. 떨어질수 있는 범위중 제일 낮은 값이 현재 블럭이 떨어질수 있는 값이다.

 

 getblockDropPos(pos: [number, number][], background: string, stage: ModelStage): {pos: [number, number][], background: string} {
    //  key is rowIndex, value is column index
    let toCheckPos: { [key: number]: number } = {}

    for (const posItem of pos) {
      if (!toCheckPos[posItem[0]]) {
        toCheckPos[posItem[0]] = posItem[1]
      } else {
        toCheckPos[posItem[0]] = toCheckPos[posItem[0]] > posItem[1] ? toCheckPos[posItem[0]] : posItem[1]
      }
    }

    let minCanMoveYPos = stage[0].length
    for (const rowIndex in toCheckPos) {
      let canMoveYPos = 0

      const nowRow = stage[parseInt(rowIndex)]
      for (let y = toCheckPos[rowIndex] + 1; y < nowRow.length; y++) {
        if (!nowRow[y].settle) canMoveYPos++
        else break
      }

      minCanMoveYPos = Math.min(minCanMoveYPos, canMoveYPos)
    }

    return {
      pos: pos.map(posItem => [posItem[0], posItem[1] + minCanMoveYPos]),
      // 약간의 opacity를 주기위해 hex 형식으로 지정된 background에 2음절의 접미사를 붙인다
      background: background + 'a3'
    }
  }
  

 

 

 

또한 pastDown이라는 블럭을 즉각적으로 떨어트린다음 settle하는 기능도 추가할거기 때문에

userBlock state에 새로운 key를 추가해준다

 

interface ModelUserBlockStage extends ModelUserBlock {
  pos: [number, number][];
  pastDownPos: [number, number][];
}

 

 

shiluete을 그릴때 다음과 같은 상황에 추가해 주고 빼줘야 한다

 

추가

  1. 블럭이 처음 그려졌을때 && 블럭이 settle되서 새로운 블럭을 그려줬을때
  2. 블럭이 움직일때 (여기서 y축의 움직임은 dropPos가 똑같으므로 배제한다)
  3. 블럭이 rotate 됬을때

제거

  1. 블럭이 rotate 될수 있을때 그 전 userBlock shiluete
  2. 블럭이 x로 움직였을때 그 전 userBlock shilhouete
  3. 블럭이 settle되기 전 userBlock shiluete

 

주의하면서 추가해주자

 

 

2. 변경사항

 

gameOver 되는 경우의 logic 변경

이전과 같은 logic에선 다음과같은 상황에서 해당 블럭이 다음에 생성될떄

바로 gameOver처리가 되버린댜. 따라서 block값을 기준으로 좌, 우로 움직여보고 그래도 안될경우 gameOver 처리를 하자

 

blockSettle () {
  ...
  ...
  
    // if can't settle gameOver
    if (!this.canSettle(newBlockPos)) {
      const tryBlockMove = (xPos: number): boolean => {
        const tryMoveUserBlock = {...newUserBlock}
        const xMover = xPos - tryMoveUserBlock.standardPos[0]
        tryMoveUserBlock.standardPos = [xPos, tryMoveUserBlock.standardPos[1]]
        tryMoveUserBlock.pos = tryMoveUserBlock.pos.map(posItem => [posItem[0] + xMover, posItem[1]])
  
        if (this.canSettle(tryMoveUserBlock.pos)) {
          tryMoveUserBlock.pastDownPos = this.getblockDropPos(tryMoveUserBlock.pos, tryMoveUserBlock.background, breakRowsStage).pos
          newUserBlock = tryMoveUserBlock
          doGrid()

          return true
        } else {
          return false
        }
      }
      // try x move right
      for (let xPos = newUserBlock.standardPos[0] + 1; xPos < this.state.stage.length; xPos++) {
        if (tryBlockMove(xPos)) {
          return false
        }
      }
      // try x move left
      for (let xPos = newUserBlock.standardPos[0] - 1; xPos > -1; xPos--) {
        if (tryBlockMove(xPos)) {
          return false
        }
      }

      this.props.gameOverEvent()
    } else {
      doGrid()
    }
    /*
      set new userBlock [end]
    */
}

 

 

여기까지의 commit

 

 

 

 

2. gameOver & reStart 기능 추가

 

gameStatus는 Tetris에서 내려주는 prop이다 따라서 gameStatus와 따라 변경되는 Stage는 eventHandler 밖에 없으므로

componentDidUpdate를 사용한다.

 

componentDidUpdate (prevProps: StageProps) {
    if (prevProps.gameStatus === 'gameOver' && this.props.gameStatus === 'play') {
      const clearStage: ModelStage = this.state.stage.map(row => row.map(() => ({ background: '', settle: false })))

      const userBlockShape = getBlock()
      const block: ModelUserBlock = {
        ...userBlockShape,
        standardPos: [Math.floor((this.state.stage.length - userBlockShape.shape.length) / 2), 0]
      }
      const blockPos = this.shapeBlockPosPareser(block)
      const dropObj = this.getblockDropPos(blockPos, block.background, clearStage)

      const newUserBlock: ModelUserBlockStage = {
        ...block,
        pos: blockPos,
        pastDownPos: dropObj.pos
      }

      const stageAddUserBlock = this.getStageConcatBlock(blockPos, block.background, false, clearStage)

      const stageAddShilhouete = this.getStageConcatBlock(dropObj.pos, dropObj.background, false, stageAddUserBlock)
      this.setState({
        stage: stageAddShilhouete,
        predictionBlocks: this.state.predictionBlocks.map(() => getBlock()),
        userBlock: newUserBlock
      })
    }
  }

 

 

여기까지의 전체적인 그림은 다음과 같다.

 

 

 

여기까지의 commit

 

 

이제 테트리스는 완료되었다. 다음은 nodejs를 통한 서버 설정 및 데이터 저장이다.

 

 

( 결과물은 심심풀이... 버튼 눌러보면 있음. 잘 안되면 cache 초기화하면 됨 )

반응형

'Toy Project > react' 카테고리의 다른 글

동시성모드, selectable hydrate 관련 좋은 영상  (0) 2021.07.03
Tetris 개발일지 (day 3)  (0) 2020.05.26
Tetris 개발일지 (day 2)  (0) 2020.05.25
TETRIS 개발일지 (day1)  (0) 2020.05.22
TETRIS 개발일지 (prologue)  (0) 2020.05.22