您的位置:首页> 日志> 前端技术 正文
DTOP

HTML5 《贪吃蛇》小游戏

laomao800 | 2013年04月12日 |

>>收藏本文 已有 1条评论

游戏背景

贪吃蛇又名贪食蛇,是一款经典的小游戏。玩家使用方向键操控一条长长的蛇不断吞下豆子,
同时蛇身随着吞下的豆子不断变长,当蛇头撞到蛇身或障壁时游戏结束。
贪吃蛇最初为人们所知的是诺基亚手机附带的一个小游戏,它伴随着诺基亚手机走向世界。
现在的贪吃蛇出现了许多衍生版本,并被移植到各种平台上。
(以上为百度百科资料)

 

游戏规则

贪吃蛇的基本规则是控制一条不停走动的蛇,只能通过左、右2个方向90°转弯,在行走过程中通过吃豆子(食物)得分,如果撞上自己的身体或墙壁既游戏结束。还有一些衍生的版本有血槽、障碍物等丰富的内容,这些丰富的游戏要素在此次的版本中……都不涉及……

这次做的贪吃蛇的游戏规则设置的比较简单:

  • 撞上墙壁游戏结束。 ( 也可以通过修改判断条件实现行走至墙壁会从对面墙壁穿越回来)
  • 撞上自己身体游戏结束
  • 每吃一个食物分数加1

 

运行截图

 

实现过程

游戏初始化

首先进行资源 (图片) 的加载,加载完毕后执行初始化函数 gameInit

imgData = new Array (
    {name:"bg", path:"images/bg.png"},
    {name:"btn", path:"images/btn.png"},
    {name:"king", path:"images/king.jpg"}
)

//……略

var main = function() {
    //填充背景
    backLayer.graphics.drawRect(5,"#BD8D46",[0,0,option.canvasWidth,option.canvasHeight],true,"#E6E2AF");
    addChild(backLayer);
    
    loadingLayer = new LoadingSample1();
    backLayer.addChild(loadingLayer);
    //加载方法
    LLoadManage.load(
        imgData,
        function(progress) {
            loadingLayer.setProgress(progress);
        },
        function(result) {
            imglist = result;
            backLayer.removeChild(loadingLayer);
            loadingLayer = null;

            bgMapData[0] = new LBitmapData(imglist["bg"], 0, 0, option.imgStep, option.imgStep);
            bgMapData[2] = new LBitmapData(imglist["bg"], option.imgStep, 0, option.imgStep, option.imgStep);
            bgMapData[1] = new LBitmapData(imglist["bg"], option.imgStep*2, 0, option.imgStep, option.imgStep);
            //游戏初始化
            gameInit();
        }
    );
}

 

游戏初始化函数用于初始化各个游戏层以及层内元素

var gameInit = function() {
    //初始化背景
    initBackground();
    //初始化控制按钮
    initButton();
    //初始化蛇
    initSnake();
    //初始化食物
    initFood();
    //初始化得分
    initScore();
}
  • 初始化背景:建立背景的地图小格子内容。
  • 初始化控制按钮:建立按钮显示层,同时设置按钮事件。 (同时支持键盘方向键)
  • 初始化蛇:建立蛇显示层,并通过调用 snakeLoop 方法实现刷新绘制蛇的行走动画。
    snakeLoop 方法内包括行走判断、游戏结束判断等内容
  • 初始化食物:建立食物显示层,随机显示一个食物于未被蛇身体占据的位置。
  • 初始化得分:建立分数显示层,初始化分数显示。

 

蛇的行走

switch (goWay) {
    case "top":
        y = snake[snake.length-1].y-1;
        if (y<1) {
            if (!option.loopMap) gameover("撞到边界拉 ┑( ̄。 ̄)┍");
            y = option.map.y;
        }
        snake.push({x:snake[snake.length-1].x, y:y});
        break;
    case "bottom":
        y = snake[snake.length-1].y+1;
        if (y>option.map.y) {
            if (!option.loopMap) gameover("撞到边界拉 ┑( ̄。 ̄)┍");
            y = 1;
        }
        snake.push({x:snake[snake.length-1].x, y:y});
        break;
    case "left":
        x = snake[snake.length-1].x-1;
        if (x<1) {
            if (!option.loopMap) gameover("撞到边界拉 ┑( ̄。 ̄)┍");
            x = option.map.x;
        }
        snake.push({x:x, y:snake[snake.length-1].y});
        break;
    case "right":
        x = snake[snake.length-1].x+1;
        if (x>option.map.x) {
            if (!option.loopMap) gameover("撞到边界拉 ┑( ̄。 ̄)┍");
            x = 1;
        }
        snake.push({x:x, y:snake[snake.length-1].y});
        break;
}

