canvas+javascript 实现贪吃蛇游戏

news2025/1/20 3:51:08

引言

在当今数字化时代,编程已经成为一种极具创造力和趣味性的活动。通过编写代码,我们可以创造出各种各样的应用程序和游戏,其中包括经典的贪吃蛇游戏。本文将向您介绍如何使用 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.coloradmin.cn/o/1568719.html

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

相关文章

蓝桥杯刷题day13——玩游戏【算法赛】

一、问题描述 小 A 和小 B 两个人在海边找到了 n 个石子&#xff0c;准备开始进行一些游戏&#xff0c;具体规则如下&#xff1a;小 B 首先将 n 个石子分成若干堆&#xff0c;接下来从小 A 开始小 A 和小 B 轮流取石子&#xff0c;每次可以任选一堆石子取走任意个&#xff0c;…

huggingface使用git上传model或者Dataset到HF,当然也可以上传代码,以及大文件,很哇塞!!!

本地设置huggingface可写token&#xff08;仅需一次&#xff09; 第一次使用时&#xff0c;需要使用huggingface-cli设置系统全局的huggingface token&#xff0c;这个token必须是可写(write)权限的才能上传模型。 点击huggingface首页右上角的头像 → settings → access to…

洛谷_P1048 [NOIP2005 普及组] 采药_dp_python写法

P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) T, M map(int,input().split())dp [[0]*(T1) for _ in range(M1)]for i in range(1,M1):t, v map(int,input().split())for j in range(1,T1):if j<t:dp[i][j] dp[i-1][j]else:dp[i][j] ma…

蓝桥杯真题:路径

