17站长网

17站长网 首页 编程 JavaScript 查看内容

JS+Canvas实现贪吃蛇小游戏

2022-10-26 19:07| 查看: 1636 |来源: 互联网

今天呢,主要和小伙伴们分享一下一个贪吃蛇游戏从构思到实现的过程~因为我不是很喜欢直接PO代码,所以只copy代码的童鞋们请出门左转不谢。

今天呢,主要和小伙伴们分享一下一个贪吃蛇游戏从构思到实现的过程~因为我不是很喜欢直接PO代码,所以只copy代码的童鞋们请出门左转不谢。

按理说canvas与其应用是老生常谈了,可我在准备阶段却搜索不到有用的资料(不是代码!),所以说呢,只能自力更生 -_- 

首先是大致要考虑的东西:

1.要有蛇(没蛇怎么叫贪吃蛇)。

2.然后要有地图(蛇是不能上天的)。

3.不能水平\垂直掉头(如果想掉头,需要至少变换方位并且至少移动一格才可)。

4.食物(不然怎么贪吃)。

5.吃了食物要变长(这才是精髓)。

PS:~现在我回想起来,当时的确只想到这么多(⊙﹏⊙)

构思完毕,开工!

怎么做呢?从大到小,先画个矩形作地图,可我觉得太丑,于是画了一张图出来:

1

2

3

4

5

context.beginPath();

var bgImg = new Image();

bgImg.src = "img/background.png";

context.drawImage(bgImg, 0, 0, 600, 600);

context.closePath();

现在我们有地图了

地图上好像缺点什么……没错就是礼物,所以我们现在生成礼物,那么问题来了:礼物最多有几个、生成位置、何时生成。

我这里暂时定义为:最多2个、随机位置生成、当礼物个数小于2时生成至2个。

接下来就很简单了,上图中,允许蛇活动的范围是14颗树(周围两颗树是墙),然后16颗树=600px,很容易我们得到每格多宽~

所以呢,我们只需要定义一个随机生成1-14整数的方法就可以很轻松找到应该生成的位置:

1

2

3

4

//随机数

function selectfrom() {

   return Math.floor(Math.random() * 14 + 1);

}

然后再用求出的数乘以每一格子的宽度,即可求出生成的具体X坐标,因为是正方形,所以Y也一样:

1

2

var x = selectfrom() * (600/16);

var y = selectfrom() * (600/16);

并且每得到一组礼物坐标后,都需要存储在一个数组内(一会儿有大用处),至于画矩形太基础我就不说了。

And Now,我们有了礼物,有了地图,就差蛇了,那么问题又来了:出生的蛇多长、出生地、死亡方式、移动方式、转弯方式、如何判断吃掉了礼物、吃掉了礼物变长到哪里。

出生蛇长度:实际编写过程中,我发现默认长度1和2都不能够很好的体现“蛇的转弯”,所以定义为3,并且需将蛇身所有坐标记录在数组内。

出生地:地图中央或者自己定一个位置(按照格子来分),XY坐标求取方式上面已经说过不再赘述。

死亡方式:碰到障碍,或者(吃到自己)蛇头碰到蛇身。

移动方式:通过定义一个全局变量记录当前方向(0、1、2、3,默认1),并且使用计时器驱动蛇运动。

转弯方式:加入键盘按键检测事件,当方向键按下的时候修改-记录方向的全部变量即可。

如何判断吃掉了礼物:每次蛇头移动时,都要遍历下礼物集合(上面有说过),如果蛇头将要移动到的下个坐标与之重合了,则视为吃掉了礼物。

吃掉了礼物变长到哪里:直接加在头部可能会导致意外的死亡,所以我决定吃到礼物后的下一次移动不消除蛇尾(最后一个元素)。

有了上面的构思,我们可以着手定义一些可能会用到的公共变量:

移动方式:通过定义一个全局变量记录当前方向(0、1、2、3,默认1),并且使用计时器驱动蛇运动。

