一个编程思想——位图化的矢量图

本文探讨了游戏开发中碰撞检测的原理及优化方法,通过将自由空间划分为格子,实现高效的空间分区,减少计算量,提高游戏性能。

引子



大家很多都玩过魔兽争霸3,还有相当一部分玩过星际争霸。大家都知道这两个游戏与命令与征服系列系统上的区别。为什么暴雪系列会有围杀这种战术呢?因为在这两个游戏中,任何一个地面单位都有一个碰撞体积,在碰撞体积的范围内不允许任何其他地面单位通过。这就涉及到游戏制作中的一个最古老的问题之一:如何判断碰撞呢?


另外提醒大家一点,这篇文章跟我以前那些有根本的不同,因为我感觉它一点都不通俗,肯定跟笔者的表达能力很差有关。但是还是那句老话,希望我的这篇文章像平静的水面上的一粒石子,引起了浅浅的却是无限的波纹那样吧



起因



最基本的判断两个平面上的物体是否相撞的方法,就是矩形判断和距离判断,前者可以不涉及浮点运算而且最为简单,《星际争霸》是这样做的;后者比矩形精确,而且相比来说也很简单,《魔兽争霸3》是这样做的


如果屏幕上只有很少的物体,判断它们之间的相互碰撞是一件很简单的事情。例如红白机上经典的小蜜蜂游戏,屏幕上同时可以出现40个蜜蜂,1个己方子弹和6个敌方子弹,这样,程序每一次循环需要做判断如下:



己方子弹和敌人
40*1=40次


己方飞机和敌人
40*1=40次


己方飞机和敌人子弹 6*1=6次


合计:86次



而F16雷鸟号/StarForce屏幕上可以同时出现最多8个敌人,32个地面敌人,3个己方子弹(不信用模拟器暂停看)和6个敌方子弹。这样,程序每一次循环需要做判断如下:


己方子弹和敌人
3*8=24次


己方子弹和地面敌人 3*32=96次


己方飞机和敌人
40*1=40次


己方飞机和敌人子弹 6*1=6次


合计:166次



红白机的优势是有一个超时代(6502单片机的时代)的显卡,所以不用担心显示的问题。


个人电脑的处理能力远远超过红白机,比如,凤凰战斗机4代屏幕上允许同时出现64个己方子弹,48个敌人,48个地面敌人和96个敌方子弹(这些上限是很难观察到的,大多数时候都远远达不到这些数目)。这样,程序每一次循环需要做判断如下:


己方子弹和敌人
64*48=3072次


己方子弹和地面敌人 64*48=3072次


己方飞机和敌人
48*2=96次


己方飞机和敌人子弹 96*2=192次


合计:6432次


另外,考虑到个人电脑的计算速度是红白机的动辄数千倍。即使这里不用什么技巧,程序的瓶颈实际上主要还是是图形的显示,因为GDI绘图的效率相比来说太差了。



但是,如果是星际争霸的碰撞检测的话,地图上将要同时出现也许1000个单位,对于魔兽争霸3来说这个数字更加庞大,因为树木和一些装饰物体也同样拥有碰撞体积。如果单位两两之间都要检测碰撞的话,这个判断次数将是:


C10002=499500


这样仅仅这一项计算,计算机就不用干别的了。在很多网络游戏中也要涉及到处理极多单位(也许有数十万个)的碰撞的问题,尽管专用服务器的性能比个人计算机要强得多,但是这里地图的广度和单位数量远远超过单机游戏。



基本



在计算机的性能还很差的时候,有一个很巧妙的游戏叫做打砖块。因为屏幕上的砖块数量——也就是需要和小球检测碰撞的物体的数量达到了数十个以至上百个,而且游戏规则通常允许你获得一个道具后拥有三个小球。这样两个因素乘起来,计算量是很大的。


但是,这些砖块被划分在了格子里,格子的位置是很明显有规律的。我们可以简单地用一个二位数组表示砖块的分布,然后根据小球的位置计算出可能与它碰撞的砖块的索引。小球可以看作一个点(X,Y),很容易用除法计算出来它所位于的矩形空间的索引(X
/ Block.Width, Y /
Block.Height)。只有当小球穿过两个格子的边界的时候才可能发生碰撞,这时才需要判断小球即将进入的格子是否存在一个砖块。判断的对象被限制在了一个很小的范围之内,而且在这个例子中恰好判断的时机也被准确地把握。


