Vanilla javascript 로 할만한 간단한 프로젝트가 뭐가 있을까 고민을 하다가 유튜브 강의 중에 바닐라 자바스크립트로 테트리스 만들기가 있어서 한 번 따라 만들어 봤다.
기본적인 테트리스 게임의 기능만 구현했다.
(시간이 된다면 CSS 및 다른 기능들을 추가할 예정이다)
게임을 시작했을 때와 블록이 다 내려왔을 때, 새로운 블록을 랜덤으로 생성한다. 생성된 블록은 일정한 속도로 내려온다.
(점수가 높아질수록 속도 추가예정 ..)
한 줄이 블록으로 모두 차게 되면, 해당 라인을 제거하고 최상단에 새로운 라인을 추가한다.
라인을 제거하면 점수를 추가한다.
(한 번에 많이 제거하면 점수 ++ 예정)
주요 코드
function prependNewLine(){
const li = document.createElement("li");
const ul = document.createElement("ul");
for (let j = 0; j < GAME_COLS; j++) {
const matrix = document.createElement("li");
ul.prepend(matrix);
}
li.prepend(ul);
playground.prepend(li);
}
function renderBlocks(moveType=""){
//변수에 바로 접근할 수 있도록 선언.
const { type, direction, top, left } = tmpMovingItem;
const movingBlocks = document.querySelectorAll(".moving");
movingBlocks.forEach((moving) =>{
moving.classList.remove(type, "moving");
})
// forEach 대신 some 사용 > 원하는 시점에 멈출 수 있게
BLOCKS[type][direction].some(block=>{
const x = block[0] + left;
const y = block[1] + top;
const target = playground.childNodes[y] ? playground.childNodes[y].childNodes[0].childNodes[x] : null;
const isAvailable = checkEmpty(target);
if (isAvailable) {
target.classList.add(type, "moving");
} else {
tmpMovingItem = { ... movingItem }
if(moveType === 'retry'){
clearInterval(downInterval);
showGameOverText();
}
//콜스택 에러방지
setTimeout(()=>{
renderBlocks('retry');
if(moveType === "top"){
seizeBlock();
}
}, 0)
return true;
}
})
movingItem.left = left;
movingItem.top = top;
movingItem.direction = direction;
}
주요 코드
document.addEventListener("keydown", (e)=>{
switch (e.keyCode){
case 39:
moveBlock("left", 1);
break;
case 37:
moveBlock("left", -1);
break;
case 40:
moveBlock("top", 1);
break;
case 38:
chageDirection();
break;
case 32:
dropBlock();
break;
default :
break;
}
})
function init(){
// 값만 따오는 것이기 때문에 tmpMovingItem 은 변경 x >> 값을 복사
tmpMovingItem = { ...movingItem };
for (let i = 0; i < GAME_ROWS; i++) {
prependNewLine();
}
generateNewBlock();
}
반복문을 사용할 때 상황에 따라 some()
이나 every()
를 사용해보자 !
forEach
를 쓰게 되면 반복문을 중단할 수 없다.
some()
성능을 위해 조건을 만족하는 값이 발견되면 그 즉시 순회가 중단된다 (Return True)
사용 예시
var testArr = [1, 2, 3, 4, 5]
testArr.some(el => {
if(el === 2) { return true }
console.log(el)
})
// 출력
> 1
--------------
var testArr = [1, 2, 3, 4, 5]
testArr.some(el => {
if(el === 2) { return false }
console.log(el)
})
// 출력
> 1
> 3
> 4
> 5
every()
모든 원소가 조건을 만족하면true
, 하나라도 만족하지 않으면false
를 반환
사용 예시
var testArr = [1, 2, 3, 4, 5]
testArr.every(el => el > 3); // false
testArr.every(el => el >= 1); // true
wrapper는 단일 요소를 감싸는 경우 사용한다 !
단일 요소를 감싸는
div
인 경우wrapper
를,
여러 요소를 감싸는 경우container
를 사용한다고 한다.
사용 예시
<ul class="items-container">
<li class="item-wrapper">
<div class="item">...</div>
</li>
<li class="item-wrapper">
<div class="item">...</div>
</li>
<li class="item-wrapper">
<div class="item">...</div>
</li>
</ul>
객체를 복사할 때 얕은 복사를 조심하자 !
movingItem
은 이전의 상태를 불러오기 위해 사용하는 객체이므로 spread 연산자를 활용하여 프로퍼티를 깊은 복사했다.
const tempMovingItem = { ...movingItem }
이와 같이 객체를 복사하면 얕은 복사가 되기 때문에tempMovingItem
의 값을 변경하게 되면 원본인movingItem
도 같이 변하게 된다.
얕은 복사란 객체를 복사할 때 기존 값과 복사된 값이 같은 참조를 가리키고 있는 것을 말한다.
객체 안에 객체가 있을 경우 한 개의 객체라도 기존 변수의 객체를 참조하고 있다면 이를 얕은 복사라고 한다.
사용 예시
const obj = { a: 1 };
const copyObj = obj;
copyObj.a = 2;
console.log(obj.a); // 2
console.log(obj === copyObj); // true
깊은 복사된 객체는 객체 안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 객체를 말한다.
사용 예시
const a = 1;
const b = a;
b = 2;
console.log(a); // 1
console.log(b); // 2
console.log(a === b); // false