转弯方式:加入键盘按键检测事件,当方向键按下的时候修改-记录方向的全部变量即可。

如何判断吃掉了礼物:每次蛇头移动时,都要遍历下礼物集合(上面有说过),如果蛇头将要移动到的下个坐标与之重合了,则视为吃掉了礼物。

吃掉了礼物变长到哪里:直接加在头部可能会导致意外的死亡,所以我决定吃到礼物后的下一次移动不消除蛇尾(最后一个元素)。

有了上面的构思,我们可以着手定义一些可能会用到的公共变量:

移动方式:通过定义一个全局变量记录当前方向(0、1、2、3,默认1),并且使用计时器驱动蛇运动。

转弯方式:加入键盘按键检测事件,当方向键按下的时候修改-记录方向的全部变量即可。

如何判断吃掉了礼物:每次蛇头移动时,都要遍历下礼物集合(上面有说过),如果蛇头将要移动到的下个坐标与之重合了,则视为吃掉了礼物。

吃掉了礼物变长到哪里:直接加在头部可能会导致意外的死亡,所以我决定吃到礼物后的下一次移动不消除蛇尾(最后一个元素)。

有了上面的构思,我们可以着手定义一些可能会用到的公共变量:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

var canvas = document.getElementById("mycanvas");//画布主体

var context = canvas.getContext("2d");

var timer;//计时器

const WIDTH = canvas.width;//画布宽

const HEIGHT = canvas.height;//画布高

const XSUM = 16; //画布宽分为几格

const YSUM = 15; //画布高分为几格

const MAXFFOD = 2; //最大食物数量

var score = 0;//定义记录游戏得分

var xsplit = WIDTH / XSUM; //x每一格子的宽度

var ysplit = HEIGHT / YSUM; //y每一格子的高度

var foodcount = 0; //当前食物数量

var sinak = []; //贪吃蛇坐标集

var get = []; //礼物坐标集

var MoveTo = 1; //移动方向 默认1(右)

有了这些变量,是不是发现很多东西都通了呢?

我们先来画蛇:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

//画贪吃蛇

function drawsinak(sl) { //sl默认长度

    context.beginPath();

    context.fillStyle = "#000";

    var ling = 0; //贪吃蛇被打印长度

    for (var r = 0; r < sinak.length; r++) {

        context.fillRect(sinak[r].split(',')[0], sinak[r].split(',')[1], xsplit, ysplit);

        ling++;

    }

    if (ling == 0) {

        for (var i = 0; i < sl; i++) {

            context.fillRect(xsplit * (7 - i), ysplit * 6, xsplit, ysplit); //默认出生点:7,6默认中心点

            sinak.push(xsplit * (7 - i) + ',' + ysplit * 6);

        }

    }

    context.fill();

    context.closePath();

}

可以看到我将生成的蛇的坐标都计入了数组内,生成的礼物自然也要计入:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

context.beginPath();

  var x = selectfrom(XSUM - 2) * xsplit;

  var y = selectfrom(YSUM - 2) * ysplit;

  context.fillStyle = "red";

  for (var i = 0; i < get.length; i++) {

      context.fillRect(get[i].split(',')[0], get[i].split(',')[1], xsplit, ysplit);

      context.fill();

      foodcount++;

  }

  if (MAXFFOD > foodcount) {

      context.fillRect(x, y, xsplit, ysplit);

      context.fill();

      foodcount++;

      get.push(x + ',' + y);

  }

  context.closePath();

接下来比较重要了,蛇的移动,以及吃到礼物和触发死亡判断:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

//移动方法

//[c]移动方向 上右下左 0123

