jQuery 를 사용한 2048 웹 게임

JavaScriptBeginner
지금 연습하기

소개

2048 은 전 세계적으로 선풍적인 인기를 끌고 있는, 배우기 매우 쉽고 중독성 있는 게임입니다. 아직 플레이해보지 않으셨다면, 모바일 폰에서 다운로드하여 시도해 볼 수 있습니다. 이 프로젝트는 HTML, CSS, JavaScript 및 jQuery 를 사용하여 2048 게임의 웹 버전을 만드는 과정을 안내합니다.

  • 웹 애플리케이션 개발 프로세스 학습
  • 다양한 크기의 화면을 수용하고 레이아웃 및 초기화를 처리하기 위해 모바일 장치에서 애플리케이션을 반응형으로 만드는 방법 탐구
  • JavaScript 및 jQuery 를 활용하여 게임 로직을 작성하고, 블록 이동을 구현하며, 게임 결과를 결정합니다.

👀 미리보기

2048 game preview animation

🎯 과제

이 프로젝트에서는 다음을 배우게 됩니다.

  • HTML 및 CSS 를 사용하여 2048 웹 게임의 페이지 레이아웃을 만드는 방법
  • JavaScript 및 jQuery 로 게임 로직을 구현하는 방법
  • 블록 이동 및 블록 병합을 처리하는 방법
  • 웹 브라우저에서 웹 게임을 테스트하고 실행하는 방법

🏆 성과

이 프로젝트를 완료하면 다음을 수행할 수 있습니다.

  • 2048 게임을 위한 반응형 웹 애플리케이션 개발
  • JavaScript 및 jQuery 를 활용하여 게임 로직 및 기능 작성
  • 게임에서 블록 이동 및 병합 구현
  • 웹 브라우저에서 웹 게임 테스트 및 실행

개발 준비

페이지 스타일을 제외하고, 2048 게임은 2 차원 배열로 추상화할 수 있습니다. 초기 상태에서는 두 개의 숫자를 무작위로 생성해야 합니다. 무작위로 생성된 숫자는 2 또는 4 만 가능합니다. 다음 구현에서는 2 와 4 의 발생 확률을 동일하게 만들 것입니다. 물론, 4 가 나타날 확률을 더 작게 만들 수도 있습니다.

0 0 0 0
2 0 0 0
0 0 2 0

다음으로, 방향키를 누르면 숫자가 해당 방향으로 이동하고, 같은 숫자를 가진 인접한 셀이 병합됩니다. 그런 다음, 다른 숫자가 무작위로 생성됩니다. 예를 들어, 위쪽 → 왼쪽 → 위쪽을 누르면 인터페이스가 다음과 같이 될 수 있습니다.

2 0 2 0
0 0 0 0
0 0 0 0
4 0 0 0
4 0 0 0
0 0 0 0
0 0 2 0
4 0 0 0
8 0 2 0
0 0 0 0
0 4 0 0

이런 식으로, 숫자를 더 이상 병합할 수 없거나 2048 이 나타날 때까지 계속해서 숫자를 이동하고 병합하여 게임을 종료합니다. 보시다시피, 게임의 핵심은 이 2 차원 배열을 조작하는 것입니다.

✨ 솔루션 확인 및 연습

프로젝트 파일 구조

먼저, ~/project 경로 아래에 다음 파일 구조를 생성해야 합니다.

~/project
 |__ index.html
 |__ main.js
 |__ support.js
 |__ showanimation.js
 |__ style.css

jquery.min.js는 이미 ~/project 경로 아래에 있으므로, 바로 사용할 수 있습니다.

이제 본격적으로 코딩 작업을 시작해 봅시다! 페이지 레이아웃부터 시작하여 체스판 초기화 및 숫자 블록 이동과 같은 모듈을 점차적으로 완성할 것입니다.

✨ 솔루션 확인 및 연습

페이지 레이아웃

