介绍 我正在我的Android设备上开发一款游戏,不用说,因为我想要接触到尽可能多的用户,我做到了 省略了硬件加速。因此,我需要编写能够在大多数设备上运行的最快的代码。我从一个简单的表面视图开始 并使用工作线程执行视图上的所有操作。然后在测试过程中我遇到了一个问题——我的碰撞检测算法在图像边界重叠时报告了误报——尽管图像的位元本身没有重叠 背景 在本文中,我假设您已经知道冲突检测需要做什么,并且能够熟练地编写Java代码。我还假设对线程和其他UI元素有基本的了解。你只要知道就行了 基本的,但一个明确的要求是对图像如何在内存中布局有一个舒适的理解。位图对计算机来说是什么样子的。 使用的代码 如果框架(游戏、碰撞检测机制)已经就绪,经验丰富的Java开发人员可以在15分钟内编写出本文中介绍的核心概念。大多数其他开发人员,从初学者到中级 能够在1.5小时内整理出这篇文章的核心元素——当你考虑从流行的方法中休息时,这是不错的。 已经存在哪些算法? 在介绍这个相当简单的算法之前,我想花一点时间来掩盖最流行的方法,在这些方法中,仅使用软件测试冲突(不使用硬件加速)。 简单的老碰撞检测-迭代测试位图边界,直到边界矩形相交,此时报告碰撞。边界优化的碰撞检测-位图被给予一个“边界”来缩小矩形的交叉测试-当缩小区域的交叉发生碰撞报告。每像素碰撞检测-位图边界测试交叉,当交叉发生时,两个位图的每个位被测试,看不透明像素是否重叠-如果重叠发生碰撞报告。 正如您所看到的,一般开发人员在确定要实现哪种算法时要做出一组不错的选择,但是每个特定的算法都有其缺点,让我们看看可能会出现什么潜在问题。 让我们面对它,这个方法只有当你的位图是矩形的并且在图像的边缘没有透明区域时才可靠的工作,这是实现的最快的方法(也是最容易理解的)但是结果是可怕的。边界优化碰撞检测,该方法更可靠的使用常规的几何图形时,但需要添加额外的代码来区分位图的尺寸,它包含和缩小的图像,容易实现,但结果并不壮观,希望假阳性,错过了碰撞。逐像素碰撞检测——这种方法是大多数现代游戏事实上的标准,但需要硬件加速以避免游戏速度变慢。在接近任何硬件的地方编码都有一个陡峭的学习曲线,尽管使用OpenGL这样的库可以使这一点变得更容易。没有假阳性,需要中级到高级技能 如你所见,每种方法都有优点和缺点,但任何开发人员保留意见接近硬件,或者根本不愿承担硬件库的学习曲线可以放心,有一个方法来测试每个像素,同时保留一个像样的帧速率。 提出的算法 我称我的方法为n分布逐像素碰撞检测算法,它还有其他名字(抖动碰撞检测,n采样碰撞检测等), 但是,要么是缺乏文档记录的,要么是适用于大多数新手都不愿涉入的非常具体的科学应用。我的方法实现起来很简单, 我们首先写一个非常简单的普通的旧的碰撞检测算法(冷被边界优化的碰撞检测交换,但使代码更难 然后,当我们发现一个交叉区域时,我们报告碰撞,而不是报告碰撞 “intersect-rect”——这是一个矩形区域,描述了两个位图重叠区域的尺寸——聪明的程序员会简单地返回 两个矩形结构(每个位图一个),每个描述原始各自位图的子区域。 一旦我们有了交叉区域,我们只需将每个维度除以n,这在软件中是很直观的, 然后在每个维度上以n的倍数进行比较。让我们来看一个例子: 如果我们取两个图像-让我们称之为精灵(technologiese)ak表示在屏幕上绘制的任何可移动位图-为了我们的目的,我们将使用这个术语来描述绘制的任何交互式位图),尺寸为200 * 200像素。 让我们相信这些精灵在水平面上相交,重叠了50个像素,这意味着第二个精灵的左边缘与第一个精灵的右边缘重叠了50个像素。 通常我们会称其为碰撞并处理它,但对于我们的算法,我们添加了最后一步——遍历图像像素,在每次迭代(在每个图像维度中)跳过n个像素,只有当不透明像素重叠时才返回一个正的命中值。 这个算法,虽然仍然受到cpu的限制,但速度快了n个数量级,让我们做一下数学运算: 对于逐像素碰撞检测算法,我们会比较两个源图像的每个像素,直到在图像的不透明区域找到重叠——傻了吧,我们可能最终会比较几乎两个图像的每个像素。 这将给我们一个200 * 200像素的比较(如果精灵们正坐在彼此之上),这将给我们一个总计40000像素的比较操作。对于适应算法,我们可以用任意小的数来表示n,我喜欢用n=2来表示好的结果。 当n = 2时,我们比较(200/n)*(200/n)像素,这样就得到了100*100次比较,总共有10000次比较。我们将对比次数减少了四分之三! 在现实世界中,我们将主要使用较大的图像,并且我们将主要比较图像的子部分,因此我们几乎总是以小于(宽度/n) *(高度/n)像素的比较结束-这在大多数情况下可以提供一个非常显著的加速。我喜欢用更大的n值进行实验,在这种情况下,极端的精确度不是一个限制因素,这样可以减少比较的次数 给我们更多的CPU奉献给其他游戏逻辑子系统-如物理。 让我们看一些示例代码 我对这一代码的正确性不作任何声明,因此对执行本文提出的想法后可能发生的任何崩溃或伤害不承担任何责任: 隐藏,收缩,复制Code
// This function is written specifically to test intersections only // on the horizontal plane - meaning only sidelong collisions // are reported - this is easily generalized to any/all axis(es) boolean detectCollisions(Sprite sprite) { //I assume that _sprites is an ArrayListwhich contains //the bounding regions of the sprites (As well as the bitmaps) we are hit testing. int n = 2; Rect overlap = new Rect(0, 0, 0, 0); for each (Sprite _object : _sprites) { if (intersect_rect(sprite.rect, _object.rect, overlap)) //if the two rectangles intersect, we determine the intersect regions and perform the pixel test { //let's also pretend that we went through the schlep of figuring //out which rectangle is on the right and which is on the left //the intersect_rect functions returns the intersecting region //in the overlap rect, if the width and height are both non zero, then we have overlap! if (overlap.getWidth() && overlap.getHeight()) if (intersect_pixels(overlap, n, sprite, _object)) return true; //if no collision is detected, then continue testing the rest of the sprites } } } //This function takes an ordered pair of sprites and tests their //images using dithered algorithm to determine if opaque regions overlap boolean intersect_pixels(Rect _overlapRegion, int n, Sprite left, Sprite right) { for (int y = 0; y < _overlapRegion.Height() / n; y+=n) { for (int x = 0; x < _overlapRegion.Width() / n; x+=n) { //did not add test to check if pixel is on image bounds, //since overlapping region assumes to be within the bounds of BOTH images //the left image has its bounds starting BEFORE _overlapRegion int left_image_color = left.image.getPixel(_overlapRegion.left + x, _overlapRegion.top + y); //the right image has its bounds starting AT _overlapRegion int right_image_color = right.image.getPixel(x, y); if ((Color.alpha(left_image_colour) != 255) || (Color.alpha(right_image_colour) != 255)) return true; //there are non-transparent pixels which overlap! return false; } } }
记住,你仍然需要写intersect_rect函数-我还是把这个留作参考吧 练习在家做,这里给出的大部分代码是功能性的 从我的游戏中提取。我有意省略了重叠区域测试函数 给读者做练习。 的兴趣点 我最初编写的所有碰撞检测函数都是为了严格地处理矩形,它们很快就向南延伸, 因为我需要额外的变量来跟踪循环索引,并且需要回调来通知交叉区域已经找到。我后来才知道 将矩形和图像封装在一个名为Sprite的类中要简单得多,这样就可以在一个方便的对象中访问所有与Sprite相关的功能。 有趣的是,对于小图像来说,优化逐像素检测并不重要,因为循环迭代通常不会拖低图像 性能太高了,但是当图像的宽度和高度超过100像素时,就需要进行智能像素测试了,因为有一个简单的10个精灵 在屏幕上(彼此垂直地坐在一起,每个100*100像素)很快就会进入测试10 *(100 *100)*10 *(100 *100)像素或(10000000000像素)。 现代显卡可以在几毫秒内轻松地测试这么多像素,但在软件中执行同样的操作可能会拖垮最厉害的处理器, 我通常发现n的值与图像的大小直接相关,并且测试表明,在任何维度上超过200像素的图像都有好处 极大地从更大的n值-注意,它警告使用的n值超过图像的任何维数(i1)。如果你的图像尺寸是10*5,那么n应该是<5) 历史 1月11日-更新文章来源,以纠正语法错误。目前,该算法满足了我的实现对速度的要求,没有任何改进。 本文转载于:http://www.diyabc.com/frontweb/news30810.html