canvas+javascript 实现贪吃蛇游戏

引言

在当今数字化时代,编程已经成为一种极具创造力和趣味性的活动。通过编写代码,我们可以创造出各种各样的应用程序和游戏,其中包括经典的贪吃蛇游戏。本文将向您介绍如何使用 JavaScript 编程语言制作一个简单而有趣的贪吃蛇游戏,并通过代码分析和解释帮助您了解游戏的实现原理。

一、游戏背景

       贪吃蛇是一款经典的街机游戏,早在 Nokia 手机时代就备受欢迎。玩家控制一条蛇在一个有限的空间内移动,吃掉食物以增加长度,同时要避免撞到墙壁或自身。本文将使用 HTML5 的 Canvas 元素和 JavaScript 语言来实现这个经典游戏的简化版本。

二、游戏功能及实现

       首先,我们需要定义一些游戏所需的变量,如蛇的初始位置、食物的位置、移动速度等。接着,我们监听键盘事件,根据用户按键改变蛇的移动方向。蛇的移动是通过周期性地更新蛇头位置并移除蛇尾来实现的。当蛇吃到食物时,增加得分并重新生成食物位置;当蛇头碰到墙壁或自身时,游戏结束。

三、代码分析 

3.1 html代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>贪吃蛇游戏</title><link rel="stylesheet" href="style.css" /></head><body><div class="container"><div class="header"><div>难度:<span id="speed"></span></div><div>分数:<span id="score"></span></div></div><canvas id="gameCanvas" width="400" height="400"></canvas></div><script src="game.js"></script></body>
</html>

3.2 css代码

body {display: flex;justify-content: center;align-items: center;height: 100vh;margin: 0;background-color: #212121;
}.header {background-color: #266e61;color: #f9f9f9;display: flex;justify-content: space-between;padding: 10px 40px;border-radius: 10px;margin-bottom: 10px;
}span {font-size: 20px;
}canvas {background-color: #353535;border: 1px solid black;border-radius: 10px;padding: 10px;
}

3.3 JavaScript核心代码

我们将代码分为几个主要功能函数:

  • changeDirection(event):监听键盘事件,根据按键改变蛇的移动方向。
function changeDirection(event) {const keyPressed = event.key; // 获取按下的键值// 根据按键改变蛇的移动方向,确保蛇不会向相反方向移动if (keyPressed === "ArrowUp" && dy === 0) {dx = 0;dy = -gridSize;} else if (keyPressed === "ArrowDown" && dy === 0) {dx = 0;dy = gridSize;} else if (keyPressed === "ArrowLeft" && dx === 0) {dx = -gridSize;dy = 0;} else if (keyPressed === "ArrowRight" && dx === 0) {dx = gridSize;dy = 0;}
}
  • moveSnake():更新蛇的位置,处理吃食物和碰撞检测逻辑。
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) {food = getRandomPosition(); // 重新生成食物位置score += 10; // 增加得分document.getElementById("score").textContent = score; // 更新得分显示// 每吃到50分,增加游戏速度if (score % 50 === 0) {speed += 1; // 增加速度document.getElementById("speed").textContent = speed; // 更新速度显示}} else {snake.pop(); // 如果没有吃到食物,移除蛇尾,实现蛇的移动效果}// 如果蛇碰到墙壁或者自身,游戏结束if (head.x < 0 ||head.x >= canvas.width ||head.y < 0 ||head.y >= canvas.height ||collision()) {gameOver = true; // 设置游戏结束标志为true}
}
  • drawSnake() 和 drawFood():绘制蛇和食物。   
function drawSnake() {// 绘制蛇身snake.forEach((segment) => {ctx.fillStyle = "#266e5f"; // 设置蛇的颜色ctx.fillRect(segment.x, segment.y, gridSize, gridSize); // 绘制蛇的每一段});
}
  • collision():检测蛇头是否与蛇身相撞。
function collision() {// 检测蛇头是否与蛇身相撞return snake.slice(1).some((segment) => segment.x === snake[0].x && segment.y === snake[0].y);
}
  • drawGrid():绘制游戏网格。