보셨듯이, 2048 게임은 16 개의 사각형으로 구성됩니다. 아래에서는 div + css 를 사용하여 이 4X4 셀을 그릴 것입니다. index.html 파일에 다음 코드를 추가하십시오.

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
    />
    <title>2048 Game</title>
    <link rel="stylesheet" href="style.css" />
    <script type="text/javascript" src="jquery.min.js"></script>
    <script type="text/javascript" src="main.js"></script>
    <script type="text/javascript" src="showanimation.js"></script>
    <script type="text/javascript" src="support.js"></script>
  </head>
  <body>
    <header>
      <h1>2048 Game</h1>
      <a href="javascript:new_game();" id="new_game_button">New Game</a>
      <p>score: <span id="score">0</span></p>
    </header>
    <div id="grid_container">
      <div class="grid_cell" id="grid_cell_0_0"></div>
      <div class="grid_cell" id="grid_cell_0_1"></div>
      <div class="grid_cell" id="grid_cell_0_2"></div>
      <div class="grid_cell" id="grid_cell_0_3"></div>

      <div class="grid_cell" id="grid_cell_1_0"></div>
      <div class="grid_cell" id="grid_cell_1_1"></div>
      <div class="grid_cell" id="grid_cell_1_2"></div>
      <div class="grid_cell" id="grid_cell_1_3"></div>

      <div class="grid_cell" id="grid_cell_2_0"></div>
      <div class="grid_cell" id="grid_cell_2_1"></div>
      <div class="grid_cell" id="grid_cell_2_2"></div>
      <div class="grid_cell" id="grid_cell_2_3"></div>

      <div class="grid_cell" id="grid_cell_3_0"></div>
      <div class="grid_cell" id="grid_cell_3_1"></div>
      <div class="grid_cell" id="grid_cell_3_2"></div>
      <div class="grid_cell" id="grid_cell_3_3"></div>
    </div>
  </body>
</html>

다음으로, 페이지와 각 셀에 스타일을 추가해야 합니다. style.css 파일에 다음 코드를 추가하십시오.

body {
  padding: 50px 0px;
}

header {
  display: block;
  margin: 0 auto;
  width: 100%;
  text-align: center;
}

header h1 {
  font-family: Arial;
  font-size: 40px;
  font-weight: bold;
  margin: 0 auto;
  color: #776e65;
  padding: 20px 0px;
}

header #new_game_button {
  display: block;
  margin: 0px auto;
  width: 100px;
  padding: 10px 10px;
  background-color: #8f7a66;
  font-family: Arial;
  color: white;
  border-radius: 10px;
  text-decoration: none;
}

header #new_game_button:hover {
  background-color: #9f8b77;
}

header p {
  font-family: Arial;
  font-size: 25px;
  margin: 5px auto;
}

#grid_container {
  width: 460px;
  height: 460px;
  padding: 20px;
  margin: 0px auto;
  background-color: #bbada0;
  border-radius: 10px;
  position: relative;
}

.grid_cell {
  width: 100px;
  height: 100px;
  border-radius: 6px;
  background-color: #ccc0b3;
  position: absolute;
}

.number_cell {
  border-radius: 6px;
  font-family: Arial;
  font-weight: bold;
  font-size: 60px;
  line-height: 100px;
  text-align: center;
  position: absolute;
}

이 단계를 완료한 후, Preview 를 사용하여 index.html 파일을 엽니다.

HTML code for 2048 game

다음과 같은 결과를 볼 수 있습니다.

2048 game grid layout
✨ 솔루션 확인 및 연습

체스판 초기화

모바일 기기의 인기가 높아짐에 따라, 다양한 화면 크기에 대한 적응성을 처리해야 합니다.

게임 시작 시, 체스판에 두 개의 임의의 숫자를 생성해야 합니다. 이는 JavaScript 코드를 통해 수행됩니다.

main.js에 다음을 추가하십시오.

var board = new Array(); // 각 셀의 숫자
var score = 0; // 점수
var has_conflicted = new Array(); // 연속적인 제거를 해결하기 위한 플래그
var startx = 0; // 모바일 화면 터치 시 시작점의 x 좌표
var starty = 0; // 모바일 화면 터치 시 시작점의 y 좌표
var endx = 0; // 모바일 화면 터치 시 종료점의 x 좌표
var endy = 0; // 모바일 화면 터치 시 종료점의 y 좌표
var success_string = "Success";
var gameover_string = "GameOver";

// HTML 문서가 로드된 후 체스판 초기화
$(document).ready(function () {
  // 적응성 처리
  prepare_for_mobile();
  new_game();
});

// 새 게임 시작
function new_game() {
  // 체스판 초기화
  init();
  // 두 개의 임의의 셀에 숫자 생성
  generate_one_number();
}

