1. 引言
大家好!今天我们要一起动手做一个有趣的小项目——一个可以在局域网内共享的贪吃蛇游戏。这个游戏不仅能够锻炼你的编程技能,还能让你的朋友或家人在同一个网络环境下一起玩耍。我们将使用一些基础但强大的工具:Node.js 和 HTML/CSS/JavaScript。如果你是编程新手,也不用担心,我会一步一步带你完成这个项目,并尽量用简单易懂的语言来解释每一个步骤。
2. 项目目标
- 创建一个简单的贪吃蛇游戏。
- 使用Node.js搭建一个本地服务器。
- 将游戏部署到服务器上,使其可以通过局域网访问。
3. 准备工作
3.1 安装Node.js
首先,我们需要安装Node.js。Node.js是一个基于Chrome V8引擎的JavaScript运行时,让我们能够在服务器端运行JavaScript代码。
-
下载Node.js:
- 访问Node.js官网。
- 点击“Download”按钮,选择适合你操作系统的版本(推荐LTS版本)。
- 下载完成后,双击安装包并按照提示完成安装。
-
验证安装:
-
打开命令行工具(Windows用户可以使用“命令提示符”或“PowerShell”,macOS/Linux用户可以使用“终端”)。
-
输入以下命令并按回车:
node -v -
如果安装成功,会显示Node.js的版本号,例如
v20.9.0。 -
同样地,输入以下命令验证npm是否安装成功:
npm -v -
这也会显示npm的版本号,例如
10.1.0。
-
3.2 创建项目目录
接下来,我们将在D盘根目录下创建一个新的文件夹来存放我们的项目文件。
-
打开命令行工具:
- Windows用户打开“命令提示符”或“PowerShell”。
- macOS/Linux用户打开“终端”。
-
导航到D盘根目录:
cd D:\ -
创建项目文件夹:
mkdir snake_game_server -
进入项目文件夹:
cd snake_game_server
4. 初始化Node.js项目
现在,我们来初始化一个新的Node.js项目。这将创建一个 package.json 文件,用于管理项目的依赖和配置信息。
-
初始化项目:
-
在命令行中输入以下命令并按回车:
npm init -y -
-y参数表示使用默认设置,无需手动输入任何信息。
-
-
查看生成的
package.json文件:-
在项目目录下会生成一个
package.json文件,内容大致如下:{ "name": "snake_game_server", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
-
5. 编写贪吃蛇游戏代码
接下来,我们将编写贪吃蛇游戏的核心代码。这部分包括HTML、CSS和JavaScript。
5.1 创建HTML文件
- 创建
index.html文件:- 在项目目录下创建一个新的文件
index.html。 - 使用你喜欢的文本编辑器(如Notepad++、VS Code等)打开该文件,并粘贴以下代码:
- 在项目目录下创建一个新的文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>贪吃蛇游戏</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f4f4f9;
}
canvas {
border: 1px solid #333;
background-color: #fff;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const gridSize = 20;
const tileCount = canvas.width / gridSize;
let snake = [{ x: 10, y: 10 }];
let dx = 0;
let dy = 0;
let food = { x: 15, y: 15 };
let score = 0;
function getRandomFoodPosition() {
return {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
}
function drawRect(x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x * gridSize, y * gridSize, gridSize, gridSize);
}
function clearScreen() {
ctx.fillStyle = '#f4f4f9';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
function moveSnake() {
const head = { x: snake[0].x + dx, y: snake[0].y + dy };
snake.unshift(head);
if (head.x === food.x && head.y === food.y) {
score++;
food = getRandomFoodPosition();
} else {
snake.pop();
}
if (
head.x < 0 || head.x >= tileCount ||
head.y < 0 || head.y >= tileCount ||
snake.slice(1).some(segment => segment.x === head.x && segment.y === head.y)
) {
resetGame();
}
}
function drawSnake() {
snake.forEach(segment => drawRect(segment.x, segment.y, 'green'));
}
function drawFood() {
drawRect(food.x, food.y, 'red');
}
function drawScore() {
ctx.fillStyle = 'black';
ctx.font = '20px Arial';
ctx.fillText(`分数: ${score}`, 10, canvas.height - 10);
}
function gameLoop() {
clearScreen();
moveSnake();
drawSnake();
drawFood();
drawScore();
setTimeout(gameLoop, 100);
}
document.addEventListener('keydown', event => {
switch (event.key) {
case 'ArrowUp':
if (dy === 0) { dx = 0; dy = -1; }
break;
case 'ArrowDown':
if (dy === 0) { dx = 0; dy = 1; }
break;
case 'ArrowLeft':
if (dx === 0) { dx = -1; dy = 0; }
break;
case 'ArrowRight':
if (dx === 0) { dx = 1; dy = 0; }
break;
}
});
function resetGame() {
snake = [{ x: 10, y: 10 }];
dx = 0;
dy = 0;
food = getRandomFoodPosition();
score = 0;
}
gameLoop();
</script>
</body>
</html>
5.2 解释代码
HTML部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>贪吃蛇游戏</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f4f4f9;
}
canvas {
border: 1px solid #333;
background-color: #fff;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<!DOCTYPE html>: 声明文档类型为HTML5。<html lang="en">: 设置文档语言为英语。<meta charset="UTF-8">: 设置字符编码为UTF-8。<meta name="viewport" content="width=device-width, initial-scale=1.0">: 设置视口,使页面适应不同设备。<title>贪吃蛇游戏</title>: 设置页面标题。<style>: 内联CSS样式,设置页面布局和样式。<body>: 页面主体内容。<canvas id="gameCanvas" width="400" height="400"></canvas>: 创建一个画布元素,用于绘制游戏画面。
JavaScript部分
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const gridSize = 20;
const tileCount = canvas.width / gridSize;
let snake = [{ x: 10, y: 10 }];
let dx = 0;
let dy = 0;
let food = { x: 15, y: 15 };
let score = 0;
function getRandomFoodPosition() {
return {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
}
function drawRect(x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x * gridSize, y * gridSize, gridSize, gridSize);
}
function clearScreen() {
ctx.fillStyle = '#f4f4f9';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
function moveSnake() {
const head = { x: snake[0].x + dx, y: snake[0].y + dy };
snake.unshift(head);
if (head.x === food.x && head.y === food.y) {
score++;
food = getRandomFoodPosition();
} else {
snake.pop();
}
if (
head.x < 0 || head.x >= tileCount ||
head.y < 0 || head.y >= tileCount ||
snake.slice(1).some(segment => segment.x === head.x && segment.y === head.y)
) {
resetGame();
}
}
function drawSnake() {
snake.forEach(segment => drawRect(segment.x, segment.y, 'green'));
}
function drawFood() {
drawRect(food.x, food.y, 'red');
}
function drawScore() {
ctx.fillStyle = 'black';
ctx.font = '20px Arial';
ctx.fillText(`分数: ${score}`, 10, canvas.height - 10);
}
function gameLoop() {
clearScreen();
moveSnake();
drawSnake();
drawFood();
drawScore();
setTimeout(gameLoop, 100);
}
document.addEventListener('keydown', event => {
switch (event.key) {
case 'ArrowUp':
if (dy === 0) { dx = 0; dy = -1; }
break;
case 'ArrowDown':
if (dy === 0) { dx = 0; dy = 1; }
break;
case 'ArrowLeft':
if (dx === 0) { dx = -1; dy = 0; }
break;
case 'ArrowRight':
if (dx === 0) { dx = 1; dy = 0; }
break;
}
});
function resetGame() {
snake = [{ x: 10, y: 10 }];
dx = 0;
dy = 0;
food = getRandomFoodPosition();
score = 0;
}
gameLoop();
-
变量声明:
canvas: 获取画布元素。ctx: 获取绘图上下文。gridSize: 每个方格的大小。tileCount: 画布上的方格数量。snake: 蛇的身体,初始位置为(10, 10)。dx,dy: 蛇移动的方向。food: 食物的位置,初始位置为(15, 15)。score: 游戏得分,初始为0。
-
函数:
getRandomFoodPosition(): 随机生成食物的位置。drawRect(x, y, color): 在指定位置绘制矩形。clearScreen(): 清除整个画布。moveSnake(): 移动蛇的身体,处理吃到食物和撞墙的情况。drawSnake(): 绘制蛇的身体。drawFood(): 绘制食物。drawScore(): 显示当前得分。gameLoop(): 游戏主循环,每隔100毫秒执行一次。resetGame(): 重置游戏状态。
-
事件监听:
- 监听键盘按键事件,根据方向键改变蛇的移动方向。
-
启动游戏:
- 调用
gameLoop()开始游戏循环。
- 调用
6. 创建HTTP服务器
为了能够让其他设备通过局域网访问我们的贪吃蛇游戏,我们需要创建一个简单的HTTP服务器。
6.1 创建 server.js 文件
- 创建
server.js文件:- 在项目目录下创建一个新的文件
server.js。 - 使用你喜欢的文本编辑器打开该文件,并粘贴以下代码:
- 在项目目录下创建一个新的文件
const http = require('http');
const fs = require('fs');
const path = require('path');
const hostname = '0.0.0.0'; // 绑定所有网络接口
const port = 3000;
const server = http.createServer((req, res) => {
if (req.url === '/') {
fs.readFile(path.join(__dirname, 'index.html'), (err, data) => {
if (err) {
res.statusCode = 500;
res.setHeader('Content-Type', 'text/plain');
res.end('Internal Server Error');
return;
}
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
res.end(data);
});
} else {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Not Found');
}
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
6.2 解释代码
-
引入模块:
http: 创建HTTP服务器。fs: 文件系统模块,用于读取文件。path: 处理和转换文件路径。
-
服务器配置:
hostname: 设置为'0.0.0.0',表示绑定所有网络接口。port: 设置为3000,表示监听3000端口。
-
创建服务器:
http.createServer((req, res) => {...}): 创建HTTP服务器实例。if (req.url === '/') {...}: 当请求URL为根路径时,读取index.html文件并返回给客户端。res.statusCode = 200: 设置响应状态码为200(OK)。res.setHeader('Content-Type', 'text/html'): 设置响应头,指示内容类型为HTML。res.end(data): 发送文件内容作为响应体。else {...}: 如果请求URL不是根路径,则返回404错误。
-
启动服务器:
server.listen(port, hostname, () => {...}): 启动服务器,并在控制台输出服务器地址。
7. 启动服务器
现在,我们已经完成了所有的准备工作,可以启动服务器了。
-
启动服务器:
-
在命令行中输入以下命令并按回车:
node server.js -
你应该会看到类似以下的输出:
Server running at http://0.0.0.0:3000/
-
-
访问服务器:
- 打开浏览器,输入
http://localhost:3000/或http://127.0.0.1:3000/。 - 你应该能够看到并玩你的贪吃蛇游戏。
- 打开浏览器,输入
8. 查找本机IP地址
为了让其他设备通过局域网访问你的服务器,你需要知道你的本机IP地址。
查找方法
Windows
-
命令提示符:
-
打开“命令提示符”或“PowerShell”。
-
输入以下命令并按回车:
ipconfig -
找到“以太网适配器”或“无线局域网适配器”的“IPv4 地址”。例如:
IPv4 地址 . . . . . . . . . . . : 192.168.1.100
-
macOS
-
终端:
-
打开“终端”。
-
输入以下命令并按回车:
ifconfig -
找到
en0(Wi-Fi)或en1(有线)的“inet”字段后面的地址。例如:inet 192.168.1.100 netmask 0xffffff00 broadcast 192.168.1.255
-
Linux
-
终端:
-
打开“终端”。
-
输入以下命令并按回车:
ip addr show -
找到
wlan0(Wi-Fi)或eth0(有线)的“inet”字段后面的地址。例如:inet 192.168.1.100/24 brd 192.168.1.255 scope global dynamic wlan0
-
9. 通过局域网访问服务器
假设你的本机IP地址是 192.168.1.100,你可以通过以下方式让其他设备访问你的服务器:
-
在同一网络下的设备:
- 打开浏览器,输入
http://192.168.1.100:3000/并按回车。 - 你应该能够看到并玩你的贪吃蛇游戏。
- 打开浏览器,输入
-
防火墙设置:
- 确保你的防火墙允许外部设备访问3000端口。如果不允许,需要在防火墙设置中添加例外规则。
示例
假设你的本机IP地址是 192.168.1.100,以下是具体操作步骤:
-
启动服务器:
cd D:\snake_game_server node server.js -
查找本机IP地址:
- 使用上述方法找到你的本机IP地址,例如
192.168.1.100。
- 使用上述方法找到你的本机IP地址,例如
-
在另一台设备上访问:
- 打开另一台设备上的浏览器。
- 输入
http://192.168.1.100:3000/并按回车。 - 你应该能够看到并玩你的贪吃蛇游戏。
10. 总结
通过本文,我们从零开始创建了一个简单的贪吃蛇游戏,并将其部署到一个本地服务器上,使其可以通过局域网访问。我们学习了以下内容:
- 如何安装和验证Node.js。
- 如何创建和初始化一个Node.js项目。
- 如何编写HTML、CSS和JavaScript代码来实现贪吃蛇游戏。
- 如何使用Node.js创建一个简单的HTTP服务器。
- 如何查找本机IP地址并通过局域网访问服务器。
希望这篇文章对你有所帮助!如果你有任何问题或建议,请随时留言。祝你编程愉快!
附录
完整项目结构
D:\snake_game_server
├── index.html
├── package.json
└── server.js
完整代码
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>贪吃蛇游戏</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f4f4f9;
}
canvas {
border: 1px solid #333;
background-color: #fff;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const gridSize = 20;
const tileCount = canvas.width / gridSize;
let snake = [{ x: 10, y: 10 }];
let dx = 0;
let dy = 0;
let food = { x: 15, y: 15 };
let score = 0;
function getRandomFoodPosition() {
return {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
}
function drawRect(x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x * gridSize, y * gridSize, gridSize, gridSize);
}
function clearScreen() {
ctx.fillStyle = '#f4f4f9';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
function moveSnake() {
const head = { x: snake[0].x + dx, y: snake[0].y + dy };
snake.unshift(head);
if (head.x === food.x && head.y === food.y) {
score++;
food = getRandomFoodPosition();
} else {
snake.pop();
}
if (
head.x < 0 || head.x >= tileCount ||
head.y < 0 || head.y >= tileCount ||
snake.slice(1).some(segment => segment.x === head.x && segment.y === head.y)
) {
resetGame();
}
}
function drawSnake() {
snake.forEach(segment => drawRect(segment.x, segment.y, 'green'));
}
function drawFood() {
drawRect(food.x, food.y, 'red');
}
function drawScore() {
ctx.fillStyle = 'black';
ctx.font = '20px Arial';
ctx.fillText(`分数: ${score}`, 10, canvas.height - 10);
}
function gameLoop() {
clearScreen();
moveSnake();
drawSnake();
drawFood();
drawScore();
setTimeout(gameLoop, 100);
}
document.addEventListener('keydown', event => {
switch (event.key) {
case 'ArrowUp':
if (dy === 0) { dx = 0; dy = -1; }
break;
case 'ArrowDown':
if (dy === 0) { dx = 0; dy = 1; }
break;
case 'ArrowLeft':
if (dx === 0) { dx = -1; dy = 0; }
break;
case 'ArrowRight':
if (dx === 0) { dx = 1; dy = 0; }
break;
}
});
function resetGame() {
snake = [{ x: 10, y: 10 }];
dx = 0;
dy = 0;
food = getRandomFoodPosition();
score = 0;
}
gameLoop();
</script>
</body>
</html>
server.js
const http = require('http');
const fs = require('fs');
const path = require('path');
const hostname = '0.0.0.0'; // 绑定所有网络接口
const port = 3000;
const server = http.createServer((req, res) => {
if (req.url === '/') {
fs.readFile(path.join(__dirname, 'index.html'), (err, data) => {
if (err) {
res.statusCode = 500;
res.setHeader('Content-Type', 'text/plain');
res.end('Internal Server Error');
return;
}
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
res.end(data);
});
} else {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Not Found');
}
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
package.json
{
"name": "snake_game_server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
希望这篇详细的教程能帮助你顺利完成项目!如果有任何疑问,欢迎随时提问。

被折叠的 条评论
为什么被折叠?