通过 snakeLoop 函数判断当前行走方向变量 goWay , 按照前进方向,给蛇的身体数组 "snake" 头部增加一个方块(增加一个数组元素,为方块坐标),并在此判断是否撞到边界。

 

if (snake[snake.length-1].x==food.x && snake[snake.length-1].y==food.y){
    if (snake.length>=(option.map.x*option.map.y)) {
        option.godMode = false;
        gameover("长度太长拉 ┑( ̄。 ̄)┍");
    }
    //与食物坐标重合,重新设置食物
    setFood();
    //设置分数
    setScore();
} else if (snakeCache[(snake[snake.length-1].y-1)*option.map.x+(snake[snake.length-1].x)]==1) {
    //碰到自己身体
    gameover("撞到自己啦 ┑( ̄。 ̄)┍");
} else {
    //普通移动,移除最末一个方块
    snake.shift();
}
drawSnake();

若不是撞到边界,则进行判断:

  • 如果和食物坐标重合,表示吃到食物,不移除尾部方块。(此处再加上判断是否超过地图最大容量)
  • 如果和蛇的身体占用位置从何,表示撞到自己,执行gameover。
  • 如果以上情况都不是,则是普通行走,从尾部移除一个方块。

 

判断完行走情况之后执行绘制函数 drawSnake ,在屏幕上绘制出蛇的形状。

最后给 snakeLoop 函数增加一个定时循环,就可以让蛇看起来在屏幕上“行走”了,通过控制定时时间则可以实现蛇的行走速度快慢的改变。

  canTurn  变量在改变一次方向后会设为flase,表示不能再转向(连续多次改变转向),在行走过一步后,将  canTurn  变量设为true,表示可以进行转向,防止快速改变方向时行走错乱。

 

蛇的绘制

var drawSnake = function() {
    var i, j;
    snakeCache = new Array();
    for (i=0, j=snake.length; i<j; i++) {
        bitmap = new LBitmap(bgMapData[1]);
        bitmap.alpha = (i==snake.length-1) ? 1 : 0.5;
        //设置小图片坐标
        bitmap.x = (snake[i].x-1)*option.imgStep + option.offsetX;
        bitmap.y = (snake[i].y-1)*option.imgStep + option.offsetY;
        //将小图片添加到背景层
        snakeLayer.addChild(bitmap);
        //将蛇占用的位置写入snakeCache用于计算方块是否被身体占据
        snakeCache[(snake[i].y-1)*option.map.x+(snake[i].x)] = 1;
    }
}

遍历snake数组绘制蛇的形状,并同时更新蛇的占地位置 snakeCache。

 

地图空白位置

var getPos = function() {
    var x, y;
    //[1~地图宽度]随机数
    while(true) {
        x = Math.ceil(Math.random()*option.map.x);
        y = Math.ceil(Math.random()*option.map.y);
        if (!snakeCache[(y-1)*option.map.x + x]) break;
    }
    return {
        x : x,
        y : y
    }
}
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

snakeCache 数组用于存放蛇的占位信息,这里使用的方法是给地图中每个方块一次编上数字作为下标(类似上面的表格的形式),设置true、false,是否占用状态。

获取到空白位置后则可以实现给地图随机空白地方生成食物。

 

其他

  • gameover 方法
  • 分数计算

 

要点

  • 通过  snakeCache  变量实时存储被蛇占用的位置,确保食物生成的位置是未被占用的。
  • 通过  canTurn  变量,确保改变方向防止用户快速改变方向的时候行走错乱。
  • 用 lufylegend 本身的帧事件实现行走动画的时候会有“延迟一步”的问题,手感上就是行走动作和按键之间有延迟。 (发现用  setInterval  无此问题,无深究,估计是库的帧事件的实现方式的问题?)

 

演示

[在线演示]
[下载地址]