// 초기화
function init() {
  for (var i = 0; i < 4; i++) {
    for (var j = 0; j < 4; j++) {
      var grid_cell = $("#grid_cell_" + i + "_" + j);
      grid_cell.css("top", get_pos_top(i, j));
      grid_cell.css("left", get_pos_left(i, j));
    }
  }
  for (var i = 0; i < 4; i++) {
    board[i] = new Array();
    has_conflicted[i] = new Array();
    for (var j = 0; j < 4; j++) {
      board[i][j] = 0;
      has_conflicted[i][j] = false;
    }
  }
  update_board_view();
  score = 0;
  update_score(score);
}

// 체스판 뷰 업데이트
function update_board_view() {
  $(".number_cell").remove();
  for (var i = 0; i < 4; i++) {
    for (var j = 0; j < 4; j++) {
      $("#grid_container").append(
        '<div class="number_cell" id="number_cell_' + i + "_" + j + '"></div>'
      );
      var number_cell = $("#number_cell_" + i + "_" + j);
      if (board[i][j] == 0) {
        number_cell.css("width", "0px");
        number_cell.css("height", "0px");
        number_cell.css("top", get_pos_top(i, j) + cell_side_length / 2);
        number_cell.css("left", get_pos_left(i, j) + cell_side_length / 2);
      } else {
        number_cell.css("width", cell_side_length);
        number_cell.css("height", cell_side_length);
        number_cell.css("top", get_pos_top(i, j));
        number_cell.css("left", get_pos_left(i, j));
        number_cell.css(
          "background-color",
          get_number_background_color(board[i][j])
        );
        number_cell.css("color", get_number_color(board[i][j]));
        number_cell.text(board[i][j]);
      }
      has_conflicted[i][j] = false;
    }
  }
  $(".number_cell").css("line-height", cell_side_length + "px");
  $(".number_cell").css("font-size", 0.6 * cell_side_length + "px");
}

// 임의의 셀에 숫자 생성
function generate_one_number() {
  if (nospace(board)) {
    return false;
  }
  // 임의의 위치
  var randx = parseInt(Math.floor(Math.random() * 4));
  var randy = parseInt(Math.floor(Math.random() * 4));
  var time = 0;
  while (time < 50) {
    if (board[randx][randy] == 0) {
      break;
    }
    randx = parseInt(Math.floor(Math.random() * 4));
    randy = parseInt(Math.floor(Math.random() * 4));
    time++;
  }
  if (time == 50) {
    for (var i = 0; i < 4; i++) {
      for (var j = 0; j < 4; j++) {
        if (board[i][j] == 0) {
          randx = i;
          randy = j;
        }
      }
    }
  }
  // 임의의 숫자
  var rand_number = Math.random() < 0.5 ? 2 : 4;
  // 임의의 위치에 임의의 숫자 표시
  board[randx][randy] = rand_number;
  show_number_with_animation(randx, randy, rand_number);
  return true;
}

// 적응성 처리
function prepare_for_mobile() {
  if (document_width > 500) {
    grid_container_width = 500;
    cell_side_length = 100;
    cell_space = 20;
  }
  $("#grid_container").css("width", grid_container_width - 2 * cell_space);
  $("#grid_container").css("height", grid_container_width - 2 * cell_space);
  $("#grid_container").css("padding", cell_space);
  $("#grid_container").css("border-radius", 0.02 * grid_container_width);
  $(".grid_cell").css("width", cell_side_length);
  $(".grid_cell").css("height", cell_side_length);
  $(".grid_cell").css("border-radius", 0.02 * grid_container_width);
}
✨ 솔루션 확인 및 연습

게임 로직 개선

다음으로, 숫자 타일 이동 및 게임 종료 여부 확인을 포함하여 게임의 로직을 개선해야 합니다. support.js에서 다음 코드를 완성하십시오.

document_width = window.screen.availWidth; // 화면 너비
grid_container_width = 0.92 * document_width; // 게임 보드 너비
cell_side_length = 0.18 * document_width; // 각 그리드 셀의 크기
cell_space = 0.04 * document_width; // 각 그리드 셀 사이의 간격

// 게임 보드 상단에서 해당 그리드 셀까지의 거리 가져오기
function get_pos_top(i, j) {
  return cell_space + i * (cell_space + cell_side_length);
}

// 게임 보드 왼쪽에서 해당 그리드 셀까지의 거리 가져오기
function get_pos_left(i, j) {
  return cell_space + j * (cell_space + cell_side_length);
}

