Android设备上的逐像素碰撞检测
介绍 我正在我的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 ArrayList<Sprite> which 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
Android设备上的逐像素碰撞检测的更多相关文章
- 非root Android设备上Tcpdump的实现
通常我们在Android应用中执行某个命令时会使用"Runtime.getRuntime().exec("命令路径")"这种方式,但是当我们执行抓包操作时,使用 ...
- 在ios android设备上使用 Protobuf (使用dll方式)
http://game.ceeger.com/forum/read.php?tid=13479 如果你的工程可以以.Net 2.0 subset模式运行,请看这个帖子中的方法. 地址:http://g ...
- 如何查看Android设备上的分区信息
Android设备上,一般都会存在一块eMMC存储芯片来存放系统和用户数据,甚至部分的引导程序. 一般设备出厂时,各个厂商都会将这块存储芯片分成很多的分区,每个分区内存放不同的内容.具体分区的布局每个 ...
- (转)在ios android设备上使用 Protobuf (使用dll方式)
自:http://game.ceeger.com/forum/read.php?tid=13479 如果你的工程可以以.Net 2.0 subset模式运行,请看这个帖子中的方法. 地址:http:/ ...
- android设备上运行i-jetty服务
android设备上运行i-jetty服务: 1) i-jetty安装 本人小菜一个,i-jetty源码有好几个文件,不知道怎么运行起来,于是找了一个现成可运行的i-jetty工程(感谢这位同学的分享 ...
- Android设备上i-jetty环境的搭建-手机上的web服务器
本文主要跟大家分享如何将一台Android设备打造成一个web服务器使用. 编译i-jetty 1.将源码download下来,http://code.google.com/p/i-jetty/dow ...
- 如何通过Chrome远程调试android设备上的Web网站
网上的帖子很多,但很多都是老版本的,试过了,根本不管用,花了一天时间,终于在本机试验通过了,特记录下来,以备用.有需要的朋友也可以参考.先上一张图,看看PC端chrome上调试的效果: 左边是手机的模 ...
- 在android设备上调试ionic应用
方法1: ionic run android -l -c 将会在console中输出日志信息 方法2: (1).使用usb连接android设备,并打开android设备的调试功能 (2).在chro ...
- 18、ESC/POS指令集在android设备上使用实例(通过socket)
网上关于通过android来操作打印机的例子太少了,为了方便更多的开发同仁,将近日所学分享一下. 我这边是通过android设备通过无线来对打印机(佳博58mm热敏式-58130iC)操作,实现餐厅小 ...
随机推荐
- AngularJS中的父作用域与自作用域
对于$scope上的原生类型,如$scope.name=""; 自作用域获取变量时,会查找作用域本身,找不到就会查找父作用域 修改时,若本作用域不存在,就会在本作用域创建一个变量, ...
- 解锁用户scott并授权
请输入用户名: system 输入口令: 连接到: Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Producti ...
- python中的n次方表示法 **n
例题:计算2的n次方,n由用户输入(N次方用**表示)# n=eval(input('手动输入n的值:')) #个人感觉,不确定是int还是float时,用eval来表示,eval后面接表达式# pr ...
- [深入理解JVM虚拟机]第2章-Java内存区域与内存溢出异常
2.0引-Java内存区域中,栈内存和堆内存分别装什么,为什么? 栈:解决程序的运行问题,即程序如何执行,或者说如何处理数据. 堆:解决的是数据存储的问题,即数据怎么放,放在哪儿. 参考链接https ...
- 掌握Rabbitmq几个重要概念,从一条消息说起
RabbitMQ 是功能强大的开源消息代理.根据官网称:也是使用量最广泛的消息队列.就像他的口号“Messaging that just works”,开箱即用使用简单,支持多种消息传输协议(AMQP ...
- Linux实战(19):Shell交互式read 用法
read 用法有好几种,我在实战过程中用到了 -p,记一笔以防不用忘记了. 实例 #!/bin/bash echo "检测IP是否被占用" while read -p " ...
- Linux实战(13):Centos8安装FFmpeg
此文章所做的操作参考漏网的鱼:参考链接 步骤1:安装RPMfusion Yum存储库 RHEL或兼容发行版(如CentOS)上启用EPEL. dnf -y install https://downlo ...
- javaweb开发中的常见错误
Javaweb中的最常见错误及其解决方法 1.200:表示成功处理业务. 2.400 请求出错: 由于语法格式有误,服务器无法理解此请求.不作修改,客户程序就 无法重复此请求. 解决办法:,遇到400 ...
- Spring学习(七)bean装配详解之 【通过注解装配 Bean】【自动装配的歧义解决】
自动装配 1.歧义性 我们知道用@Autowired可以对bean进行注入(按照type注入),但如果有两个相同类型的bean在IOC容器中注册了,要怎么去区分对哪一个Bean进行注入呢? 如下情况, ...
- 金蝶k/3 cloud 生产用料清单下推生成调拨单二开记录
系统默认的生产用料清单下推生成调拨单功能,是根据调拨选单数量来的,有库存和没有库存的都混在一起,导致业务人员审核调拨单的时候需要删除没有库存的分录行,严重影响工作效率. 现通过二开程序,根据生产用料清 ...