function drawGrid() {// 绘制游戏网格for (let x = 0; x < canvas.width; x += gridSize) {for (let y = 0; y < canvas.height; y += gridSize) {ctx.strokeStyle = "black"; // 设置边框颜色ctx.lineWidth = gridBorderWidth; // 设置边框宽度ctx.strokeRect(x, y, gridSize, gridSize); // 绘制方格边框}}
}

3.4 整体代码

const canvas = document.getElementById("gameCanvas"); // 获取画布元素
const ctx = canvas.getContext("2d"); // 获取2D绘图上下文
const gridSize = 20; // 网格大小
const gridBorderWidth = 1; // 新增的边框宽度
let snake = [{ x: 200, y: 200 }]; // 蛇的初始位置
let food = getRandomPosition(); // 随机生成食物位置
let dx = gridSize; // 蛇的水平移动速度
let dy = 0; // 蛇的垂直移动速度
let gameOver = false; // 游戏结束标志
let speed = 1; // 游戏速度
let score = 0; // 得分init(); //初始化
document.addEventListener("keydown", changeDirection); // 监听键盘按下事件,改变蛇的移动方向function init() {document.getElementById("score").textContent = score;document.getElementById("speed").textContent = speed;
}function changeDirection(event) {const keyPressed = event.key; // 获取按下的键值// 根据按键改变蛇的移动方向,确保蛇不会向相反方向移动if (keyPressed === "ArrowUp" && dy === 0) {dx = 0;dy = -gridSize;} else if (keyPressed === "ArrowDown" && dy === 0) {dx = 0;dy = gridSize;} else if (keyPressed === "ArrowLeft" && dx === 0) {dx = -gridSize;dy = 0;} else if (keyPressed === "ArrowRight" && dx === 0) {dx = gridSize;dy = 0;}
}function drawSnake() {// 绘制蛇身snake.forEach((segment) => {ctx.fillStyle = "#266e5f"; // 设置蛇的颜色ctx.fillRect(segment.x, segment.y, gridSize, gridSize); // 绘制蛇的每一段});
}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) {food = getRandomPosition(); // 重新生成食物位置score += 10; // 增加得分document.getElementById("score").textContent = score; // 更新得分显示// 每吃到50分,增加游戏速度if (score % 50 === 0) {speed += 1; // 增加速度document.getElementById("speed").textContent = speed; // 更新速度显示}} else {snake.pop(); // 如果没有吃到食物,移除蛇尾,实现蛇的移动效果}// 如果蛇碰到墙壁或者自身,游戏结束if (head.x < 0 ||head.x >= canvas.width ||head.y < 0 ||head.y >= canvas.height ||collision()) {gameOver = true; // 设置游戏结束标志为true}
}function drawFood() {ctx.fillStyle = "#FFF"; // 设置食物的颜色ctx.fillRect(food.x, food.y, gridSize, gridSize); // 绘制食物
}function getRandomPosition() {// 随机生成食物的位置,确保在网格内return {x: Math.floor(Math.random() * (canvas.width / gridSize)) * gridSize,y: Math.floor(Math.random() * (canvas.height / gridSize)) * gridSize,};
}function collision() {// 检测蛇头是否与蛇身相撞return snake.slice(1).some((segment) => segment.x === snake[0].x && segment.y === snake[0].y);
}function drawGrid() {// 绘制游戏网格for (let x = 0; x < canvas.width; x += gridSize) {for (let y = 0; y < canvas.height; y += gridSize) {ctx.strokeStyle = "black"; // 设置边框颜色ctx.lineWidth = gridBorderWidth; // 设置边框宽度ctx.strokeRect(x, y, gridSize, gridSize); // 绘制方格边框}}
}function draw() {// 主绘制函数ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空画布drawGrid(); // 绘制游戏网格drawSnake(); // 绘制蛇身drawFood(); // 绘制食物moveSnake(); // 移动蛇// 如果游戏结束,显示游戏结束文字if (gameOver) {ctx.fillStyle = "#FFF";ctx.font = "35px Arial";ctx.fillText("游戏 结束", canvas.width / 2 - 65, canvas.height / 2);return;}setTimeout(draw, 1000 / speed); // 根据速度调整 setTimeout 的时间间隔,实现游戏速度控制
}draw(); // 开始游戏

