【前端】使用 Canvas 实现贪吃蛇小游戏

news2024/12/25 13:39:47

使用 Canvas 实现贪吃蛇小游戏

在这篇博客中,我们将介绍如何使用 HTML5 Canvas 和 JavaScript 实现一个简单的贪吃蛇(Snake)小游戏。这个项目是一个基础的游戏开发练习,它可以帮助你理解如何在 Canvas 上绘图、如何处理用户输入以及如何管理游戏状态。
在这里插入图片描述

项目结构

在开始之前,确保你的项目文件结构如下:

  • index.html
  • index.css
  • index.js

HTML 部分

首先,我们在 index.html 中定义 Canvas 和一个重新开始按钮。

<!DOCTYPE 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="index.css">
</head>
<body>
    <canvas id="gameCanvas" width="400" height="400"></canvas>
    <button id="restartButton" style="display: none;" onclick="restartGame()">重新开始</button>
    <script src="index.js"></script>
</body>
</html>

CSS 部分

index.css 中,我们可以设置 Canvas 和按钮的样式。

/* styles.css */
body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #f0f0f0;
}

canvas {
    border: 1px solid #000;
    background-color: #fff;
}

button {
    display: none;
    margin-top: 20px;
    padding: 10px 20px;
    font-size: 16px;
    cursor: pointer;
    position: absolute;
}

JavaScript 部分

接下来,我们在 script.js 中编写游戏的主要逻辑。

1. 初始化 Canvas 和变量

我们首先获取 Canvas 元素及其上下文,并定义游戏所需的一些变量。

const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
const restartButton = document.getElementById("restartButton");

const gridSize = 20; // 格子大小
const rows = canvas.height / gridSize;
const cols = canvas.width / gridSize;

2. 初始化游戏

创建 initGame 函数来初始化游戏状态:

function initGame() {
  snake = [{ x: cols / 2, y: rows / 2 }, { x: cols / 2 + 1, y: rows / 2 }];
  food = {
    x: Math.floor(Math.random() * cols),
    y: Math.floor(Math.random() * rows),
  };
  direction = { x: 1, y: 0 };
  lastDirection = { x: 1, y: 0 };
  gameOver = false;
  restartButton.style.display = "none";
  gameLoop();
}

3. 绘制游戏元素

我们需要两个函数来绘制蛇和食物:

function drawCell(x, y, color) {
  ctx.fillStyle = color;
  ctx.fillRect(x * gridSize, y * gridSize, gridSize, gridSize);
  ctx.strokeStyle = "#000";
  ctx.lineWidth = 2; // 可以根据需要调整
  ctx.strokeRect(x * gridSize, y * gridSize, gridSize, gridSize);
}

function drawSnake() {
  snake.forEach((part) => drawCell(part.x, part.y, "#409EFF"));
}

function drawFood() {
  drawCell(food.x, food.y, "#E6A23C");
}

4. 更新蛇的位置

创建 updateSnake 函数来更新蛇的位置,并检查它是否吃到食物或撞到墙及自身:

function updateSnake() {
  const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y };

  if (head.x === food.x && head.y === food.y) {
    food = {
      x: Math.floor(Math.random() * cols),
      y: Math.floor(Math.random() * rows),
    };
  } else {
    snake.pop();
  }

  snake.unshift(head);

  // 撞墙或撞到自己
  if (
    head.x < 0 ||
    head.x >= cols ||
    head.y < 0 ||
    head.y >= rows ||
    snake.slice(1).some((part) => part.x === head.x && part.y === head.y)
  ) {
    gameOver = true;
  }

  lastDirection = direction;
}

5. 游戏循环

创建 gameLoop 函数来处理游戏的绘制和更新:

function gameLoop() {
  if (!gameOver) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    drawFood();
    updateSnake();
    drawSnake();
    setTimeout(gameLoop, 100);
  } else {
    ctx.font = "100px Arial";
    ctx.fillStyle = "#F56C6C";
    ctx.fillText("Game Over", canvas.width / 4, canvas.height / 2);
    restartButton.style.display = "block"; // 显示重新开始按钮
  }
}

6. 处理用户输入

添加键盘事件监听来控制蛇的移动方向:

window.addEventListener("keydown", (event) => {
  switch (event.key) {
    case "ArrowUp":
      if (lastDirection.y === 0) direction = { x: 0, y: -1 };
      break;
    case "ArrowDown":
      if (lastDirection.y === 0) direction = { x: 0, y: 1 };
      break;
    case "ArrowLeft":
      if (lastDirection.x === 0) direction = { x: -1, y: 0 };
      break;
    case "ArrowRight":
      if (lastDirection.x === 0) direction = { x: 1, y: 0 };
      break;
  }
});

7. 重新开始游戏

最后,创建一个函数用于重新启动游戏:

function restartGame() {
  initGame();  // 重新启动游戏
}

initGame();  // 启动游戏

总结

通过这篇博客,你学习了如何使用 HTML5 Canvas 和 JavaScript 来实现一个简单的贪吃蛇小游戏。我们展示了如何绘制游戏元素,处理用户输入,并管理游戏状态。希望这个项目能帮助你对游戏开发有更深入的理解,并激发你进行更多有趣的项目开发!

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

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

相关文章

【Spring security】【pig】Note03-pig token令牌解析器过程

&#x1f338;&#x1f338; pig token令牌解析器过程 &#x1f338;&#x1f338; pig后端源码 一、解析请求中的令牌值。 二、验证令牌 内省并验证给定的令牌&#xff0c;返回其属性。返回映射表示令牌有效。 /*** author lengleng* date 2019/2/1 扩展用户信息*/ publi…

重新安装vmware与再次编译u-boot

一、使用环境&#xff1a; 使用vmware 16pro安装 ubuntu18.04桌面版 二、遇到的问题与解决&#xff1a; 1&#xff09;、无法连网&#xff1a; 保持nat模式&#xff0c;移除再添加。 2&#xff09;、git配置私钥&#xff1a; 如果是拉取自己的仓库&#xff0c;请查看此步&am…

电路笔记 :元器件焊接相关 酒精灯松香浴加热取芯片

记录一下只使用松香和小火源加热&#xff08;如酒精灯、小蜡烛&#xff09;从电路板中取芯片。 过程 多放松香 让松香淹没芯片尽量均匀加热&#xff0c;等芯片旁边的松香开始从芯片里冒细小的“泡泡”&#xff0c;就差不多了 注&#xff1a;这种方法也可以用于焊接&#xff0…

232COM串口通讯读写NFC卡C#示例源码

本示例使用的发卡器&#xff1a;RS232串口RFID NFC IC卡读写器可二次开发编程发卡器USB转COM-淘宝网 (taobao.com) using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using Syste…

【技术实操】中标麒麟高级服务器操作系统实例分享,rsync数据同步配置方案

1.rsync介绍 rsync是一款开源的、快速的、多功能的、可实现全量及增量的本地或远程数据同步备份工具。 在守护进程模式&#xff08;daemon mode&#xff09;下&#xff0c;rsync默认监听TCP端口873&#xff0c;以原生rsync传输协议或者通过远程shell如RSH或者SSH提供文件。SS…

App Inventor 2 如何接入ChatGPT:国内访问OpenAI的最佳方式

如何接入OpenAI 由于国内无法访问OpenAI&#xff0c;KX上网可选大陆及香港&#xff08;被屏蔽&#xff09;以外才行。因此对于大多数人来说&#xff0c;想体验或使用ChatGPT就不太便利&#xff0c;不过App Inventor 2 为我们提供了相对便利的一种方式&#xff0c;即“试验性质…

快速搭建SpringMvc项目

一、什么是springMvc 1、介绍 Spring Web MVC是基于Servlet API构建的原始Web框架&#xff0c;从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块的名称&#xff08; spring-webmvc &#xff09;&#xff0c;但它通常被称为“Spring MVC”。 在控制…

MVS net笔记和理解

文章目录 传统的方法有什么缺陷吗&#xff1f;MVSnet深度的预估 传统的方法有什么缺陷吗&#xff1f; 传统的mvs算法它对图像的光照要求相对较高&#xff0c;但是在实际中要保证照片的光照效果很好是很难的。所以传统算法对镜面反射&#xff0c;白墙这种的重建效果就比较差。 …

京准电子、NTP网络时间服务器工作原理及应用领域分析

京准电子、NTP网络时间服务器工作原理及应用领域分析 京准电子、NTP网络时间服务器工作原理及应用领域分析 Network Time Protocol&#xff08;NTP&#xff09;是一种用于在计算机网络中同步时钟的协议。它旨在确保在网络中的各个设备之间维持准确的时间。NTP的设计目标是允许…