// 해당 숫자의 배경색 가져오기
function get_number_background_color(number) {
  switch (number) {
    case 2:
      return "#eee4da";
      break;
    case 4:
      return "#ede0c8";
      break;
    case 8:
      return "#f2b179";
      break;
    case 16:
      return "#f59563";
      break;
    case 32:
      return "#f67c5f";
      break;
    case 64:
      return "#f65e3b";
      break;
    case 128:
      return "#edcf72";
      break;
    case 256:
      return "#edcc61";
      break;
    case 512:
      return "#9c0";
      break;
    case 1024:
      return "#33b5e5";
      break;
    case 2048:
      return "#09c";
      break;
    case 4096:
      return "#a6c";
      break;
    case 8192:
      return "#93c";
      break;
  }
  return "black";
}

// 해당 숫자의 색상 가져오기
function get_number_color(number) {
  if (number <= 4) return "#776e65";
  return "white";
}

// 게임 보드에 빈 그리드 셀이 있는지 확인
function nospace(board) {
  for (var i = 0; i < 4; i++) {
    for (var j = 0; j < 4; j++) {
      if (board[i][j] == 0) {
        return false;
      }
    }
  }
  return true;
}
✨ 솔루션 확인 및 연습

애니메이션 효과 향상

다음으로, 숫자 타일 표시 및 점수 업데이트를 포함하여 애니메이션 효과를 향상시켜야 합니다. 이 코드는 모두 showanimation.js에 있습니다.

// 숫자 타일 표시를 위한 애니메이션
function show_number_with_animation(i, j, rand_number) {
  var number_cell = $("#number_cell_" + i + "_" + j);
  number_cell.css("background-color", get_number_background_color(rand_number));
  number_cell.css("color", get_number_color(rand_number));
  number_cell.text(rand_number);
  number_cell.animate(
    {
      width: cell_side_length,
      height: cell_side_length,
      top: get_pos_top(i, j),
      left: get_pos_left(i, j)
    },
    50
  );
}

// 점수 업데이트
function update_score(score) {
  $("#score").text(score);
}
✨ 솔루션 확인 및 연습

숫자 타일 이동

레이아웃 및 초기화를 완료한 후, 게임이 성공하거나 실패할 때까지 숫자 타일을 이동하고 제거하는 기능을 구현합니다.

main.js에 다음 코드를 추가하십시오.

// 키보드 화살표 키 움직임 감지
$(document).keydown(function (event) {
  if ($("#score").text() == success_string) {
    new_game();
    return;
  }
  switch (event.keyCode) {
    case 37: // 왼쪽
      event.preventDefault();
      if (move_left()) {
        setTimeout("generate_one_number()", 210);
        setTimeout("is_gameover()", 300);
      }
      break;
    case 38: // 위쪽
      event.preventDefault();
      if (move_up()) {
        setTimeout("generate_one_number()", 210);
        setTimeout("is_gameover()", 300);
      }
      break;
    case 39: // 오른쪽
      event.preventDefault();
      if (move_right()) {
        setTimeout("generate_one_number()", 210);
        setTimeout("is_gameover()", 300);
      }
      break;
    case 40: // 아래쪽
      event.preventDefault();
      if (move_down()) {
        setTimeout("generate_one_number()", 210);
        setTimeout("is_gameover()", 300);
      }
      break;
    default:
      break;
  }
});

// 모바일 장치에서 touchstart 이벤트 감지
document.addEventListener("touchstart", function (event) {
  startx = event.touches[0].pageX;
  starty = event.touches[0].pageY;
});

// 모바일 장치에서 touchmove 이벤트 감지
document.addEventListener("touchmove", function (event) {
  event.preventDefault();
});

// 모바일 장치에서 touchend 이벤트 감지
document.addEventListener("touchend", function (event) {
  endx = event.changedTouches[0].pageX;
  endy = event.changedTouches[0].pageY;

  var deltax = endx - startx;
  var deltay = endy - starty;
  if (
    Math.abs(deltax) < 0.3 * document_width &&
    Math.abs(deltay) < 0.3 * document_width
  ) {
    return;
  }
  if ($("#score").text() == success_string) {
    new_game();
    return;
  }
  // x 축 이동
  if (Math.abs(deltax) >= Math.abs(deltay)) {
    if (deltax > 0) {
      // 오른쪽으로 이동
      if (move_right()) {
        setTimeout("generate_one_number()", 210);
        setTimeout("is_gameover()", 300);
      }
    } else {
      // 왼쪽으로 이동
      if (move_left()) {
        setTimeout("generate_one_number()", 210);
        setTimeout("is_gameover()", 300);
      }
    }
  } else {
    // y 축 이동
    if (deltay > 0) {
      // 아래로 이동
      if (move_down()) {
        setTimeout("generate_one_number()", 210);
        setTimeout("is_gameover()", 300);
      }
    } else {
      // 위로 이동
      if (move_up()) {
        setTimeout("generate_one_number()", 210);
        setTimeout("is_gameover()", 300);
      }
    }
  }
});