四、游戏画面展示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/784165.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动态内存管理-错题合集讲解

空指针的解应用操作&#xff08;错误信息合集&#xff09; 越界访问 首先我们上一个代码&#xff0c;看看这个的代码的问题 这个代码的问题显而易见 &#xff0c;就是在循环里面&#xff0c;产生了越界访问的问题&#xff0c;这里你开辟了10个整形空间&#xff0c;但是从0-1…

【javaWeb 第九篇】功能接口开发流程以及常用注解

常用注解 准备-环境搭建开发规范开发流程 注解补充 准备-环境搭建 准备数据库表&#xff08;dept,emp&#xff09;准备后端SpringBoot环境 需要依赖&#xff1a; Web起步依赖&#xff0c;数据库驱动依赖&#xff0c;Mybatis依赖&#xff0c;lombok依赖配置文件application.pr…

9.Python类与对象

1 面向对象 类和对象都是面向对象中的重要概念。面向对象是一种编程思想&#xff0c; 即按照真实世界的思维方式构建软件系统。 例如&#xff0c;在真实世界的校园里有学生和老师&#xff0c;学生有学号、姓名、所 在班级等属性&#xff08;数据&#xff09;&#xff0c;还有…

MySQL核心命令详解与实战,一文掌握MySQL使用

文章目录 文章简介演示库表创建数据库表选择数据库删除数据库创建表删除表向表中插入数据更新数据删除数据查询数据WHERE 操作符聚合函数LIKE 子句分组 GROUP BY HAVINGORDER BY(排序) 语句LIMIT 操作符 分页查询多表查询-联合查询 UNION 操作符多表查询-连接的使用-JOIN语句编…

本地GPU调用失败问题解决3重新配置anaconda环境(成功)

1、右键“以管理员身份”打开anaconda prompt conda create -n python 3.9 2、使用官方下载源的配置 3、修改conda下载超时 conda config --set remote_connect_timeout_secs 60 conda config --set remote_read_timeout_secs 100 查看配置结果conda config --show 配置内…

122、内网安全——域信息收集应用网络凭据CS插件AdfindBloodHound

文章目录 理解域域信息搜集 理解域 假设有1000台计算机&#xff0c;运维人员需要为每一台计算机进行软件的安装、环境部署&#xff0c;实际上运维人员不可能亲自对每一台计算机进行软件的安装和环境部署。实际&#xff0c;将所有1000台计算机放入一个域中&#xff0c;域内有一…

多传感器标定——相机内参标定

文章目录 一、前言二、内参标定流程三、如何提升标定精度四、精度验证五、内外参联合标定 一、前言 之前写过一篇文章&#xff08;相机内参、外参、畸变系数简介&#xff09;&#xff0c;感觉应该把这几个东西说的还算明白&#xff0c;但是里边并没有深究该如何进行标定&#…

牛客NC153 信封嵌套问题【中等 动态规划,最长递增子序列 Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/9bf77b5b018d4d24951c9a7edb40408f 相同的题目&#xff1a; https://www.lintcode.com/problem/602 思路 本质是求最长子序列问题envelopes 先按 w 升序排序&#xff0c;再按 h 降序 排序&#xff0c;只需考虑h…

一条SQL在MySQL中的执行过程

图解&#xff1a; 第⼀步&#xff1a;连接器 过程 1. 建⽴连接&#xff1a;与客户端进⾏ TCP 三次握⼿建⽴连接&#xff1b; 2. 校验密码&#xff1a;校验客户端的⽤户名和密码&#xff0c;如果⽤户名或密码不对&#xff0c;则会报错&#xff1b;3. 权限判断&#xff1a…

手机无线投屏到windows11电脑

1 安装无线投影组件 2 电脑端打开允许其他设备投影的开关 3 手机找到投屏选项 4 手机搜索可用设备连接即可 这里的官方文档给的不太好,给了一些让人眼花撩乱的信息,以下是经过整合的有效信息

金融衍生品市场