qmt量化交易策略小白学习笔记第10期【qmt编程之获取股票订单流数据--内置Python】

qmt编程之获取股票订单流数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 感谢关注&#xff0c;需免费开通量化回测与咨询实盘权限&#xff0c;可以和博主联系&#xff01; 获取股票订单流…

鹧鸪云光伏业务管理软件:引领光伏行业四个信息化变革

随着全球对可再生能源的日益重视和光伏技术的快速发展&#xff0c;光伏业务管理正面临着前所未有的挑战与机遇。在这个大背景下&#xff0c;鹧鸪云光伏业务管理软件以其独特的优势&#xff0c;通过四个信息化变革&#xff0c;为光伏行业带来了前所未有的效率提升和智能化管理。…

hypermesh二次开发tcl脚本

hypermesh二次开发tcl脚本 1、tcl读csv文件,得到list,再转成二维数组2、tcl写csv文件3、hypermesh tcl根据读入的节点坐标建节点, 再显示节点号4、hypermesh tcl根据节点号建节点set5、hypermesh tcl根据节点set读取节点号&#xff0c;再根据节点号读取节点坐标&#xff0c;再将…

vue 点击复制文本到剪贴板

一、首先在vue文件的template中定义复制按钮 <div size"small" v-if"item.prop jadeCode" class"cell-container"><span>{{ scope.row.jadeCode }}</span> <button click"handleCopy(scope.row.jadeCode)" clas…

FreeRTOS_事件组_学习笔记

事件组 原文链接 事件组是一个整数&#xff0c;其中的高8位留给内核&#xff0c;只能用其他位来表示时间 每一位代表一个事件&#xff0c;且每个时间的含义由程序员决定 1为发生&#xff0c;0为未发生 一个/多个任务或ISR都能读写这些位 可以等待某一位&#xff0c;也可以等待…

详细分析Element中的Drawer(附Demo)

目录 前言1. 基本知识2. Demo2.1 基本用法2.2 不同方向2.3 自定义大小2.4 嵌入表单2.5 嵌套抽屉 3. 实战4. Element Plus&#xff08;Drawer&#xff09; 前言 对于该组件针对Vue2比较多&#xff0c;而Element Plus中的Drawer针对Vue3比较多 此处的Demo主要偏向Vue2 后续的El…

五一期间吉祥桥再创新高,抢占宴席酒市场高位

“人生过三桥&#xff0c;必喝吉祥桥”“古韵吉祥桥&#xff0c;今喜结良缘”“吉祥桥下酒香飘&#xff0c;东方韵味醉心田”…… 这个五一&#xff0c;吉祥桥酒商的朋友圈热闹起来了&#xff0c;一边刷屏式宣传自家主打的宴席产品&#xff0c;一边炫耀式的发布每天的宴席成绩…

08.CNN

文章目录 Observation 1Pooling - Max PoolingFlattenApplication&#xff1a;Playing Go使用验证集选择模型食物分类 Observation 1 Pooling - Max Pooling Pooling主要为了降低运算量&#xff0c;现在一般不用了&#xff0c;全convolution Flatten Application&#xff1a;P…

一文了解安卓内存抖动

目录 目录一、什么是内存抖动&#xff1f;1.1 Android里的内存抖动1.2 如何直观查看这种现象1.3 内存抖动带来的风险 二、如何避免内存抖动 目录 一、什么是内存抖动&#xff1f; 在程序里&#xff0c;每创建一个对象&#xff0c;就会有一块内存分配给它&#xff0c;每分配一…

使用 RisingWave 和 Redash 处理和可视化实时数据

在创建流处理管道时&#xff0c;需要两个关键组件&#xff1a;一个用于处理和转换数据&#xff0c;一个用于数据可视化。RisingWave 和 Redash 就提供了一个优秀的解决方案。 RisingWave 是一个支持实时数据处理的分布式 SQL 流数据库。它提供增量更新的物化视图&#xff0c;使…

【MySQL精通之路】MySQL8.0新增功能-原子DDL语句支持

太长不看系列&#xff1a; 本文一句话总结&#xff0c;MySQL8.0支持多条DDL语句执行时的原子性了&#xff08;仅限Innodb&#xff09; 本文属于下面这篇博客的子博客&#xff1a; 【MySQL精通之路】MySQL8.0官方文档-新增功能 1.意义描述 MySQL 8.0支持原子数据定义语言&…