JavaScript操作canvas制作前端H5小游戏——Flappy Bird

本文介绍了一位前端新手如何利用JavaScript和HTML5的canvas制作Flappy Bird小游戏的过程。文章详细阐述了游戏的各个元素,包括背景、小鸟、水管、地板和分数的绘制,以及碰撞检测和游戏状态的管理。通过分析游戏逻辑,逐步分解实现步骤,帮助读者理解游戏的实现原理。
摘要由CSDN通过智能技术生成

游戏查看

源码和素材下载

博主学习前端一年多一点,还是个新手,不过勇于尝试,才能不断进步,如果代码质量不好,欢迎提意见,下面开始讲解,首先贴张游戏界面图:

这里写图片描述

游戏使用canvas画图制作,分析游戏确定有这几个元素:

  1. 天空背景不动
  2. 小鸟上下移动,左右不动
  3. 地板和水管向左移动(造成小鸟向前移动的错觉)

canvas画图是覆盖画图,所以画图顺序很重要,它的api我就不多说了,只有用到以下内容:

<!-- html代码 -->
<canvas id="canvas">您的浏览器不支持canvas</canvas>
/* js相关 */
var canvas = document.getElementById('canvas'),   //获取canvas节点
    ctx = canvas.getContext('2d');                //获取画布上下文画图环境
//画图(只举了一种,还有另一种传参方式)
ctx.drawImage(img, imgx, imgy, imgw, imgh, canx, cany, canw, canh);
//参数的含义依次为:图片资源、图片的x坐标、y坐标、宽度、高度、画布中x坐标、y坐标、宽度、高度
//因为我把所有的图片都合成一张,所以需要用截取图像的传参方式

下面简单说说整个游戏的运行代码结构:

var img = new Image();           //加载图像
img.src = './img.png';             
img.onload = start;              //图像加载完成就运行start函数,所以start是入口

function start(){
    //检查是否碰撞到地板,水管
    check();
    if(是否游戏结束){
        //游戏结束的操作然后退出
        return;
    }
    //画背景
    ...
    if(isStarted){            //isStarted为是否开始游戏的变量,是全局的,默认为false
        //开始游戏就画小鸟,水管
    }else{
        //否则就画准备开始的图像
    }
    //画分数(默认为0,准备阶段画的是0...
    //画地板
    ...
    //设置定时器,保证动画在游戏中不断进行
    timer = requestAnimationFrame(start); //和setTimeout(start, 16)效果差不多
}

document.ontouchstart = document.onmousedown = function(e){
    //点击屏幕时小鸟进行跳跃等处理
}

整体结构就是这样,然后我们一部分一部分完成就可以了。

第一步:获取设备的屏幕大小,兼容各种屏幕设备

var viewSize = (function(){
   

    var pageWidth = window.innerWidth,
        pageHeight = window.innerHeight;

    if (typeof pageWidth != 'number') {
        pageHeight = document.documentElement.clientHeight;
        pageWidth = document.documentElement.clientWidth;
    };

    if(pageWidth >= pageHeight){
        pageWidth = pageHeight * 360 / 640;
    }
    pageWidth = pageWidth >  414 ? 414 : pageWidth;
    pageHeight = pageHeight > 736 ? 736 : pageHeight;

    return {
        width: pageWidth,
        height: pageHeight
    };

})();
//然后就设置画布宽高
canvas.width = viewSize.width;
canvas.height = viewSize.height;
//定义原图像与游戏界面的像素比
var k = viewSize.height / 600    //我找的背景图高度为600px,所以比例就是屏幕高除以600

第二步:完成游戏进行中的部分(没有gameover检查,isStarted为true时)

1)画背景(没有难点,主要是图像大小的计算要想清楚)

//清除
ctx.clearRect(0,0,viewSize.width,viewSize.height);
//画背景
ctx.drawImage(img, 0, 0, 800, 600, 0, 0, Math.ceil(k * 800), viewSize.height);

2)画小鸟:我在全局定义了一个小鸟类,如下:

function Bird(){
   
    //小鸟拍翅膀有三种状态,所以画图相关大多用一个数组来表示
    this.imgX = [170, 222, 275];                           //在原图中x的坐标
    this.imgY = [750, 750, 750];                           //在原图中y的坐标
    this.imgW = [34, 34, 34];                              //在原图中宽度
    this.imgH = [24, 24, 24];                              //在原图中高度
    var canX = Math.ceil(110 / 450 * viewSize.width);      //在画布中x的坐标
    this.canX = [canX, canX, canX];
    var canY = Math.ceil(380 / 800 * viewSize.height);     //在画布中y的初始坐标 
    this.canY = [canY, canY, canY];                        
    var canW = Math.ceil(34 * k);                          //在画布中的宽度  
    this.canW = [canW, canW, canW];                 
    var canH = Math.ceil(24 * k);                          //在画布中的高度
    this.canH = [canH, canH, canH];
    //下面三个变量是用来协助记住是在三个状态中的哪个状态,后面一看就知道了
    this.index = 0;                                        
    this.count = 0;
    this.step = 1;
    //表示小鸟飞行的时间,后面知道用途
    this.t = 0;
    //记住初始y坐标,也是后面一看就知道了
    this.y = [canY, canY, canY];
}

定义类的好处就是可以不用设置那么多的全局变量,你可以直接定义小鸟为一个对象,接着定义小鸟画图方法:

Bird.prototype.draw = function(){
    var index = this.index;
    //翅膀拍动, this.count就是用来控制拍动的频率,记住定时器1秒运行16帧,频率很快的
    this.count++;
    if(this.count == 6){
        this.index += this.step;   
        this.count = 0;
    }
    //this.index的变化过程为0、1、2、1、0、1、2、1...所以需要this.index +1和-1变化
    if((this.index == 2 && this.step == 1) || (this.index == 0 && this.step) == -1){
        this.step = - this.step;
    } 
    //计算垂直位移,使用公式 y = a * t * (t - c),这里就知道了this.t是代表着小鸟起跳后到现在的时间
    //我使用了抛物线的函数方程,你也可以自己选择,代码下面我会给出函数坐标图就很清除了
    var c = 0.7 * 60;
    var minY = - 85 * viewSize.height / 800;
    var a = -minY * 4 / (c * c);
    var dy = a * this.t * (this.t - c);  //dy是小鸟的位移
    //下面是小鸟飞到顶部的情况,我的处理是,使再点击失效,要小鸟飞下来才能继续点击
    if(this.y[0] + dy < 0){
        canClick = false;
    }else{
        canClick = true;
    }
    //然后小鸟在画布的y坐标就等于原先的y坐标加上位移
    for(var i = 0; i < 3; i++){
        this.canY[i] = this.y[i] + Math.ceil(dy);
    }
    this.t++;
    ctx.drawImage(img, this.imgX[index], this.imgY[index], this.imgW[index], 
                       this.imgH[index], this.canX[index], this.canY[index], 
                       this.canW[index], this.canH[index]);

};

给出小鸟计算方程的坐标图

这里写图片描述

因为canvas的y正方向是向下的,所以跳跃应该位移是先负后正,自由落体又是抛物线,接下来就是数学知识了,图中可以看出:

  • 如果this.t > c,dy > 0,所以可以得出,当this.t = c小鸟到最高点,选c的大小就可以控制上升和下落的速度

  • 在this.t = c/2时,dy达到了最小值,所以,控制Ymin可以确定小鸟的垂直移动最大距离。

要画小鸟就可以:

var bird = new Bird();
bird.draw();

3)画水管:

游戏画面中最多出现两组水管,当第一组水管到中间时,第二组开始出现,当第一组水管从游戏界面的左边出去了,第二组水管刚刚到达中间,而最右边又开始有水管进来,以此类推,不断重复。

这里写图片描述

先解决一组水管的画法,仍然先定义水管类,分为上水管和下水管:

//基类,属性的含义同小鸟类
function Pie(){
   
    this.imgY = 751;              
    this.imgW = 52;              
    this.imgH = 420;
    this.canX = viewSize.width;               //默认在画布的最右边
    this.canW = Math.ceil(80 / 450 * viewSize.width);
    this.canH = Math.ceil(this.canW * 420 / 52);
}
//其中top我们随机生成,代表的是同一组水管中,上水管的左下角在画布中的y坐标
//上水管类
function UpPie(top){
     
    Pie.call(this);                       //继承相同的属性
    this.imgX = 
  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值