对一个H5射击小游戏的算法理解

导言

这是一个初学者通过一个h5小游戏教程中出现的一些算法包括(对象池,脏矩形绘制,四叉树的2D碰撞检测)进行一个简单介绍,用于自我理解的目的。

游戏的DEMO,教程中的纵版改为横版。

教程中分为5个部分,包括从背景的制作到最后规则判定。其中关于玩家飞机和子弹判定这两个部分用到了对象池,脏矩形绘制,四叉树的2D碰撞检测算法。比较有意思。

对于游戏来说
对象池用于重复利用对象,这里是子弹,而不是对象创造,使用,消失,再创造的循环。脏矩形绘制用于判定子弹是否可以再利用。四叉树用来帮助子弹碰撞判定。

对象池 Object Pool

再介绍对象池之前,先想一下,在射击游戏中,通常会在短时间内,产生,运行,移除大量对象(子弹)。这通常需要大量的运算,可能会导致游戏的卡顿。原因是因为后台程序垃圾收集器(garbage collector)会让系统资源清除已用内存(used memory)。

简单介绍一下垃圾收集器,垃圾回收是一个自动资源分配程序,用于清除不再需要的已用内存空间。
比如下例:

for (int i = 1; i < 10; i++) {
  System.out.println(i);
} 

当这个for loop 运行时,会产生变量i ,内存会保存变量i的所有数据。当loop结束时,变量i就会被清除。
这个例子当中,变量i会有足够多的内存保存。但是当变量i所需内存在巨量增加时,而可用内存又不够时,垃圾回收机制会清除其他内存来保证变量i所需的内存。这会导致短时间内的卡顿。

因此,对象池的目的就是减少变量i所需的内存,从而增加游戏流畅度。本质上就是对旧对象的重复利用,而不是不停的创造,删除。

假象一下,一盒纸牌(纸牌盒代表内存容量),每次需要一个新纸牌(新对象,新子弹),你抽出一张使用,然后扔到垃圾桶。最终,纸牌会用完,或者垃圾桶满了需要倒掉(垃圾收集)。
在一个对象池中,纸牌是空白的,当需要的时候,直接写上需要的信息,用完后擦干净,放回纸牌盒。这样就可以循环。

一个标准的对象池包含两个部分,初始化容器,以及函数getanimate )。

初始化容器,就是对设定一个包含n个对象的数组,并且实例化为空值。
下图设定了最大值maxSize为参数,并把子弹bullet的参数设置空值。也就是说子弹已经被加载了,只是参数为0而已(x和y的位置,和速度)。

function ObjectPool(maxSize) {
    var size = maxSize; 
    var ObjectPool = [];
    this.init = function() { //实例化并空值每一个子弹
        for (var i = 0; i < size; i++) {
            var bullet = new Bullet(); 
            bullet.init(//); 
            pool[i] = bullet;
        }
    };

函数部分由getanimate组成。
get 用于发射子弹。函数用于把检测最后一个对象是否正在被使用(alive or dead),如果没有被使用(dead),函数会初始化(这里指的是赋值子弹的默认参数)并把它排到第一位(发射)。如果正在被使用(通常情况下不会,不然也可以调整最大值),可以再创建一个对象。

    this.get = function(x, y, speed) {
        if(!pool[size - 1].alive) {
            pool[size - 1].spawn(x, y, speed); 
            //赋予子弹的默认参数,也就是发射子弹
            pool.unshift(pool.pop());
        }
    };

animate 重复利用已经被创造对象。检测对象是否被能被使用,如果为true,清除参数,并放置到容器中。其中检测对象的方式依据项目而异。

    this.animate = function() {
        for (var i = 0; i < size; i++) {
        //通过alive和draw的返回值检测
            if (pool[i].alive) {
                if (pool[i].draw()) {
                    pool[i].clear();
                    pool.push((pool.splice(i,1))[0]);
                }
            }
            else
                break;
        }
    };

本例中,用了脏矩形技术(Dirty rectangles)检测。
首先alive的判定方式是看对象是否被赋值使用。draw 用的是脏矩形技术检测。
脏矩形技术用于检测画面中变化的部分,这样就不需要每一帧重绘全部图像。这里就是判定目标对象。

    this.draw = function() {
        this.context.clearRect(this.x, this.y, this.width, this.height);
        this.y -= this.speed;
        //检测目标有没有移出屏幕
        if (this.y <= 0 - this.height) {
            return true;
        }
        else {
            this.context.drawImage(imageRepository.bullet, this.x, this.y);
        }
    };

上例当中,draw主要用于检测目标是否移动到屏幕外,如果为true,。
之前的animate 函数中的意义就是,检测子弹有没有飞出去,有没有飞到屏幕外,如果两个都为true,则把飞出去的子弹移到容器中。

四叉数 QuadTree

四叉树用于物体碰撞检测,当物体只有两个时,只需要直接计算就可以得出碰撞,当物体数量过多时,比如100个,那么计算碰撞压力就很大,因为每一个物体都要跟其余99个计算一次碰撞。
四叉树通过把屏幕划分4个区域,只有区域内或者区域线上的物体才会有可能发射碰撞。四叉树返回的是有可能发生碰撞的物体,只需要计算这些物体就可以。

四叉树例子

上图包含了4个区域,其中坐上区域又被划分为4个子区域,因为对象数量过过多。
所以需要设定的是需要多少层区域划分,每一层所承载的最大对象。

回到游戏来说,四叉树检测可能过于小题大做,其他也有更方便的方法用于子弹检测。主要有利于理解四叉树原理。

引用:
JavaScript QuadTree Implementation
http://www.mikechambers.com/blog/2011/03/21/javascript-quadtree-implementation/

HTML5 Canvas Game: 2D Collision Detection
http://blog.sklambert.com/html5-canvas-game-2d-collision-detection/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值