这只是这个思想的最简单表达之一,但已经完美地表达了这个想法的核心:


1把自由的空间(矢量图)划分为格子(位图)。


2物体类带有一个“这个物体占用的格子的列表”的信息。比如图中的青色圆圈占用了4个格子。这4个格子的索引都被存储在
青色圆圈物体 相关格子区间 里。


3每一个格子类携带着“这个格子所包含的物体的列表”的信息。比如说打砖块当中砖块物体和格子是一一对应的;又比如图中
青色圆圈左下角的格子 相关物体列表 包含了一个青色圆圈和一个黄色圆圈。


4当判断两个物体的碰撞情况时,先得到其中一个物体的相关格子列表,再得到这些格子的相关物体列表。最后判断原物体和它的这些相关物体的碰撞情况。比如说图中的青色圆圈可以得到4个相关格子,然后这4个相关格子又会得到3个黄色圆圈的相关物体,检测青色圆圈的碰撞情况时,只需要检测它和3个黄色圆圈的碰撞情况就可以了,其他物体都不必考虑。


有这样一条古老的定律说,完成同样功能的算法,要么消耗更多的空间来换取时间,要么消耗更多的时间来换取空间。对于位图化的矢量图来说,节省时间的代价是,消耗了更多的空间来存储格子、格子的相关物体列表和物体的相关格子列表信息。



细节



每一种理论天生都是很完美的。但是应用到实践之中通常会发生很多意想不到的困难。我想说实践会给理论增加许多细节,也可能增加一些限制。



在一个物体移动的时候,通过它的当前位置和 临时下一时刻
位置,来判断这个物体是否会和其他物体碰撞,碰撞的话就要使这个物体的行为作出变化。


临时下一时刻
位置的另一个作用是判断物体是否离开或者进入了某个格子。这时需要更改这个格子的相关物体列表。计算这个物体的当前格子范围,以及这个物体的下一循环时刻的格子范围。


除了检测碰撞,格子 的 相关物体
属性还有另一个作用,就是决定渲染这个物体的先后顺序。即让位于画面下部(位置靠前)的物体挡住位于画面上部(位置靠后)的物体。


物体的最小尺寸应该是比格子大小的一半稍微大一点。实际上可以观察到《星际争霸》和《魔兽争霸》也是这样做的。这样做的好处是:把物体最紧密地排列,可以确定一个格子中最多可以含有排列成六角星形状的7个
相关物体。参考图中右上角的7个红色圆圈。


物体的最大尺寸(这里指的是碰撞体积,不是显示体积。选定单位时的那个圆圈不是碰撞体积,地图编辑器里绘制单位时出现的方框才是)也应该有限制,否则物体的
相关格子
太多,会影响判断的速度。《星际争霸》中,碰撞体积最大的单位是虫族的猛犸,它的高度是一个格子,宽度比一个格子宽一些,这样它可以分布在最多六个格子当中。其他的大型单位,人族的坦克,神族的执政官等等,都是正好一个格子大小,它们可以分布在最多四个格子当中。而建筑物的尺寸不受这个限制,因为它们被严格地限定在格子之上,不是自由移动,这样他们不会随时随地地改变
相关格子
属性,计算要简单许多。《星际争霸》中人族的建筑物可以自由移动,但他们要首先变成空中单位,这样它就不像地面单位那样需要计算碰撞。《魔兽争霸3》中夜精灵族的建筑物可以站起来自由移动,但是站起来之后它们的碰撞体积减小了许多,肯定会小于物体的尺寸上限。这两个游戏策划时都十分巧妙地避开了这个问题。



启示



对于这个想法,短短几天之中逐渐产生,并且渐渐明朗起来,而且似乎找到了很多证据。但是周总理说过:“现在下结论还为时尚早。”废话少说,希望大家积极深入思考探讨,批评指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值