javascript - Tetris

김동하·2020년 10월 24일
post-thumbnail

Tetris

나중에 테트리스를 아주 재밌게 흥미진진하게 만들 날이 오겠지? 열심히 최선을 다한다면 그런 날이 오리라 믿는다!

로직

화면은 3개의 부분이 있는데 다음 나올 블럭을 보여주는 파트, 블럭이 떨어지는 파트, 그리고 마지막 줄에 닿으면 블럭이 멈추는 파트다. html에 미리 div을 준다. width, height를 정한다. 블럭의 모양을 하드코딩으로 정해놓고 랜덤 함수로 결정하고 떨어지는 건 setInterval. rotate는 for를 돌려서 i를 바꿔서 모양을 바꾸면 될 듯!? 바닥에 닿으면 그 모양을 유지하게 하는 건 clear interval을 하고 지금 블럭의 index를 찾아서 class list에 추가..? 그리고 한 줄을 다 채우면 for문(블럭이 땅에 닿을 때마다 전체를 돌아야 함)으로 일치하는 한 줄을 삭제하고 width +1 만큼 내리면 된다.

시작

DOMLoaded 필수!(안 해도 되는 것도 있던데 이유를 알아보장)

출처 : 나의 교수님 위키피디아

블럭들을 하드코딩으로 만든다. 모양은 상상해야 한다. 각 배열의 0번째를 기준으로 로테이트한 모양을 나타낸다. 그리고 4개의 배열을 하나의 배열에 담는다. 이제 먼저 그리자. 랜덤하게 뽑아햐 하니까 랜덤 숫자 정하고 현재 어떤 rotation인지 알아야 하니까 current rotation 변수를 만들어 0을 준다. 이제 current는 전체 배열[랜덤넘버][current rotation]이다. draw 함수를 만든다. 테트리스 블록 말고 전체 squares를 담을 배열도 필요하니까 전역에 squares를 만들고 모든 div를 가져온다.

이제 선택된 블럭들에게 draw 함수에서 class를 add한다. squares에 currentPos+ 블럭의 위치값(인덱스)를 더한 인덱스를 forEach로 준다.

이벤트 리스너

control를 만들어 늘 그렇듯이 right,left,down 그리고 rotate 함수를 준다. 일단 startBtn에 이벤트 리스너를 걸어서 interval을 하자 늘 그렇듯이 timer id에 인터벌을 주고 movedown함수를 호출. 이제 다시 move down으로 가자.

move down 로직

블록이 아래로 내려가려면 다 지워지고 current pos에 += width 한 만큼 다시 생겨나야 한다. 일단 undraw함수가 필요하고 그리고 draw를 다시 해주면 된다. undraw 함수는 그냥 remove class다.

블럭이 아래로 떨어진다. 단연컨대 지금 이 순간 떨어지는 것 중 가장 아름다울 것이다.

오른쪽 왼쪾

재빠르게 right, left를 혼내주러 간다. 로직은 비슷하다 먼저 undraw를 실행하고 인덱스 이동후 다시 그려준다. 오른쪽의 경우 오른쪽 벽를 if로 지정해서 거기를 limit으로 잡으면 된다.

edge 로직

나의 멍청하고 얄팍하기 그지없는 머리로 생각하기에 왼쪽 벽에 닿을 때 인덱스를 구해야하니까 for 돌려서 왼쪽 벽 index - 블록들의 인덱스 중 0이 있으면 이라고 생각했는데 some으로 해결하면 된다. 이래서 코딩을 사랑하지 않을 수 없어.. 만약 edge가 아니라면 왼쪽 오른쪽에 맞는 인덱스를 준다.

rotate

rotate를 하려면 일단 변수로 만들었던 currentRotation을 더해주면 된다. undraw하고 currentRotation을 더하고 current를 다시 할당한다. currentRotation이 블록 배열 length와 같아지면 0으로 다시 초기화시킨다.

바닥에 닿았을 때

html에서 마지막 줄 row에 다른 class을 정해둔다. 블록이 하나라도 바닥에 닿으면 class가 바뀌고 멈추는 것이다. freeze 함수를 만든다. some으로 current를 돌아서 두 경우 체크하고 해당되면 정해둔 class를 추가한다.

바닥 로직

처음 블록만 바닥이고 그 다음부터는 이미 쌓인 블록 위에 쌓인다. 그래서 2가지 경우가 필요하다. 바닥인 경우, 다른 블록인 경우.

다음 블록

바닥에 닿아서 변하는 순간 다음 블록이 나와야 한다. draw를 다시 호출하면 이미 freeze된 block이 타겟이 되니까(왜냐면 current변수는 전역에 있으니까. 이것은 늘 그렇듯이 추측) 함수 안에서 다시 draw를 풀어서 작성해야 한다. draw에 필요한 nextRandom과 cuurent, currentPos를 다시 쓰고 draw를 호출한다.

차곡차곡 쌓인다.

미리보기

다음 나올 블록을 미리 보여줘야 한다. html에서 이미 div를 만들어 놓는다. display()를 만든다. 사실 여기서부터는 나의 영역을 벗어났으니 그냥 마음 편히 감상을 하면 된다. smallTetrominoes라는 배열을 만든다. 그 배열 안에는 보여줄 블록의 인덱스를 담은 배열들이 있다.

이 배열에 아까 변경했던 nextRandom을 이용해 해당 배열을 forEach한다. displaySquares라는 변수로 html div들을 가져오고 draw()처럼 display index+ index로 블록들에 class를 추가한다. displayShape()이 호출되는 시점은 첫 번째 블록이 만들어지는 시점이다. timerId로 movedown을 호출하고 next random 설정해서 전역 변수에 재할당하고 displayShape()호출한다. 그리고 freeze에서 draw가 끝난 다음에 호출하면 된다. display가 호출될 때 remove하지 않으면 중복된다.

row가 찼을 때

가장 어려운 부분이라고 생각한다. 나는 블록이 movedown이 호출될 때마다 for문을 다 돌려서 한 줄이 다 같은 class면 remove하고 그 위에 index 변경하는 줄 알았는데 더 쉬운 방법이 있다.

일단 for문에 currentIndex를 0으로 잡는다. 그리고 끝까지 돈다. width만큼 증가한다. 이제 current index기준으로 한 row를 정하고 배열에 담는다. row 배열에 every로 모든 squaes를 검사한다. 그리고 every 메서드 안에서 또 row를 forEach로 검사해서 class를 다 제거한다. 이제 위에 남은 블럭이 떨어지면 된다. removed 라는 변수를 만들고 squares를 splice하는데 currentIndex에서 width만큼 잘라온다. 그리고 squares를 concat해 다시 squares에 할당한다. 그리고 squares에 forEach해서 element를 다시 grid에 매단다. 이 마지막 3줄의 코드가 이해가 안 간다.

내일의 나는 오늘의 나보다 더 나아졌길

profile
프론트엔드 개발

0개의 댓글