无限循环的H5游戏背景是怎么实现的

490 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。 点击查看活动详情

写在前面

我在玩打飞机跑酷等h5小游戏的时候,有一个小疑问,那就是随着游戏的不断进行,新场景的游戏背景是怎么处理的,这些背景都是用图片做的,这样不是要加载很多图片素材吗,但是我查看源文件却发现背景图片只有一张或者几张,显然是有其他的方式处理游戏背景。

孤独的背景图

这类小游戏里背景和游戏内容通常都是割裂的,也就是游戏元素是一套逻辑,背景图一直孤独的在后面不听的变换,既没有与游戏元素之间的关联关系,也不会与之发生碰撞等交互。

我们可以很朴素的想到,随着游戏进度的增加,一直往后绘制背景就可以了,但是图片就那么大,总要绘制到头的那一刻,那么这个时候就出现只能绘制背景1的一部分,剩余的部分要用背景2的补上了

image.png

背景2也绘制完了的时候,难道要接着绘制背景3吗,这样还是要加载很多背景图啊,这个时候设计的巧妙就来了,只需要把背景1的顶部和背景2的底部衔接起来,这样背景1背景2就可以一直轮流着充当游戏背景了。

这里只是为了方便表述,实际上只要把背景图的首尾衔接上,一张图也能达到这样的效果,但是这样会造成单张图过大,也会限制背景设计的内容,实际上图片的数量是不限制的,只需要把第一张和最后一张的首尾衔接起来就好了

bg2.gif

实现

这里假设手机屏幕大小是300 * 500, 每张背景图的大小是300 * 600,那么很显然,初始就是绘制背景10 * 500部分,慢慢的绘制到背景1100 * 600部分,而再接下去,绘制背景1200 * 600部分的时候已经不能充满屏幕了,需要再绘制背景20 * 100部分了,而当背景1全部绘制完的时候,背景2就完全替代的一开始的背景1(即绘制背景20 * 500部分),如此循环下去,就能达到只用几张背景图,但是背景却一直滚动的效果了。

代码实现

先准备一个背景图数组,数量不定

this.bgs = ["./bg1.png", "./bg2.png", "./bg3.png"]
// 背景1
this.index = 0
this.bg1 = new Image()
this.bg1.src = this.bgs[this.index]
this.y = 0; // 游戏进度

this.bg2 = null

再根据游戏进度y把背景绘制上去,一开始背景2为空,如果计算出背景2也需要绘制,那么就赋值bg2

// 一开始y为0,即绘制bg1的0-500部分
// y增加到50的时候,绘制bg1的50-550部分
// y增加到200的时候,绘制bg1的200-600部分,
// 但是这总共只有400,屏幕为500,还差100,要绘制bg2的0-100到canvas的400-500位置
// y增加到300的时候,绘制bg1的300-600部分,再绘制bg2的0-200部分到canvas的300-500位置
// y增加到600的时候,说明bg1已经绘制完了,把bg2重置为bg1,把y重置为0从先循环
// y一直增加,绘制bg1的[0, y, w, h]到canvas上的[0, 0, w, h]上,
this.ctx.drawImage(this.bg1, 0, this.y, this.w, this.imgh, 0, 0, this.w, this.imgh);
if(this.bg2 && this.bg2.src) {
    // 如果bg1只绘制了部分,那么从bg2上补充上空缺
    this.ctx.drawImage(
        this.bg2, 
        0, 
        0, 
        this.w, 
        this.h - (this.imgh - this.y), 
        0, 
        this.imgh - this.y, 
        this.w, 
        this.h - (this.imgh - this.y));
}

接下来用定时器 用requestAnimationFrame封装一个定时器 不断更新游戏进度,计算要绘制的内容

animate() {
    this.timer = new Timer(() => {
        this.y += 3
        // 如果图片的绘制内容小于屏幕,就要考虑绘制下一张图片了
        if(this.imgh - this.y < this.h) {
            this.bg2 = new Image()
            this.bg2.src = this.bgs[(this.index + 1) % this.bgs.length]
        }
        // 如果bg1已经全部绘制完了,就把bg2当成bg1,把bg3当成bg2准备绘制,一直循环替换
        if(this.y > this.imgh) {
            this.bg1.src = this.bgs[(this.index + 1) % this.bgs.length]
            this.y = 0
            this.bg2 = null
            this.index ++
        }
    }, 16)
}

这样一个不断滚动的游戏背景就实现了

bg21.gif