import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {int n 2022; //从下标为1开始&#xff0c;方便计算int[] q new int[n]; //存储最短路q[1] 0; //起始条件for (int i 2; i < 202…

[QOpenGLWidget+QMouseEvent]实时绘制长方形

复现moho-打卡第1天 - 20240402 1.1--QOpenGLWidget中显示长方形 实现方法&#xff1a;顶点着色器中给定长方形的四个顶点数据&#xff0c;代码如下&#xff1a; // 顶点位置 GLfloat vertics[1][4][3] { {{mousePressPosX,mousePressPosY,0.0},{mousePressPosX,mouseMoveP…

【大模型应用篇2】提示词实践-短剧文案

在上节课《【大模型应用篇1】学会对模型念咒语》带大家一起学习了提示词工程&#xff0c;我相信大部分朋友学完之后&#xff0c;还是有懵懂的&#xff0c;这节课带大家实操一下提示词的应用场景&#xff0c;现在短剧的创作很火&#xff0c;好看的短剧内容一定不会差&#xff0c…

Kubesphere 自动化部署失败报错

Kubesphere 自动化部署在 push tag 阶段失败报错 git push http://****:****github.com/****/devops-java-sample.git --tags --ipv4 remote: Support for password authentication was removed on August 13, 2021. remote: Please see https://docs.github.com/get-started/g…

中颖51芯片学习1. 系统时钟及IO口输出操作

中颖51芯片学习1. 系统时钟及IO口输出操作 一、芯片介绍1. 规格2. 资源3. 中断源&#xff1a;4. 方框图5. 封装&#xff08;1&#xff09;32脚LQFP封装&#xff08;2&#xff09;28脚SOP封装&#xff08;3&#xff09;20脚SOP封装 二、开发环境搭建1. 安装keil2. 安装中颖keil插…

鸿蒙实战开发:【实现应用悬浮窗】

如果你要做的是系统级别的悬浮窗&#xff0c;就需要判断是否具备悬浮窗权限。然而这又不是一个标准的动态权限&#xff0c;你需要兼容各种奇葩机型的悬浮窗权限判断。 fun checkPermission(context: Context): Boolean if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)…

江大白 | 万字长文,近3年Transformer在小目标检测领域,进展与突破系统梳理!

本文来源公众号“江大白”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;万字长文&#xff0c;近3年Transformer在小目标检测领域&#xff0c;进展与突破系统梳理&#xff01; 以下文章来源于微信公众号&#xff1a;AI视界引擎 …

WEB漏洞挖掘详细教程--用户输入合规性(sql注入测试)

前置教程&#xff1a;WEB漏洞挖掘&#xff08;SRC&#xff09;详细教程--信息收集篇-CSDN博客 WEB漏洞挖掘&#xff08;SRC&#xff09;详细教程--身份认证与业务一致性-CSDN博客 WEB漏洞挖掘&#xff08;SRC&#xff09;详细教程--业务数据篡改-CSDN博客 2.4 用户输入合规性…

C#学生信息管理系统

一、引言 学生信息管理系统是现代学校管理的重要组成部分&#xff0c;它能够有效地管理学生的基本信息、课程信息、成绩信息等&#xff0c;提高学校管理的效率和质量。本文将介绍如何使用SQL Server数据库和C#语言在.NET平台上开发一个学生信息管理系统的课程设计项目。 二、项…

risc-v向量扩展strlen方法学习

riscv向量文档中给出了strlen的实现&#xff0c; 大概是这么一个思路&#xff0c; 加载向量: 使用向量加载指令&#xff08;如 vload&#xff09;从内存中加载一个向量长度的字符。比较向量与零: 使用向量比较指令&#xff08;如 vmask 或 vcmpeq&#xff09;来检查向量中的每…

HTTPS RSA 握手解析(计算机网络)

传统的 TLS 握手基本都是使用 RSA 算法来实现密钥交换的&#xff0c;在将 TLS 证书部署服务端时&#xff0c;证书文件其实就是服务端的公钥&#xff0c;会在 TLS 握手阶段传递给客户端&#xff0c;而服务端的私钥则一直留在服务端。 在 RSA 密钥协商算法中&#xff0c;客户端会…

【generate】如何维护一套icon组件库,直接输出svg为react component

https://github.com/ant-design/ant-design-web3/pull/761/files 实现了icon-preview(通过jsdoc, 鼠标放在组件上可以看到icon的样式)&#xff0c;因为打包方式、产物以及命名上有一些不同&#xff0c;可能需要稍加改造。 这个同步脚本应该后续也用得上&#xff0c;略加改造同步…

装饰工程管理系统|基于Springboot的装饰工程管理系统设计与实现(源码+数据库+文档)

装饰工程管理系统-项目立项子系统目录 目录 基于Springboot的装饰工程管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、管理员功能实现 &#xff08;2&#xff09;合同报价管理 &#xff08;3&#xff09;装饰材料总计划管理 &#xff08;4&#xff0…

深度解析GPT中的Tokenizer

继学习完深度解析大语言模型中的词向量后&#xff0c;让我们继续学习大语言模型中另外几个重要概念&#xff1a;token&#xff08;词元&#xff09;、tokenization&#xff08;词元化&#xff09;、tokenizer&#xff08;词元生成器&#xff09;。 在GPT模型中&#xff0c;toke…

Minikube本地搭建单节点Kubernetes集群

1、什么是 Minikube Minikube 是一个开源工具&#xff0c;旨在为开发者提供一种便捷的方式在本地环境中搭建单节点的 Kubernetes 集群。它主要用于开发、测试和学习 Kubernetes 应用程序&#xff0c;无需依赖大型的硬件资源或复杂的多节点集群配置。minikube 使用轻量级虚拟化技…

小林coding图解计算机网络|基础篇02|键入网址到网页显示,期间发生了什么?

小林coding网站通道&#xff1a;入口 本篇文章摘抄应付面试的重点内容&#xff0c;详细内容还请移步&#xff1a;小林coding网站通道 文章目录 孤单小弟——HTTP真实地址查询——DNS指南好帮手——协议栈可靠传输——TCP远程定位——IP两点传输——MAC出口——网卡送别者——交…

如何优化TCP?TCP的可靠传输机制是什么?

在网络世界中&#xff0c;传输层协议扮演着至关重要的角色&#xff0c;特别是TCP协议&#xff0c;以其可靠的数据传输特性而广受青睐。然而&#xff0c;随着网络的发展和数据量的激增&#xff0c;传统的TCP协议在效率方面遭遇了挑战。小编将深入分析TCP的可靠性传输机制&#x…