// 왼쪽으로 이동
function move_left() {
  if (!can_move_left(board)) {
    return false;
  }
  // 왼쪽으로 이동
  for (var i = 0; i < 4; i++) {
    for (var j = 1; j < 4; j++) {
      if (board[i][j] != 0) {
        for (var k = 0; k < j; k++) {
          if (board[i][k] == 0 && no_block_horizontal(i, k, j, board)) {
            show_move_animation(i, j, i, k);
            board[i][k] = board[i][j];
            board[i][j] = 0;
            break;
          } else if (
            board[i][k] == board[i][j] &&
            no_block_horizontal(i, k, j, board) &&
            !has_conflicted[i][k]
          ) {
            show_move_animation(i, j, i, k);
            board[i][k] += board[i][j];
            board[i][j] = 0;
            // 점수 추가
            score += board[i][k];
            update_score(score);
            has_conflicted[i][k] = true;
            break;
          }
        }
      }
    }
  }
  setTimeout("update_board_view()", 200);
  return true;
}

// 오른쪽으로 이동
function move_right() {
  if (!can_move_right(board)) {
    return false;
  }
  // 오른쪽으로 이동
  for (var i = 0; i < 4; i++) {
    for (var j = 2; j >= 0; j--) {
      if (board[i][j] != 0) {
        for (var k = 3; k > j; k--) {
          if (board[i][k] == 0 && no_block_horizontal(i, j, k, board)) {
            show_move_animation(i, j, i, k);
            board[i][k] = board[i][j];
            board[i][j] = 0;
            break;
          } else if (
            board[i][k] == board[i][j] &&
            no_block_horizontal(i, j, k, board) &&
            !has_conflicted[i][k]
          ) {
            show_move_animation(i, j, i, k);
            board[i][k] += board[i][j];
            board[i][j] = 0;
            // 점수 추가
            score += board[i][k];
            update_score(score);
            has_conflicted[i][k] = true;
            break;
          }
        }
      }
    }
  }
  setTimeout("update_board_view()", 200);
  return true;
}

// 위로 이동
function move_up() {
  if (!can_move_up(board)) {
    return false;
  }
  // 위로 이동
  for (var j = 0; j < 4; j++) {
    for (var i = 1; i < 4; i++) {
      if (board[i][j] != 0) {
        for (var k = 0; k < i; k++) {
          if (board[k][j] == 0 && no_block_vertical(j, k, i, board)) {
            show_move_animation(i, j, k, j);
            board[k][j] = board[i][j];
            board[i][j] = 0;
            break;
          } else if (
            board[k][j] == board[i][j] &&
            no_block_vertical(j, k, i, board) &&
            !has_conflicted[k][j]
          ) {
            show_move_animation(i, j, k, j);
            board[k][j] += board[i][j];
            board[i][j] = 0;
            // 점수 추가
            score += board[k][j];
            update_score(score);
            has_conflicted[k][j] = true;
            break;
          }
        }
      }
    }
  }
  setTimeout("update_board_view()", 200);
  return true;
}

// 아래로 이동
function move_down() {
  if (!can_move_down(board)) {
    return false;
  }
  // 아래로 이동
  for (var j = 0; j < 4; j++) {
    for (var i = 2; i >= 0; i--) {
      if (board[i][j] != 0) {
        for (var k = 3; k > i; k--) {
          if (board[k][j] == 0 && no_block_vertical(j, i, k, board)) {
            show_move_animation(i, j, k, j);
            board[k][j] = board[i][j];
            board[i][j] = 0;
            break;
          } else if (
            board[k][j] == board[i][j] &&
            no_block_vertical(j, i, k, board) &&
            !has_conflicted[k][j]
          ) {
            show_move_animation(i, j, k, j);
            board[k][j] += board[i][j];
            board[i][j] = 0;
            // 점수 추가
            score += board[k][j];
            update_score(score);
            has_conflicted[k][j] = true;
            break;
          }
        }
      }
    }
  }
  setTimeout("update_board_view()", 200);
  return true;
}

