티스토리 뷰
728x90
테트리스
고전 게임인 테트리스를 Javascript를 사용하여 구현하였습니다.
주요 기능
- 시작 버튼
시작 버튼 클릭 시 테르리스 게임이 시작됩니다.
- 점수 계산
한 줄을 완성할 경우 스코어가 1점 오르고 해당 줄을 삭제합니다.
소스 코드
전체 소스는 https://github.com/wonseok22/ToyProjects/tree/main/tetrisGame 를 참고하세요.
const tetrisInner = document.querySelector(".tetris__inner > ul");
const tetrisS = document.querySelector(".tetris__wrap .s");
const game_rows = 20;
const game_cols = 10;
let gameScore = 0;
let gameDuration = 500;
let gameInterval;
let gameMovingItem;
const blocks = {
Tmino: [
[
[2, 1],
[0, 1],
[1, 0],
[1, 1],
],
[
[1, 2],
[0, 1],
[1, 0],
[1, 1],
],
[
[1, 2],
[0, 1],
[2, 1],
[1, 1],
],
[
[2, 1],
[1, 2],
[1, 0],
[1, 1],
],
],
Imino: [
[
[0, 0],
[1, 0],
[2, 0],
[3, 0],
],
[
[0, 0],
[0, 1],
[0, 2],
[0, 3],
],
[
[0, 0],
[1, 0],
[2, 0],
[3, 0],
],
[
[0, 0],
[0, 1],
[0, 2],
[0, 3],
],
],
Omino: [
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
],
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
],
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
],
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
],
],
Zmino: [
[
[0, 0],
[1, 0],
[1, 1],
[2, 1],
],
[
[1, 0],
[1, 1],
[0, 1],
[0, 2],
],
[
[0, 0],
[1, 0],
[1, 1],
[2, 1],
],
[
[1, 0],
[1, 1],
[0, 1],
[0, 2],
],
],
Smino: [
[
[0, 1],
[1, 1],
[1, 0],
[2, 0],
],
[
[1, 0],
[1, 1],
[2, 1],
[2, 2],
],
[
[0, 1],
[1, 1],
[1, 0],
[2, 0],
],
[
[1, 0],
[1, 1],
[2, 1],
[2, 2],
],
],
Jmino: [
[
[1, 0],
[1, 1],
[1, 2],
[0, 2],
],
[
[0, 1],
[0, 2],
[1, 2],
[2, 2],
],
[
[0, 2],
[1, 2],
[0, 3],
[0, 4],
],
[
[1, 2],
[1, 1],
[0, 1],
[-1, 1],
],
],
Lmino: [
[
[0, 0],
[0, 1],
[0, 2],
[1, 2],
],
[
[0, 2],
[0, 3],
[1, 2],
[2, 2],
],
[
[-1, 2],
[0, 2],
[0, 3],
[0, 4],
],
[
[0, 1],
[0, 2],
[-1, 2],
[-2, 2],
],
],
};
let isGame = false;
const movingItem = {
type: "Zmino",
direction: 0,
top: 0,
left: 3,
};
document.querySelector(".btn").addEventListener("click", function () {
if (!isGame) {
isGame = true;
init();
}
});
// 시작하기
function init() {
gameMovingItem = { ...movingItem };
//console.log(gameMovingItem)
for (let i = 0; i < game_rows; i++) {
tetrisLine();
}
//renderBlocks();
generateNewBlock();
}
// 블록 만들기
function tetrisLine() {
const li = document.createElement("li");
const ul = document.createElement("ul");
for (let j = 0; j < game_cols; j++) {
const block = document.createElement("li");
ul.prepend(block);
}
li.prepend(ul);
tetrisInner.prepend(li);
}
// 블록 출력하기
function renderBlocks(moveType = " ") {
const { type, direction, top, left } = gameMovingItem;
const movingBlocks = document.querySelectorAll(".moving");
movingBlocks.forEach((moving) => {
moving.classList.remove(type, "moving");
});
blocks[type][direction].some((block) => {
const x = block[0] + left;
const y = block[1] + top;
//console.log(tetrisInner)
const target = tetrisInner.childNodes[y]
? tetrisInner.childNodes[y].childNodes[0].childNodes[x]
: null;
const isAvailable = checkEmpty(target);
if (isAvailable) {
target.classList.add(type, "moving");
} else {
gameMovingItem = { ...movingItem };
if (moveType === "retry") {
clearInterval(gameInterval);
gameover();
}
setTimeout(() => {
renderBlocks("retry");
if (moveType === "top") {
seizeBlock();
}
}, 0);
return true;
}
});
movingItem.left = left;
movingItem.top = top;
movingItem.direction = direction;
}
function seizeBlock() {
const movingBlocks = document.querySelectorAll(".moving");
movingBlocks.forEach((moving) => {
moving.classList.remove("moving");
moving.classList.add("seized");
});
checkMatch();
}
//블록 한줄 제거하기
function checkMatch() {
const childNodes = tetrisInner.childNodes;
childNodes.forEach((child) => {
let matched = true;
child.children[0].childNodes.forEach((li) => {
if (!li.classList.contains("seized")) {
matched = false;
}
});
if (matched) {
child.remove();
tetrisLine();
gameScore++;
tetrisS.innerText = "Score : " + gameScore;
}
});
generateNewBlock();
}
//새로운 블록 만들기
function generateNewBlock() {
clearInterval(gameInterval);
gameInterval = setInterval(() => {
moveBlock("top", 1);
}, gameDuration);
const blockArray = Object.entries(blocks);
const randomIndex = Math.floor(Math.random() * blockArray.length);
movingItem.type = blockArray[randomIndex][0];
movingItem.top = 0;
movingItem.left = 3;
movingItem.direction = 0;
gameMovingItem = { ...movingItem };
renderBlocks();
}
function checkEmpty(target) {
if (!target || target.classList.contains("seized")) {
return false;
}
return true;
}
// 블록 움직이기
function moveBlock(moveType, amount) {
gameMovingItem[moveType] += amount;
renderBlocks(moveType);
}
// 모양 변경하기
function changeDirectioin() {
const direction = gameMovingItem.direction;
direction === 3
? (gameMovingItem.direction = 0)
: (gameMovingItem.direction += 1);
renderBlocks();
}
// 스페이스바 누르기
function dropBlock() {
clearInterval(gameInterval);
gameInterval = setInterval(() => {
moveBlock("top", 1);
}, 10);
}
function gameover() {
//게임 끝났을 때
isGame = false;
console.log("게임오버");
}
// 키보드 이벤트
document.addEventListener("keydown", (e) => {
//console.log(e)
switch (e.keyCode) {
case 39:
moveBlock("left", 1);
break;
case 37:
moveBlock("left", -1);
break;
case 40:
moveBlock("top", 1);
break;
case 38:
changeDirectioin();
break;
case 32:
dropBlock();
break;
default:
break;
}
});
320x100
댓글
© 2022 WonSeok, All rights reserved