金融衍生品市场 衍生金融品的作用衍生金融工具远期合约期货合约期权 衍生金融品的作用 套期保值&#xff08;Hedging&#xff09; 组合多头头寸(long position)与空头头寸(short position)例&#xff1a;股票与股指期货 投机 衍生金融工具 远期合约 定义&#xff1a;在将来…

翻译: 硅谷软件工程师面试:准备所需的一切

没有人有时间去做成百上千道LeetCode题目&#xff0c;好消息是你实际上并不需要做那么多题目就能够在FAANG公司找到工作&#xff01; 我曾经在Grab工作&#xff0c;这是东南亚的一家共享出行公司&#xff0c;但我对工作感到沮丧&#xff0c;想要进入FAANG公司&#xff0c;但我…

【opencv】教程代码 —features2D(5)旋转相机的基本全景拼接

基本全景拼接 panorama_stitching_rotating_camera.cpp 将第二张图像进行透视变换后与第一张图像拼接 #include <iostream> // 包含了一些用于输入输出的函数 #include <opencv2/core.hpp> // 包含了OpenCV核心库的一些常用类和函数 #include <opencv2/imgpro…

Android视角看鸿蒙第十课-鸿蒙的布局之线性布局

Android视角看鸿蒙第十课-鸿蒙的布局之线性布局 导读 这篇文章开始&#xff0c;依次学习鸿蒙的八大布局&#xff0c;这是第一篇&#xff0c;所以顺带也会聊聊通用属性。 文档地址 文档地址 如何定义一个线性布局 Android中是使用LinearLayout来构建线性布局的&#xff0c…

ChatGPT chrome扩展下载与安装

官方下载地址 https://chromewebstore.google.com/detail/lpbhmlbicmgjpacbofijdfpcplfhakeo 截图 安装 离线安装 下载地址 https://static.xutongbao.top/app/chatgpt-chrome-crx-v0.0.7.zip 打开链接 chrome://extensions/ 人工智能学习网站 https://chat.xutongbao.to…

谷粒商城——RabbitMQ

0. 消息中间件 1.RabbitMQ的核心概念 2. 工作流程 整体架构&#xff1a; 相关细节&#xff1a; 上述要注意的是&#xff1a; 路由键包含在message的头中&#xff0c;其作用是用于指定该消息存储与哪个消息队列中。 信道是客户端&#xff08;包括生产者和消费者&#xff09;用…

Vue 组件化编程

Vue 组件化编程 非单文件组件 定义组件 使用Vue.extend(options&#xff09;创建 不要写eldata要写成函数&#xff0c;避免组件被复用时&#xff0c;数据存在引用关系 注册组件 局部注册&#xff1a;new Vue()的时候&#xff0c;options传入components全局注册&#xff1a;V…

回收站删除以后还能撤销吗 回收站删除以后怎么找回 回收站清空了怎么恢复 easyrecovery数据恢复软件

回收站删除以后能撤销吗&#xff1f;有不少网友前一秒清空回收站&#xff0c;后一秒就开始在网上疯狂搜寻如何撤销删除回收站的办法。实际上&#xff0c;清空回收站并不可怕&#xff0c;被删除的数据仍然保存在我们的电脑硬盘中。今天我为大家介绍回收站删除以后怎么找回数据的…

HTTP 常见面试题(计算机网络)

HTTP 基本概念 一、HTTP 是什么&#xff1f; HTTP(HyperText Transfer Protocol) &#xff1a;超文本传输协议。 HTTP 是一个在计算机世界里专门在「两点」之间「传输」文字、图片、音频、视频等「超文本」数据的「约定和规范」。 「HTTP 是用于从互联网服务器传输超文本到本…

苍穹外卖04 (新增内表的外键id获取,多表分页查询,多表批量删除,修改先查在改内表外键id用主表的,起售时包含了“停售”状态的外关联表)

1. 新增套餐 1 需求分析和设计 业务规则&#xff1a; 套餐名称唯一 套餐必须属于某个分类 套餐必须包含菜品 名称、分类、价格、图片为必填项 添加菜品窗口需要根据分类类型来展示菜品 新增的套餐默认为停售状态 2 代码实现 1 根据分类id查询菜品 DishControllerGetMa…