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을 그릴때 다음과 같은 상황에 추가해 주고 빼줘야 한다
추가
- 블럭이 처음 그려졌을때 && 블럭이 settle되서 새로운 블럭을 그려줬을때
- 블럭이 움직일때 (여기서 y축의 움직임은 dropPos가 똑같으므로 배제한다)
- 블럭이 rotate 됬을때
제거
- 블럭이 rotate 될수 있을때 그 전 userBlock shiluete
- 블럭이 x로 움직였을때 그 전 userBlock shilhouete
- 블럭이 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]
*/
}
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
})
}
}
여기까지의 전체적인 그림은 다음과 같다.

이제 테트리스는 완료되었다. 다음은 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 |