function sinakMove(c) {

    context.beginPath();

  

    //默认右侧为头

    var tou = sinak[0]; //头

    var weiba = sinak[sinak.length - 1]; //尾巴

  

    var oldX = tou.split(',')[0]; //头部旧X坐标

    var oldY = tou.split(',')[1]; //头部旧Y坐标

  

    var newX = 0; //头部最新X坐标

    var newY = 0; //头部最新Y坐标

  

    //计算头部最新XY坐标

    switch (c) {

        case 0:

            newX = oldX;

            newY = oldY - ysplit;

            break;

        case 1:

            newX = (oldX - 0) + xsplit;

            newY = oldY;

            break;

        case 2:

            newX = oldX;

            newY = (oldY - 0) + ysplit;

            break;

        case 3:

            newX = oldX - xsplit;

            newY = oldY;

            break;

    }

  

    var flag = 0; //有沒有吃到礼物 0沒有1有

  

    //如果吃到了礼物,则不消减尾部最后元素

    for (var i = 0; i < get.length; i++) {

        if (newX == get[i].split(',')[0] && newY == get[i].split(',')[1]) {

            sinak.unshift(newX + ',' + newY);

            foodcount--; //礼物计数减少1个

            get.splice(i, 1); //清空礼物

            flag = 1;

        }

    }

    //如果沒有吃到礼物,则判断是否碰到障碍或吃到自己

    if (flag == 0) {

        for (var i = 0; i < sinak.length; i++) {

            if (newX == sinak[i].split(',')[0] && newY == sinak[i].split(',')[1]) {

                if (confirm('吃掉了自己,游戏失败!是否重新开始?')) {

                    location.reload(true);

                } else {

                    context.clearRect(0, 0, WIDTH, HEIGHT);

                }

            }

        }

        if (xsplit * (XSUM - 2) < newX || ysplit * (YSUM - 2) < newY || newX == 0 || newY == 0) {

            if (confirm('撞墙了,游戏失败!是否重新开始?')) {

                location.reload(true);

            }

        }

    }

  

    //如果没有吃到礼物,那么进行普通移动

    if (flag == 0) {

        sinak.unshift(newX + ',' + newY);

        sinak.splice(sinak.length - 1, 1);

    }

  

    //画蛇

    for (var r = 0; r < sinak.length; r++) {

        context.fillRect(sinak[r].split(',')[0], sinak[r].split(',')[1], xsplit, ysplit);

    }

    context.closePath();

}

控制蛇的方向:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

//键盘事件

document.onkeydown = function (event) {

    var e = event || window.event || arguments.callee.caller.arguments[0];

    var move = 0; //移动方向

    if (e && e.keyCode == 37) { //左

        move = (MoveTo == 1 ? 1 : 3);

    } else if (e && e.keyCode == 38) { //上

        move = (MoveTo == 2 ? 2 : 0);

    } else if (e && e.keyCode == 39) { //右

        move = (MoveTo == 3 ? 3 : 1);

    } else if (e && e.keyCode == 40) { //下

        move = (MoveTo == 0 ? 0 : 2);

    } else if (e && e.keyCode == 32) {//暂停游戏

        clearInterval(timer);

    }

    MoveTo = move; //修改当前移动方向

};

这里做了防误操作,当蛇正在朝向某方向移动时,直接输入反方向是无效的。如:蛇正向右走,这时直接按←键是无效的,仍然往右走。

tag标签:JS+Canvas 贪吃蛇
本文最后更新于 2022-10-26 19:07,某些文章具有时效性,若有错误或已失效,请在网站留言或联系站长:17tui@17tui.com
·END·
站长网微信号:w17tui,关注站长、创业、关注互联网人 - 互联网创业者营销服务中心

免责声明:本站部分文章和图片均来自用户投稿和网络收集,旨在传播知识,文章和图片版权归原作者及原出处所有,仅供学习与参考,请勿用于商业用途,如果损害了您的权利,请联系我们及时修正或删除。谢谢!

17站长网微信二维码

始终以前瞻性的眼光聚焦站长、创业、互联网等领域,为您提供最新最全的互联网资讯,帮助站长转型升级,为互联网创业者提供更加优质的创业信息和品牌营销服务,与站长一起进步!让互联网创业者不再孤独!

扫一扫,关注站长网微信

大家都在看

    热门排行

      最近更新

        返回顶部