안녕하세요, 김선진입니다.
직소 퍼즐 마지막 시리즈입니다. 직소 퍼즐을 게임으로 만들자!
1. 퍼즐 조각이 맞춰졌을 때 성공 여부 검사
2. 퍼즐 맞추기 타이머 생성
3. CSS (알아서 하시길)
기존 코드에 이어서 작업합니다.
1. 퍼즐 성공 여부 검사
1-1. td에 index부여하기
저번 코드에서 전역으로 선언해놓은 퍼즐 조각 리스트를 이용하여 퍼즐 성공을 검사하는 로직을 짜보자.
간단하게 4*4 퍼즐을 1중 배열이라 생각하고 인덱싱해서 결과물 배열을 만들고, 초기 리스트와 결과물이 같은 경우 성공 판정을 한다.
먼저 td를 생성할 때 해당 index를 id로 설정한다.
tableData.id = `piece_${x * numColsToCut + y}`;
1-2. 결과 배열과 기존 배열을 비교하여 성공 판정
성공 판정은 간단하게 구현했다. 배열 두 개를 받아 두 배열이 같은지만 검사한다.
const completePuzzle = (puzzle, result) => puzzle.every((piece, index) =>piece === result[index]);
1-3. drop함수 수정
const index = ev.target.id.replace('piece_', '');
resultPieces[Number(index)] = data;
if (completePuzzle(originImagePieces, resultPieces)) window.alert('퍼즐 성공!');
기존 코드 아래에 추가해준다.
이걸로 간단하게 퍼즐을 검증해서 성공 시에는 알람을 준다.
인덱스 대신 id를 긁었다 말았다 하는 이유는 elements만 보고 퍼즐을 맞출 수 없게 하기 위함이다.
그리고, 다시 시작하는 함수에 아래 코드도 추가해준다.
while(resultPieces.length > 0) {
resultPieces.pop();
}
2. 퍼즐 맞추기 타이머
2-1. setInterval로 타이머 생성하기
const maxTime = 30;
let nowTime = 0;
let timer;
function startTimer() {
timer = window.setInterval(() => {
if(nowTime === maxTime) {
window.clearInterval(timer);
suffleRendering();
window.alert('시간 종료!');
}
nowTime += 1;
}, 1000);
}
startTimer라는 함수를 만들어 우리의 maxTime까지 기다려주게 한다.
게임을 시작함과 동시에 timer가 세팅되기 때문에 게임이 종료된 후에는 suffle을 한번 더 돌려줘야 한다.
그리고 처음 퍼즐을 맞추기 시작했을 때 다시 timer를 시작해주면 된다.
if (resultPieces.length === 0) {
startTimer();
}
(아니면 게임 시작, 다시하기 등의 버튼을 제공해줘도 좋다.)
3. 전체 js 코드
전체 변경된 코드는 길어서 접었다.
const numColsToCut = 4;
const numRowsToCut = 4;
const imagePieces = [];
const originImagePieces = [];
const resultPieces = new Array(numColsToCut*numRowsToCut);
function suffleList(array) {
for (let i = array.length - 1; i > 0; i -= 1) {
let j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
function suffleRendering() {
const box = document.getElementById('puzzle-box');
suffleList(imagePieces);
while(resultPieces.length > 0) {
resultPieces.pop();
}
imagePieces.forEach(img => {
box.appendChild(img);
});
}
const maxTime = 30;
let nowTime = 0;
let timer;
function startTimer() {
timer = window.setInterval(() => {
if(nowTime === maxTime) {
window.clearInterval(timer);
suffleRendering();
window.alert('시간 종료!');
}
nowTime += 1;
}, 1000);
}
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
const completePuzzle = (puzzle, result) => puzzle.every((piece, index) =>piece === result[index]);
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
const child = document.getElementById(data);
if (!data) return;
if (resultPieces.length === 0) {
startTimer();
}
if (ev.target.nodeName !== 'TD') {
console.log(ev.target.parentNode);
const currentImage = ev.target;
const td = ev.target.parentNode;
td.removeChild(currentImage);
td.appendChild(child)
const box = document.getElementById('puzzle-box');
box.appendChild(currentImage);
return;
}
ev.target.appendChild(document.getElementById(data));
const index = ev.target.id.replace('piece_', '');
resultPieces[Number(index)] = data;
if (completePuzzle(originImagePieces, resultPieces)) window.alert('퍼즐 성공!');
}
function updateImageDisplay() {
const preview = document.querySelector('.preview');
const input = document.querySelector('input');
const board = document.getElementById('puzzle-board');
while(preview.firstChild) {
preview.removeChild(preview.firstChild);
}
const curFiles = input.files;
for(const file of curFiles) {
const para = document.createElement('p');
const imageItem = document.createElement('div');
const img = new Image();
img.onload = function() {
const widthOfOnePiece = this.width/numColsToCut;
const heightOfOnePiece = this.height/numRowsToCut;
para.innerHTML = `${numColsToCut}X${numRowsToCut}로 생성된 퍼즐입니다.`;
while(board.firstChild) {
board.removeChild(board.firstChild);
}
for(var x = 0; x < numColsToCut; ++x) {
let tableRow = document.createElement('tr');
for(var y = 0; y < numRowsToCut; ++y) {
var canvas = document.createElement('canvas');
canvas.width = widthOfOnePiece;
canvas.height = heightOfOnePiece;
var context = canvas.getContext('2d');
context.drawImage(img, y * widthOfOnePiece, x * heightOfOnePiece, widthOfOnePiece, heightOfOnePiece, 0, 0, canvas.width, canvas.height);
let pieceImage = new Image();
pieceImage.src = canvas.toDataURL();
pieceImage.id = canvas.toDataURL();
pieceImage.draggable = true;
pieceImage.ondragstart = drag;
let tableData = document.createElement('td');
tableData.ondrop = drop;
tableData.ondragover = allowDrop;
tableData.width = widthOfOnePiece;
tableData.height = heightOfOnePiece;
tableData.id = `piece_${x * numColsToCut + y}`;
tableRow.appendChild(tableData);
originImagePieces.push(pieceImage.src);
imagePieces.push(pieceImage);
}
board.appendChild(tableRow);
}
suffleRendering();
startTimer();
}
img.src = URL.createObjectURL(file);
imageItem.appendChild(img);
imageItem.appendChild(para);
preview.appendChild(imageItem);
}
}
퍼즐게임 끝.
재미로 만들어본 프로젝트라 코드가 난잡하고 더럽지만,
재미로 하기에는 아주 좋은 프로젝트였다.
그럼 이만~.
'뚱땅뚱땅' 카테고리의 다른 글
[CSS3] pure CSS로 parallax scrolling 구현하기 (0) | 2020.11.22 |
---|---|
[HTML5/Javascript] 직소퍼즐 만들기 3: 퍼즐 조각 무작위로 섞기 (0) | 2020.10.24 |
[HTML5/JavaScript] 직소퍼즐 만들기 - 2: 드래그드롭으로 퍼즐넣기 (0) | 2020.09.28 |
[HTML5/JavaScript] 직소퍼즐 만들기 - 1 : 파일 입력 (0) | 2020.09.28 |