基于SpringBoot2048游戏+加强版
项目 总体结构

项目需要的依赖=Maven依赖配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.12</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jd</groupId>
<artifactId>Handle2048Game</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Handle2048Game</name>
<description>Handle2048Game</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
项目应用配置信息
# 应用名称
# 用于在分布式系统中唯一标识应用
spring.application.name=Handle2048Game
# Spring Boot 应用配置
# src/main/resources/application.properties
# 服务器端口
# 设置应用运行的端口号
server.port=8080
# Thymeleaf 模板配置
# 设置Thymeleaf模板文件的位置和格式
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
# 开发环境下禁用模板缓存,以便实时看到模板更改的效果
spring.thymeleaf.cache=false
# 静态资源配置
# 设置静态资源文件的位置,如CSS、JavaScript文件
spring.web.resources.static-locations=classpath:/static/
# 日志配置
# 设置日志级别,以便捕获不同级别的日志信息
logging.level.com.example.game2048=DEBUG
logging.level.org.springframework.web=DEBUG
# JSON 格式化
# 设置JSON输出格式,便于阅读和调试
spring.jackson.serialization.indent-output=true
主应用类
package com.jd.handle2048game;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 1.主应用程序
@SpringBootApplication
public class Handle2048GameApplication {
public static void main(String[] args) {
SpringApplication.run(Handle2048GameApplication.class, args);
}
}
游戏状态枚举
package com.jd.handle2048game.dao;
// 2. 游戏状态枚举
/**
* 游戏状态枚举类
* 用于定义游戏的三种状态:进行中、胜利、失败
*/
public enum GameStatus {
// 游戏进行中状态
PLAYING,
// 游戏胜利状态
WIN,
// 游戏失败状态
LOSE
}
游戏板逻辑
package com.jd.handle2048game.dao;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/** 游戏板的逻辑2
* GameBoard 类代表 2048 游戏的游戏板。
* 它包含了游戏板的状态、分数、游戏状态以及相关操作。
*/
public class GameBoard {
private int[][] board;
private int score;
private GameStatus status;
private static final int SIZE = 4;
private static final int TARGET = 2048;
/**
* 构造一个 GameBoard 对象。
* 初始化游戏板,设置初始分数为 0,游戏状态为 PLAYING,并调用 initBoard 方法。
*/
public GameBoard() {
this.board = new int[SIZE][SIZE];
this.score = 0;
this.status = GameStatus.PLAYING;
initBoard();
}
/**
* 初始化游戏板,添加两个随机方块。
*/
private void initBoard() {
addRandomTile();
addRandomTile();
}
/**
* 添加一个随机方块到游戏板上。
* 方块的值为 2 或 4,位置随机。
*/
private void addRandomTile() {
List<int[]> emptyCells = getEmptyCells();
if (!emptyCells.isEmpty()) {
Random random = new Random();
int[] cell = emptyCells.get(random.nextInt(emptyCells.size()));
board[cell[0]][cell[1]] = random.nextDouble() < 0.9 ? 2 : 4;
}
}
/**
* 获取游戏板上所有空单元格的列表。
*
* @return 包含所有空单元格坐标的列表。
*/
private List<int[]> getEmptyCells() {
List<int[]> emptyCells = new ArrayList<>();
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (board[i][j] == 0) {
emptyCells.add(new int[]{i, j});
}
}
}
return emptyCells;
}
/**
* 根据指定方向移动方块。
*
* @param direction 移动方向,可以是 "up"、"down"、"left" 或 "right"。
* @return 如果方块移动了,则返回 true;否则返回 false。
*/
public boolean move(String direction) {
int[][] oldBoard = copyBoard();
boolean moved = false;
switch (direction.toLowerCase()) {
case "up":
moved = moveUp();
break;
case "down":
moved = moveDown();
break;
case "left":
moved = moveLeft();
break;
case "right":
moved = moveRight();
break;
}
if (moved) {
addRandomTile();
updateGameStatus();
}
return moved;
}
/**
* 向左移动方块并合并。
*
* @return 如果方块移动了,则返回 true;否则返回 false。
*/
private boolean moveLeft() {
boolean moved = false;
for (int i = 0; i < SIZE; i++) {
int[] row = board[i].clone();
int[] newRow = moveAndMergeRow(row);
if (!Arrays.equals(row, newRow)) {
moved = true;
board[i] = newRow;
}
}
return moved;
}
/**
* 向右移动方块并合并。
*
* @return 如果方块移动了,则返回 true;否则返回 false。
*/
private boolean moveRight() {
boolean moved = false;
for (int i = 0; i < SIZE; i++) {
int[] row = board[i].clone();
reverseArray(row);
int[] newRow = moveAndMergeRow(row);
reverseArray(newRow);
if (!Arrays.equals(board[i], newRow)) {
moved = true;
board[i] = newRow;
}
}
return moved;
}
/**
* 向上移动方块并合并。
*
* @return 如果方块移动了,则返回 true;否则返回 false。
*/
private boolean moveUp() {
transpose();
boolean moved = moveLeft();
transpose();
return moved;
}
/**
* 向下移动方块并合并。
*
* @return 如果方块移动了,则返回 true;否则返回 false。
*/
private boolean moveDown() {
transpose();
boolean moved = moveRight();
transpose();
return moved;
}
/**
* 移动并合并一行中的方块。
*
* @param row 要移动并合并的行。
* @return 移动并合并后的行。
*/
private int[] moveAndMergeRow(int[] row) {
int[] newRow = new int[SIZE];
int index = 0;
// 移动非零元素
for (int value : row) {
if (value != 0) {
newRow[index++] = value;
}
}
// 合并相同元素
for (int i = 0; i < SIZE - 1; i++) {
if (newRow[i] != 0 && newRow[i] == newRow[i + 1]) {
newRow[i] *= 2;
newRow[i + 1] = 0;
score += newRow[i];
if (newRow[i] == TARGET) {
status = GameStatus.WIN;
}
}
}
// 再次移动非零元素
int[] finalRow = new int[SIZE];
index = 0;
for (int value : newRow) {
if (value != 0) {
finalRow[index++] = value;
}
}
return finalRow;
}
/**
* 转置游戏板,即行列互换。
*/
private void transpose() {
for (int i = 0; i < SIZE; i++) {
for (int j = i + 1; j < SIZE; j++) {
int temp = board[i][j];
board[i][j] = board[j][i];
board[j][i] = temp;
}
}
}
/**
* 反转数组,即数组中的元素顺序颠倒。
*
* @param arr 要反转的数组。
*/
private void reverseArray(int[] arr) {
for (int i = 0; i < arr.length / 2; i++) {
int temp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = temp;
}
}
/**
* 复制游戏板。
*
* @return 复制的游戏板。
*/
private int[][] copyBoard() {
int[][] copy = new int[SIZE][SIZE];
for (int i = 0; i < SIZE; i++) {
System.arraycopy(board[i], 0, copy[i], 0, SIZE);
}
return copy;
}
/**
* 更新游戏状态。
*/
private void updateGameStatus() {
if (status == GameStatus.WIN) {
return;
}
if (getEmptyCells().isEmpty() && !canMove()) {
status = GameStatus.LOSE;
}
}
/**
* 检查游戏板上是否还能移动。
*
* @return 如果还能移动,则返回 true;否则返回 false。
*/
private boolean canMove() {
// 检查是否还能移动(有相邻相同数字)
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (j < SIZE - 1 && board[i][j] == board[i][j + 1]) return true;
if (i < SIZE - 1 && board[i][j] == board[i + 1][j]) return true;
}
}
return false;
}
/**
* 重置游戏。
*/
public void reset() {
this.board = new int[SIZE][SIZE];
this.score = 0;
this.status = GameStatus.PLAYING;
initBoard();
}
// Getters
public int[][] getBoard() { return board; }
public int getScore() { return score; }
public GameStatus getStatus() { return status; }
}
响应数据传输对象
package com.jd.handle2048game.dao;
/**
* 游戏响应类,用于封装游戏的状态和结果
* 提供游戏板状态、得分、游戏状态和是否移动的封装
*/
public class GameResponse {
// 游戏板,二维数组表示
private int[][] board;
// 当前得分
private int score;
// 游戏状态
private GameStatus status;
// 是否移动,用于指示最近一次操作是否导致游戏板变化
private boolean moved;
// 默认构造函数
public GameResponse() {
}
/**
* 带参数的构造函数,用于初始化游戏响应对象
*
* @param board 游戏板状态,二维数组表示
* @param score 当前得分
* @param status 游戏状态
* @param moved 是否移动,指示最近一次操作是否导致游戏板变化
*/
public GameResponse(int[][] board, int score, GameStatus status, boolean moved) {
this.board = board;
this.score = score;
this.status = status;
this.moved = moved;
}
// Getters and Setters
/**
* 获取游戏板状态
*
* @return 游戏板状态,二维数组表示
*/
public int[][] getBoard() {
return board;
}
/**
* 设置游戏板状态
*
* @param board 游戏板状态,二维数组表示
*/
public void setBoard(int[][] board) {
this.board = board;
}
/**
* 获取当前得分
*
* @return 当前得分
*/
public int getScore() {
return score;
}
/**
* 设置当前得分
*
* @param score 当前得分
*/
public void setScore(int score) {
this.score = score;
}
/**
* 获取游戏状态
*
* @return 游戏状态
*/
public GameStatus getStatus() {
return status;
}
/**
* 设置游戏状态
*
* @param status 游戏状态
*/
public void setStatus(GameStatus status) {
this.status = status;
}
/**
* 获取是否移动的状态
*
* @return 是否移动,true表示最近一次操作导致游戏板变化,false表示未变化
*/
public boolean isMoved() {
return moved;
}
/**
* 设置是否移动的状态
*
* @param moved 是否移动,true表示最近一次操作导致游戏板变化,false表示未变化
*/
public void setMoved(boolean moved) {
this.moved = moved;
}
}
游戏业务逻辑
package com.jd.handle2048game.services;
import com.jd.handle2048game.dao.GameBoard;
import com.jd.handle2048game.dao.GameResponse;
import org.springframework.stereotype.Service;
/**
* 游戏服务类,提供2048游戏的基本操作接口
*/
@Service
public class GameService {
// 游戏棋盘实例
private GameBoard gameBoard;
// 构造方法,初始化游戏棋盘
public GameService() {
this.gameBoard = new GameBoard();
}
/**
* 获取当前游戏状态
*
* @return 返回包含当前游戏棋盘、分数、状态和是否可以移动的响应对象
*/
public GameResponse getGameState() {
return new GameResponse(
gameBoard.getBoard(),
gameBoard.getScore(),
gameBoard.getStatus(),
true
);
}
/**
* 根据指定方向移动棋盘
*
* @param direction 移动方向,包括上、下、左、右
* @return 返回更新后的游戏状态和是否成功移动的响应对象
*/
public GameResponse move(String direction) {
// 尝试按指定方向移动棋盘,并记录是否成功移动
boolean moved = gameBoard.move(direction);
return new GameResponse(
gameBoard.getBoard(),
gameBoard.getScore(),
gameBoard.getStatus(),
moved
);
}
/**
* 重置游戏,用于开始新游戏
*
* @return 返回新游戏的初始状态响应对象
*/
public GameResponse newGame() {
// 重置游戏棋盘
gameBoard.reset();
// 返回新游戏的初始状态
return getGameState();
}
}
游戏API控制器
package com.jd.handle2048game.controller;
import com.jd.handle2048game.dao.GameResponse;
import com.jd.handle2048game.services.GameService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* 6. 游戏控制器
* 该控制器用于处理与2048游戏相关的HTTP请求,提供游戏状态获取、移动操作和新游戏初始化的接口
*/
@RestController
@RequestMapping("/api/game")
@CrossOrigin(origins = "*")
public class GameController {
@Autowired
private GameService gameService;
/**
* 获取当前游戏状态
*
* @return 当前游戏状态的响应对象,包含游戏矩阵、分数等信息
*/
@GetMapping("/state")
public GameResponse getGameState() {
return gameService.getGameState();
}
/**
* 执行游戏中的移动操作
*
* @param direction 移动方向,可以是上、下、左、右
* @return 执行移动操作后的游戏状态响应对象
*/
@PostMapping("/move/{direction}")
public GameResponse move(@PathVariable String direction) {
return gameService.move(direction);
}
/**
* 新建一个游戏
*
* @return 新建游戏的初始状态响应对象
*/
@PostMapping("/new")
public GameResponse newGame() {
return gameService.newGame();
}
}
页面控制器
package com.jd.handle2048game.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* WebController类负责处理与2048游戏相关的网络请求
* 它使用Spring框架的@Controller注解来标记其作为一个控制器类
*/
@Controller
public class WebController {
/**
* 处理根路径的GET请求,返回游戏的主页视图
*
* @return 返回"index"字符串,指示视图解析器解析并返回对应的主页视图
*/
@GetMapping("/")
public String index() {
return "index";
}
}
游戏前端页面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2048游戏</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
color: #333;
}
.game-container {
background: white;
border-radius: 20px;
padding: 30px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
text-align: center;
max-width: 500px;
width: 100%;
}
.game-title {
font-size: 48px;
font-weight: bold;
color: #776e65;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}
.game-description {
color: #776e65;
margin-bottom: 20px;
font-size: 14px;
}
.score-container {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.score-box {
background: #bbada0;
padding: 10px 20px;
border-radius: 10px;
color: white;
font-weight: bold;
min-width: 80px;
}
.score-label {
font-size: 12px;
}
.score-value {
font-size: 20px;
}
.game-board {
background: #bbada0;
border-radius: 10px;
padding: 10px;
margin: 20px 0;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 10px;
max-width: 400px;
margin-left: auto;
margin-right: auto;
}
.tile {
background: #cdc1b4;
border-radius: 6px;
width: 80px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 24px;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.tile.empty {
background: #cdc1b4;
}
.tile.tile-2 { background: #eee4da; color: #776e65; }
.tile.tile-4 { background: #ede0c8; color: #776e65; }
.tile.tile-8 { background: #f2b179; color: #f9f6f2; }
.tile.tile-16 { background: #f59563; color: #f9f6f2; }
.tile.tile-32 { background: #f67c5f; color: #f9f6f2; }
.tile.tile-64 { background: #f65e3b; color: #f9f6f2; }
.tile.tile-128 { background: #edcf72; color: #f9f6f2; font-size: 20px; }
.tile.tile-256 { background: #edcc61; color: #f9f6f2; font-size: 20px; }
.tile.tile-512 { background: #edc850; color: #f9f6f2; font-size: 20px; }
.tile.tile-1024 { background: #edc53f; color: #f9f6f2; font-size: 18px; }
.tile.tile-2048 { background: #edc22e; color: #f9f6f2; font-size: 18px; box-shadow: 0 0 20px rgba(237, 194, 46, 0.5); }
.controls {
margin: 20px 0;
}
.new-game-btn {
background: #8f7a66;
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
margin: 0 10px;
}
.new-game-btn:hover {
background: #9f8a76;
transform: translateY(-2px);
}
.game-instructions {
margin-top: 20px;
font-size: 14px;
color: #776e65;
line-height: 1.4;
}
.game-over {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
}
.game-over-content {
background: white;
padding: 40px;
border-radius: 20px;
text-align: center;
max-width: 400px;
}
.game-over-title {
font-size: 32px;
margin-bottom: 20px;
color: #776e65;
}
.game-over.win .game-over-title {
color: #f2b179;
}
.game-over.lose .game-over-title {
color: #f67c5f;
}
.arrow-keys {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 10px;
max-width: 150px;
margin: 20px auto;
}
.arrow-key {
background: #bbada0;
color: white;
border: none;
padding: 15px;
border-radius: 8px;
font-size: 18px;
cursor: pointer;
transition: all 0.2s ease;
}
.arrow-key:hover {
background: #a39489;
}
.arrow-key:active {
transform: scale(0.95);
}
.arrow-key.up { grid-column: 2; }
.arrow-key.left { grid-column: 1; grid-row: 2; }
.arrow-key.right { grid-column: 3; grid-row: 2; }
.arrow-key.down { grid-column: 2; grid-row: 3; }
@media (max-width: 600px) {
.game-container {
margin: 20px;
padding: 20px;
}
.tile {
width: 60px;
height: 60px;
font-size: 18px;
}
.tile.tile-128, .tile.tile-256, .tile.tile-512 {
font-size: 16px;
}
.tile.tile-1024, .tile.tile-2048 {
font-size: 14px;
}
}
</style>
</head>
<body>
<div class="game-container">
<h1 class="game-title">2048</h1>
<p class="game-description">合并数字,达到2048!</p>
<div class="score-container">
<div class="score-box">
<div class="score-label">得分</div>
<div class="score-value" id="score">0</div>
</div>
<div class="score-box">
<div class="score-label">状态</div>
<div class="score-value" id="status">游戏中</div>
</div>
</div>
<div class="controls">
<button class="new-game-btn" onclick="newGame()">新游戏</button>
</div>
<div class="game-board" id="gameBoard">
<!-- 游戏板将在这里动态生成 -->
</div>
<div class="arrow-keys">
<button class="arrow-key up" onclick="move('up')">↑</button>
<button class="arrow-key left" onclick="move('left')">←</button>
<button class="arrow-key right" onclick="move('right')">→</button>
<button class="arrow-key down" onclick="move('down')">↓</button>
</div>
<div class="game-instructions">
<p><strong>操作说明:</strong></p>
<p>使用方向键或点击箭头按钮移动数字</p>
<p>相同数字相撞时会合并成一个数字</p>
<p>目标是创建2048这个数字!</p>
</div>
</div>
<div class="game-over" id="gameOver">
<div class="game-over-content">
<h2 class="game-over-title" id="gameOverTitle">游戏结束</h2>
<p id="gameOverMessage">你的得分:<span id="finalScore">0</span></p>
<button class="new-game-btn" onclick="newGame()">再来一局</button>
</div>
</div>
<script>
const API_BASE = '/api/game';
let gameState = null;
// 初始化游戏
async function initGame() {
try {
const response = await fetch(`${API_BASE}/state`);
gameState = await response.json();
updateDisplay();
} catch (error) {
console.error('初始化游戏失败:', error);
}
}
// 移动
async function move(direction) {
if (!gameState || gameState.status !== 'PLAYING') {
return;
}
try {
const response = await fetch(`${API_BASE}/move/${direction}`, {
method: 'POST'
});
const newState = await response.json();
if (newState.moved) {
gameState = newState;
updateDisplay();
// 检查游戏状态
if (gameState.status === 'WIN' || gameState.status === 'LOSE') {
setTimeout(showGameOver, 500);
}
}
} catch (error) {
console.error('移动失败:', error);
}
}
// 新游戏
async function newGame() {
try {
const response = await fetch(`${API_BASE}/new`, {
method: 'POST'
});
gameState = await response.json();
updateDisplay();
hideGameOver();
} catch (error) {
console.error('开始新游戏失败:', error);
}
}
// 更新显示
function updateDisplay() {
if (!gameState) return;
// 更新得分
document.getElementById('score').textContent = gameState.score;
// 更新状态
const statusText = {
'PLAYING': '游戏中',
'WIN': '胜利!',
'LOSE': '失败'
};
document.getElementById('status').textContent = statusText[gameState.status] || '游戏中';
// 更新游戏板
const gameBoard = document.getElementById('gameBoard');
gameBoard.innerHTML = '';
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
const tile = document.createElement('div');
tile.className = 'tile';
const value = gameState.board[i][j];
if (value === 0) {
tile.classList.add('empty');
} else {
tile.classList.add(`tile-${value}`);
tile.textContent = value;
}
gameBoard.appendChild(tile);
}
}
}
// 显示游戏结束界面
function showGameOver() {
const gameOver = document.getElementById('gameOver');
const title = document.getElementById('gameOverTitle');
const finalScore = document.getElementById('finalScore');
if (gameState.status === 'WIN') {
gameOver.className = 'game-over win';
title.textContent = '恭喜你!';
document.getElementById('gameOverMessage').innerHTML =
`你达到了2048!<br>最终得分:<span id="finalScore">${gameState.score}</span>`;
} else {
gameOver.className = 'game-over lose';
title.textContent = '游戏结束';
document.getElementById('gameOverMessage').innerHTML =
`没有可移动的空间了!<br>最终得分:<span id="finalScore">${gameState.score}</span>`;
}
gameOver.style.display = 'flex';
}
// 隐藏游戏结束界面
function hideGameOver() {
document.getElementById('gameOver').style.display = 'none';
}
// 键盘事件监听
document.addEventListener('keydown', function(e) {
const keyMap = {
'ArrowUp': 'up',
'ArrowDown': 'down',
'ArrowLeft': 'left',
'ArrowRight': 'right'
};
if (keyMap[e.key]) {
e.preventDefault();
move(keyMap[e.key]);
}
});
// 页面加载时初始化游戏
window.addEventListener('load', initGame);
</script>
</body>
</html>
posted on 2025-06-09 10:01 Indian_Mysore 阅读(148) 评论(0) 收藏 举报
浙公网安备 33010602011771号