首页 > 作文

手把手教学h5小游戏 – 贪吃蛇

更新时间:2023-04-03 09:40:08 阅读: 评论:0

简单的小游戏制作,代码量只有两三百行。游戏可自行扩展延申。

源码已发布至github,喜欢的点个小星星,源码入口:

游戏已发布,游戏入口:

第一步 – 制作想法

游戏如何实现是首要想的,这里我的想法如下:

利用canvas进行绘制地图(格子装)。利用canvas绘制蛇,就是占用地图格子。让蛇移动,即:更新蛇坐标,重新绘制。创建四个方向按钮,控制蛇接下来的方向。随机在地图上绘制出果子,蛇移动时“吃”到果子,增加长度和“移速”。开始键和结束键配置,分数显示、历史记录

第二步 – 框架选型

从第一步可知,我想实现这个游戏,只需要用到canvas绘制就可以了,没有物理引擎啥的,也没有高级的ui特效。可以选个简单点的,用来方便操作canvas绘制。精挑细选后选的是ealjs,比较轻量,用于绘制canvas,以及canvas的动态效果。

第三步 – 开发

准备

目录和文件准备:

| – index.html

| – js

| – | – main.js

| – css

| – | – stylesheet.css

index.html 导入相关的依赖,以及样式文件和脚本文件。设计是屏幕80%高度为canvas绘制区域,20%高度是操作栏以及展示分数区域.

<!doctype html><html lang="zh"><head>  <meta chart="utf-8">  <meta name="viewport"    content="width=device-width, initial-scale=1.0">  <meta http-equiv="x-ua-compatible"    content="ie=edge">  <title>贪吃蛇</title>  <link rel="stylesheet" href="css/stylesheet.css">  <meta name="viewport"    content="width=device-width, ur-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui"></head><body>  <div id="app">    <div class="content-canvas">      <canvas></canvas>    </div>    <div class="control">    </div>  </div>  <script src="https://cdn.bootcss.com/ealjs/1.0.2/ealjs.min.js"></script>  <!-- 载入jquery 方便dom操作 -->  <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>  <!-- sweetalert 美化alert用的 -->  <script src="https://cdn.bootcss.com/sweetalert/2.1.2/sweetalert.min.js"></script>  <script src="js/main.js"></script></body></html>

stylesheet.css

* {  padding: 0;  margin: 0;}body {  position: fixed;  width: 100%;  height: 100%;}#app {  max-width: 768px;  margin-left: auto;  margin-right: auto;}/* canvas绘制区域 */.content-canvas {  width: 100%;  max-width: 768px;  height: 80%;  position: fixed;  overflow: hidden;}.content-canvas canvas {  position: absolute;  width: 100%;  height: 100%;}/* 操作区域 */.control {  position: fixed;  width: 100%;  max-width: 768px;  height: 20%;  bottom: 0;  background-color: #aeff5d;}

main.js