// 게임 성공 또는 실패 여부 확인
function is_gameover() {
  for (var i = 0; i < 4; i++) {
    for (var j = 0; j < 4; j++) {
      if (board[i][j] == 2048) {
        update_score(success_string);
        return;
      }
    }
  }
  if (nospace(board) && nomove(board)) {
    gameover();
  }
}

// 게임 종료 시 게임 오버 텍스트 업데이트
function gameover() {
  update_score(gameover_string);
}
✨ 솔루션 확인 및 연습

support.js 개선

다음으로, 이동 가능 여부 및 아직 이동 가능한지 확인하는 기능을 포함하여 support.js의 코드를 개선해야 합니다.

// 왼쪽으로 이동 가능한지 확인
function can_move_left(board) {
  for (var i = 0; i < 4; i++) {
    for (var j = 1; j < 4; j++) {
      if (board[i][j] != 0) {
        if (board[i][j - 1] == 0 || board[i][j] == board[i][j - 1]) {
          return true;
        }
      }
    }
  }
  return false;
}

// 오른쪽으로 이동 가능한지 확인
function can_move_right(board) {
  for (var i = 0; i < 4; i++) {
    for (var j = 2; j >= 0; j--) {
      if (board[i][j] != 0) {
        if (board[i][j + 1] == 0 || board[i][j] == board[i][j + 1]) {
          return true;
        }
      }
    }
  }
  return false;
}

// 위로 이동 가능한지 확인
function can_move_up(board) {
  for (var j = 0; j < 4; j++) {
    for (var i = 1; i < 4; i++) {
      if (board[i][j] != 0) {
        if (board[i - 1][j] == 0 || board[i - 1][j] == board[i][j]) {
          return true;
        }
      }
    }
  }
  return false;
}

// 아래로 이동 가능한지 확인
function can_move_down(board) {
  for (var j = 0; j < 4; j++) {
    for (var i = 2; i >= 0; i--) {
      if (board[i][j] != 0) {
        if (board[i + 1][j] == 0 || board[i + 1][j] == board[i][j]) {
          return true;
        }
      }
    }
  }
  return false;
}

// 가로 방향에 블록이 없는지 확인
function no_block_horizontal(row, col1, col2, board) {
  for (var i = col1 + 1; i < col2; i++) {
    if (board[row][i] != 0) {
      return false;
    }
  }
  return true;
}

// 세로 방향에 블록이 없는지 확인
function no_block_vertical(col, row1, row2, board) {
  for (var i = row1 + 1; i < row2; i++) {
    if (board[i][col] != 0) {
      return false;
    }
  }
  return true;
}

// 아직 이동 가능한지 확인
function nomove(board) {
  if (
    can_move_down(board) ||
    can_move_up(board) ||
    can_move_right(board) ||
    can_move_left(board)
  ) {
    return false;
  }
  return true;
}
✨ 솔루션 확인 및 연습

게임 완료

마지막으로, 이동 애니메이션과 병합 애니메이션을 포함하여 showanimation.js의 코드를 완성해야 합니다.

// 그리드가 이동할 때의 애니메이션 효과
function show_move_animation(fromx, fromy, tox, toy) {
  var number_cell = $("#number_cell_" + fromx + "_" + fromy);
  number_cell.animate(
    {
      top: get_pos_top(tox, toy),
      left: get_pos_left(tox, toy)
    },
    200
  );
}

이것으로 2048 웹 버전이 완성되었습니다.

✨ 솔루션 확인 및 연습

실행 및 테스트

웹 브라우저에서 index.html을 엽니다.

Web browser displaying index html

다음 효과를 보려면 WebIDE 의 오른쪽 하단 모서리에 있는 Go Live 버튼을 클릭하고 "Web 8080" 탭으로 전환하십시오.

Live preview demonstration
✨ 솔루션 확인 및 연습

요약

이 프로젝트에서는 HTML, CSS, JavaScript 및 jQuery 를 사용하여 2048 게임의 웹 버전을 구현했습니다. 또한 모바일 장치와 호환되도록 만드는 방법도 배웠습니다. 이 프로젝트를 통해 프론트엔드 기술에 대한 이해를 높이고 포괄적인 응용 능력을 향상시킬 수 있을 것으로 생각됩니다.