$(function() {  // 主代码编写区域})

1.绘制格子

注意的点(遇到的问题以及解决方案):

canvas绘制的路线是无宽度的,但线条是有宽度的。比如:从(0, 0)到(0, 100)绘制一条宽度为10px的线,则线条一半是在区域外看不见的。处理方案是起点偏移,比如:从(0, 0)到(0, 100)绘制一条宽度为10px的线,改为从(5,0)到(5,100),偏移量为线条宽度的一半。用样式定义canvas的宽高坐标会被拉伸,处理方案是给canvas元素设置宽高属性,值为它当前的实际宽高。

代码

main.js

$(function () {  var line_width = 1 // 线条宽度  var line_max_num = 32 // 一行格子数量  var canvasheight = $('canvas').height() // 获取canvas的高度  var canvaswidth = $('canvas').width() // 获取canvas的宽度  var gridwidth = (canvaswidth - line_width) / line_max_num // 格子宽度,按一行32个格子计算  var num = { w: line_max_num, h: math.floor((canvasheight - line_width) / gridwidth) } // 计算横向和纵向多少个格子,即:横坐标的最大值和纵坐标的最大值  /** * 绘制格子地图 * @param graphics */  function drawgrid(graphics) {    var wnum = num.w    var hnum = num.h    graphics.tstrokestyle(line_width).beginstroke('#ffac52')    // 画横向的线条    for (var i = 0; i <= hnum; i++) {      if (i === hnum || i === 0) graphics.tstrokestyle(line_width)      if (i === 1) graphics.tstrokestyle(0.1)      graphics.moveto(line_width / 2, i * gridwidth + line_width / 2)        .lineto(gridwidth * wnum + line_width / 2, i * gridwidth + line_width / 2)    }    graphics.tstrokestyle(line_width)    // 画纵向的线条    for (i = 0; i <= wnum; i++) {      if (i === wnum || i === 0) graphics.tstrokestyle(line_width)      if (i === 1) graphics.tstrokestyle(.1)      graphics.moveto(i * gridwidth + line_width / 2, line_width / 2)        .lineto(i * gridwidth + line_width / 2, gridwidth * hnum + line_width / 2)    }  }  function init() {    $('canvas').attr('width', canvaswidth) // 给canvas设置宽高属性赋值上当前canvas的宽度和高度(单用样式配置宽高会被拉伸)    $('canvas').attr('height', canvasheight)    var stage = new createjs.stage($('canvas')[0])    var grid = new createjs.shape()    drawgrid(grid.graphics)    stage.addchild(grid)    stage.update()  }  init()})

效果图

浏览器打开index.html,可以看到效果:

2.绘制蛇

蛇可以想象成一串坐标点(数组),“移动时”在数组头部添加新的坐标,去除尾部的坐标。类似队列,先进先出。

代码

main.js

$(function () {  var line_width = 1 // 线条宽度  var line_max_num = 32 // 一行格子数量  var snake_start_point = [[0, 3], [1, 3], [2, 3], [3, 3]] // 初始蛇坐标  var dir_enum = { up: 1, down: -1, left: 2, right: -2 }    // 移动的四个方向枚举值,两个对立方向相加等于0  var game_state_enum = { end: 1, ready: 2 } // 游戏状态枚举  var canvasheight = $('canvas').height() // 获取canvas的高度  var canvaswidth = $('canvas').width() // 获取canvas的宽度  var gridwidth = (canvaswidth - line_width) / line_max_num // 格子宽度,按一行32个格子计算  var num = { w: line_max_num, h: math.floor((canvasheight - line_width) / gridwidth) } // 计算横向和纵向多少个格子,即:横坐标的最大值和纵坐标的最大值  var directionnow = null // 当前移动移动方向  var directionnext = null // 下一步移动方向  var gamestate = null // 游戏状态  /** * 绘制格子地图 * @param graphics */  function drawgrid(graphics) {    var wnum = num.w    var hnum = num.h    graphics.tstrokestyle(line_width).beginstroke('#ffac52')    // 画横向的线条    for (var i = 0; i <= hnum; i++) {      if (i === hnum || i === 0) graphics.tstrokestyle(line_width)      if (i === 1) graphics.tstrokestyle(0.1)      graphics.moveto(line_width / 2, i * gridwidth + line_width / 2)        .lineto(gridwidth * wnum + line_width / 2, i * gridwidth + line_width / 2)    }    graphics.tstrokestyle(line_width)    // 画纵向的线条    for (i = 0; i <= wnum; i++) {      if (i === wnum || i === 0) graphics.tstrokestyle(line_width)      if (i === 1) graphics.tstrokestyle(.1)      graphics.moveto(i * gridwidth + line_width / 2, line_width / 2)        .lineto(i * gridwidth + line_width / 2, gridwidth * hnum + line_width / 2)    }  }  /**    * 坐标类   */  function point(x, y) {    this.x = x    this.y = y  }  /**   * 根据移动的方向,获取当前坐标的下一个坐标   * @param direction 移动的方向   */  point.prototype.nextpoint = function nextpoint(direction) {    debugger    var point = new point(this.x, this.y)    switch (direction) {      ca dir_enum.up:        point.y -= 1        break      ca dir_enum.down:        point.y += 1        break      ca dir_enum.left:        point.x -= 1        break      ca dir_enum.right:        point.x += 1        break    }    return point  }  /** * 初始化蛇的坐标 * @returns {[point,point,point,point,point ...]} * @private */  function initsnake() {    return snake_start_point.map(function (item) {      return new point(item[0], item[1])    })  }  /**   * 绘制蛇   * @param graphics   * @param snakes // 蛇坐标   */  function drawsnake(graphics, snakes) {    graphics.clear()    graphics.beginfill("#a088ff")    var len = snakes.length    for (var i = 0; i < len; i++) {      if (i === len - 1) graphics.beginfill("#ff6ff9")      graphics.drawrect(        snakes[i].x * gridwidth + line_width / 2,        snakes[i].y * gridwidth + line_width / 2,        gridwidth, gridwidth)    }  }  /** * 改变蛇身坐标 * @param snakes 蛇坐标集 * @param direction 方向 */  function updatesnake(snakes, direction) {    var oldhead = snakes[snakes.length - 1]    var newhead = oldhead.nextpoint(direction)    // 超出边界 游戏结束    if (newhead.x < 0 || newhead.x >= num.w || newhead.y < 0 || newhead.y >= num.h) {      gamestate = game_state_enum.end    } el if (snakes.some(function (p) { // ‘吃’到自己 游戏结束      return newhead.x === p.x && newhead.y === p.y    })) {      gamestate = game_state_enum.end    } el {      snakes.push(newhead)      snakes.shift()    }  }  /**   * 引擎   * @param graphics   * @param snakes   */  function move(graphics, snakes, stage) {    cleartimeout(window._engine) // 重启时关停之前的引擎    run()    function run() {      directionnow = directionnext      updatesnake(snakes, directionnow) // 更新蛇坐标      if (gamestate === game_state_enum.end) {        end()      } el {        drawsnake(graphics, snakes)        stage.update()        window._engine = ttimeout(run, 500)      }    }  }  /**   * 游戏结束回调   */  function end() {    console.log('游戏结束')  }  function init() {    $('canvas').attr('width', canvaswidth) // 给canvas设置宽高属性赋值上当前canvas的宽度和高度(单用样式配置宽高会被拉伸)    $('canvas').attr('height', canvasheight)    directionnow = directionnext = dir_enum.down // 初始化蛇的移动方向    var snakes = initsnake()    var stage = new createjs.stage($('canvas')[0])    va回族的风俗r grid = new createjs.shape()    var snake = new createjs.shape()    drawgrid(grid.graphics) // 绘制格子    drawsnake(snake.graphics, snakes)    stage.addchild(grid)    stage.addchild(snake)    stage.update()    move(snake.g见微知著是什么意思raphics, snakes, stage)  }  init()})

效果图

效果图(gif):

3.移动蛇

制作4个按钮,控制移动方向

代码

index.html

...<div class="control">  <div class="row">    <div class="btn">      <button id="upbtn">上</button>    </div>  </div>  <div class="row clearfix">    <div class="btn half-width left">      <button id="leftbtn">左</button>    </div>    <div class="btn half-width right">      <button id="rightbtn">右</button>    </div>  </div>  <div class="row">    <div class="btn">      <button id="downbtn">下</button>    </div>  </div>  </div></div>...

stylesheet.css

....control .row {  position: relative;  height: 33%;  text-align: center;}.control .btn {  box-sizing: border-box;  height: 100%;  padding: 4px;}.control button {  display: inline-block;  height: 100%;  background-color: white;  border: none;  padding: 3px 20px;  border-radius: 3px;}.half-width {  width: 50%;}.btn.left {  padding-right: 20px;  float: left;  text-align: right;}.btn.right {  padding-left: 20px;  float: right;  text-align: left;}.clearfix:after {  content: '';  display: block;  clear: both;}

mian.js

.../** * 改变蛇行进方向 * @param dir */function changedirection(dir) {  /* 逆向及同向则不改变 */  if (directionnow + dir === 0 || directionnow === dir) return  directionnext = dir}/** * 绑定相关元素点击事件 */function bindevent() {  $('#upbtn').click(function () { changedirection(dir_enum.up) })  $('#leftbtn').click(function () { changedirection(dir_enum.left) })  $('#rightbtn').click(function () { changedirection(dir_enum.right) })  $('#downbtn').click(function () { changedirection(dir_enum.down) })}function init() {  bindevent()  ...}

效果图

效果图(gif):

4. 绘制果子

随机取两个坐标点绘制果子,判定如果“吃到”,则不删除尾巴。缩短定时器的时间间隔增加难度。

注意的点(遇到的问题以及解决方案):新增一个果子不能占用蛇的坐标,一开始考虑的是随机生成一个坐标,如果坐标已被占用,那就继续生成随机坐标。然后发现这样做有个问题就是整个界面剩余两个坐标可用时(极端情况,蛇占了整个屏幕就差两个格子了),那这样的话,不停随机取坐标,要取到这最后两个坐标要耗不少时间。后面改了方法,先统计所有坐标,然后循环蛇身坐标,一一排除不可用坐标,然后再随机抽取可用坐标的其中一个。

代码

main.js

$(function () {  var line_width = 1 // 线条宽度  var line_max_num = 32 // 一行格子数量  var snake_start_point = [[0, 3], [1, 3], [2, 3], [3, 3]] // 初始蛇坐标  var dir_enum = { up: 1, down: -1, left: 2, right: -2 }    // 移动的四个方向枚举值,两个对立方向相加等于0  var game_state_enum = { end: 1, ready: 2 } // 游戏状态枚举  var canvasheight = $('canvas').height() // 获取canvas的高度  var canvaswidth = $('canvas').width() // 获取canvas的宽度  var gridwidth = (canvaswidth - line_width) / line_max_num // 格子宽度,按一行32个格子计算  var num = { w: line_max_num, h: math.floor((canvasheight - line_width) / gridwidth) } // 计算横向和纵向多少个格子,即:横坐标的最大值和纵坐标的最大值  var directionnow = null // 当前移动移动方向  var directionnext = null // 下一步移动方向  var gamestate = null // 游戏状态  var scope = 0 // 分数  /** * 绘制格子地图 * @param graphics */  function drawgrid(graphics) {    var wnum = num.w    var hnum = num.h    graphics.tstrokestyle(line_width).beginstroke('#ffac52')    // 画横向的线条    for (var i = 0; i <= hnum; i++) {      if (i === hnum || i === 0) graphics.tstrokestyl环境科学与工程就业前景e(line_width)      if (i === 1) graphics.tstrokestyle(0.1)      graphics.moveto(line_width / 2, i * gridwidth + line_width / 2)        .lineto(gridwidth * wnum + line_width / 2, i * gridwidth + line_width / 2)    }    graphics.tstrokestyle(line_width)    // 画纵向的线条    for (i = 0; i <= wnum; i++) {      if (i === wnum || i === 0) graphics.tstrokestyle(line_width)      if (i === 1) graphics.tstrokestyle(.1)      graphics.moveto(i * gridwidth + line_width / 2, line_width / 2)        .lineto(i * gridwidth + line_width / 2, gridwidth * hnum + line_width / 2)    }  }  /**    * 坐标类   */  function point(x, y) {    this.x = x    this.y = y  }  /**   * 根据移动的方向,获取当前坐标的下一个坐标   * @param direction 移动的方向   */  point.prototype.nextpoint = function nextpoint(direction) {    var point = new point(this.x, this.y)    switch (direction) {      ca dir_enum.up:        point.y -= 1        break      ca dir_enum.down:        point.y += 1        break      ca dir_enum.left:        point.x -= 1        break      ca dir_enum.right:        point.x += 1        break    }    return point  }  /** * 初始化蛇的坐标 * @returns {[point,point,point,point,point ...]} * @private */  function initsnake() {    return snake_start_point.map(function (item) {      return new point(item[0], item[1])    })  }  /**   * 绘制蛇   * @param graphics   * @param snakes // 蛇坐标   */  function drawsnake(graphics, snakes) {    graphics.clear()    graphics.beginfill("#a088ff")    var len = snakes.length    for (var i = 0; i < len; i++) {      if (i === len - 1) graphics.beginfill("#ff6ff9")      graphics.drawrect(        snakes[i].x * gridwidth + line_width / 2,        snakes[i].y * gridwidth + line_width / 2,        gridwidth, gridwidth)    }  }  /** * 改变蛇身坐标 * @param snakes 蛇坐标集 * @param direction 方向 */  function updatesnake(snakes, fruits, direction, fruitgraphics) {    var oldhead = snakes[snakes.length - 1]    var newhead = oldhead.nextpoint(direction)    // 超出边界 游戏结束    if (newhead.x < 0 || newhead.x >= num.w || newhead.y < 0 || newhead.y >= num.h) 秀逗糖{      gamestate = game_state_enum.end    } el if (snakes.some(function (p) { // ‘吃’到自己 游戏结束      return newhead.x === p.x && newhead.y === p.y    })) {      gamestate = game_state_enum.end    } el if (fruits.some(function (p) { // ‘吃’到水果      return newhead.x === p.x && newhead.y === p.y    })) {      scope++      snakes.push(newhead)      var temp = 0      fruits.foreach(function (p, i) {        if (newhead.x === p.x && newhead.y === p.y) {          temp = i        }      })      fruits.splice(temp, 1)      var newfruit = createfruit(snakes, fruits)      if (newfruit) {        fruits.push(newfruit)        drawfruit(fruitgraphics, fruits)      }    } el {      snakes.push(newhead)      snakes.shift()    }  }  /**   * 引擎   * @param graphics   * @param snakes   */  function move(snakegraphics, fruitgraphics, snakes, fruits, stage) {    cleartimeout(window._engine) // 重启时关停之前的引擎    run()    function run() {      directionnow = directionnext      updatesnake(snakes, fruits, directionnow, fruitgraphics) // 更新蛇坐标      if (gamestate === game_state_enum.end) {        end()      } el {        drawsnake(snakegraphics, snakes)        stage.update()        window._engine = ttimeout(run, 500 * math.pow(0.9, scope))      }    }  }  /**   * 游戏结束回调   */  function end() {    console.log('游戏结束')  }  /**   * 改变蛇行进方向   * @param dir   */  function changedirection(dir) {    /* 逆向及同向则不改变 */    if (directionnow + dir === 0 || directionnow === dir) return    directionnext = dir  }  /**   * 绑定相关元素点击事件   */  function bindevent() {    $('#upbtn').click(function () { changedirection(dir_enum.up) })    $('#leftbtn').click(function () { changedirection(dir_enum.left) })    $('#rightbtn').click(function () { changedirection(dir_enum.right) })    $('#downbtn').click(function () { changedirection(dir_enum.down) })  }  /** * 创建水果坐标 * @returns point * @param snakes * @param fruits */  function createfruit(snakes, fruits) {    var totals = {}    for (var x = 0; x < num.w; x++) {      for (var y = 0; y < num.h; y++) {        totals[x + '-' + y] = true      }    }    snakes.foreach(function (item) {      delete totals[item.x + '-' + item.y]    })    fruits.foreach(function (item) {      delete totals[item.x + '-' + item.y]    })    var keys = object.keys(totals)    if (keys.length) {      var temp = math.floor(keys.length * math.random())      var key = keys[temp].split('-')      return new point(number(key[0]), number(key[1]))    } el {      return null    }  }  /** * 绘制水果 * @param graphics * @param fruits 水果坐标集 */  function drawfruit(graphics, fruits) {    graphics.clear()    graphics.beginfill("#16ff16")    for (var i = 0; i < fruits.length; i++) {      graphics.drawrect(        fruits[i].x * gridwidth + line_width / 2,        fruits[i].y * gridwidth + line_width / 2,        gridwidth, gridwidth)    }  }  function init() {    bindevent()    $('c工作绩效考核anvas').attr('width', canvaswidth) // 给canvas设置宽高属性赋值上当前canvas的宽度和高度(单用样式配置宽高会被拉伸)    $('canvas').attr('height', canvasheight)    directionnow = directionnext = dir_enum.down // 初始化蛇的移动方向    var snakes = initsnake()    var fruits = []    fruits.push(createfruit(snakes, fruits))    fruits.push(createfruit(snakes, fruits))    var stage = new createjs.stage($('canvas')[0])    var grid = new createjs.shape()    var snake = new createjs.shape()    var fruit = new createjs.shape()    drawgrid(grid.graphics) // 绘制格子    drawsnake(snake.graphics, snakes)    drawfruit(fruit.graphics, fruits)    stage.addchild(grid)    stage.addchild(snake)    stage.addchild(fruit)    stage.update()    move(snake.graphics, fruit.graphics, snakes, fruits, stage)  }  init()})

效果图

效果图(gif):

5. 分数显示、游戏结束提示、排行榜

这一部分就比较简单了,处理下数据的展示即可。这部分代码就不展示出来了。

效果图

结语

界面比较粗糙,主要是学习逻辑操作。中间出现一些小问题,但都一一的解决了。createjs这个游戏引擎还是比较简单易学的,整体只用了绘制图形的api。

本文发布于:2023-04-03 09:40:05,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/aa9ca5a1612d1b45461f0f58215f4667.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:手把手教学h5小游戏 – 贪吃蛇.doc

本文 PDF 下载地址:手把手教学h5小游戏 – 贪吃蛇.pdf

标签:坐标   格子   宽度   方向
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图