《碰撞检测》阶段性小结报告
时间:2002.11.28
话题名称:关于碰撞问题的探讨
话题发起人:sbljx
发起时间:2002-9-23 10:20:24
第一总贴:http://www.mobiusclub.com/bbs/dispbbs.asp?boardID=12&ID=12
(01)sbljx:-------------------------------------------------------------------------------------------------------
大家现在都用的什么碰撞原理?认为现有的碰撞原理合理吗?存在漏洞或缺陷吗?我认为这个问题已经到了要解决的时候了。怎样用计算机高效地模拟现实世界中的碰撞?
(04)sbljx:-------------------------------------------------------------------------------------------------------
首先,先看看一般的碰撞原理有什么缺陷。
拿两个像素点的一维运动和碰撞来举例,分别称之为Point1、Point2。假设我们赋予Point1的水平移动速度为1,而赋予Point2的水平移动速度为10,假设Point1的初位置为(0,0),Point2的初位置为(30,0)
好,开始讨论!!!
(05)admin:------------------------------------------------------------------------------------------------------
很多时候我们是依靠精灵的运动然后获得精灵位置来判断碰撞检测。但是这里有两个过程:
1.director内部处理精灵的位置移动。
2.我们获得精灵的位置属性。
显然第二个是非常费时间的。
我想可以(已经有人这样提示过,cjx2000)用变量运算来替代前述的第一个环节!
所有的位置变动驱动都是直接针对这个变量。
然后第二步就是将变量反馈给精灵属性。
这样无疑速度会提供很多!
这个过程中director始终没有参与精灵位置的运算。仅仅是更新以及基于变量的运算(这个因为和舞台图象无关,所以速度提供很多吧!?)。
好!继续1
接下来我想的是如何仅仅依靠相关的变量来实现碰撞问题的检测!
因为处理变量的检测要快的多!...
好了!只是想法!具体还没来得及实验!
(06)stiff:--------------------------------------------------------------------------------------------------------
Dir是基于时间轴,以帧的事件来触发行为。但究竟是多少时间执行一次呢?是我们设置的帧速率吗?如果帧速率是25F/S,那么是每1/25秒执行一次当前帧下的lingo脚本吗?碰撞根据什么进行呢?我想应该是基于时间的吧。也许有人会说是基于Windows的消息机制,但Windows的消息多长时间执行一次呢?这个时间到了Dir中又会变成多少呢?如果在当前帧下有两个行为:
1.on exitFrame me
2.on exitFrame me
那么,这两个脚本同时执行时是什么情况呢?是不是第二个exitframe在执行repeat的时候第一个exitframe就停止不动啦?当然不是,第一个exitframe照样执行他的代码。但如果两个exitframe中都有repeat循环语句,那怎么检测呢?实际实验结果就是先把第一个exitframe执行完,然后执行第二个exitframe。所以,你要想在两个行为中对同一物体的属性进行检测是得不到正确结果的。因为他会在外界因素干扰完的情况下才会响应你的操作。
因此,用一个行为检测要碰撞物体的属性,用另一个行为进行相关设置,我认为是有时间差异存在的!即,要想实现检测,最好在一个行为中进行,或者在同一段代码中执行,不要为碰撞的两个物体设置类似的检测代码,不要说看谁先碰到谁,而要以第三者的身份同时对目标进行检测。
如果上述文字成立的话,应该可以扩展到所有的行为检测中去。
(08)sbljx:-------------------------------------------------------------------------------------------------------
你们两位对我的启发非常之大,真的是非常大。头一次这么明显地感觉倒,团结也可以使思考产生更耀眼的火花!!!
three-m的变量计算可以前面提到的计算机所能够达到的速度上限大大提高。
stiff的第三方检测变量不仅能够使碰撞检测做到同时,而且能够大大节省系统资源。
呵呵,我试着结合两位高手的精华做个简单的碰撞检测机制看看。
另外,鉴于three-m的变量算法,我决定物体运动机制完全模拟现实世界的运动!!!也就是说,按照我上面提到的第一个方向来研究。呵呵,好像看到曙光了。
(09)sbljx:-------------------------------------------------------------------------------------------------------
是依次顺序执行的。任何事情都有先后的。不可能同时执行。
(10)stiff:--------------------------------------------------------------------------------------------------------
我的意思是两个exitframe有时是执行完一个后才会去执行另一个。如果你在一个exitframe中用循环设置物体的位置,在另一个行为中用循环检测其属性,得到的应该永远是最后一个位置,即第一个exitframe执行完后的结果,中间改变位置后的结果可能检测不到。
(11)sbljx:-------------------------------------------------------------------------------------------------------
没错!
(12)sbljx:-------------------------------------------------------------------------------------------------------
研究编写了一个基于不同机制的运动碰撞,大家下载后看看,我想说的都在源文件中。
(13)sbljx:-------------------------------------------------------------------------------------------------------
呵呵,three-m终于上道了。你说的完全正确!!!呵呵,我做的那个例子就是基于速度仅为1的运动机制。体现速度的方法也可以从我那个大长贴中找到。在我那个例子中所有物体的运动速度无论如何都是1。但要想体现物体运动的快慢,就得改变触发物体运动的频率。
我那个例子就是这样的。继续研究。
(14)stiff:--------------------------------------------------------------------------------------------------------
如此说来,你的核心思想就是要改变 “模拟世界中的最小时间单位” 这个时间单位,以获得最小的执行时间吗?three-m也说你是用on idle来处理,但我对idle没有多少太明确的概念:(
(15)stiff:--------------------------------------------------------------------------------------------------------
sbljx是利用intersect判断,three-m是利用rect重合判断,对吗?
(16)sbljx:-------------------------------------------------------------------------------------------------------
three-m的例子看过了。那个例子很简单啊。可是我要问几个问题
1.我要改变物体运动快慢怎么办
2.物体的垂直方向上的速度怎么控制
3.呵呵,这个问题以后再问
(18)sbljx:-------------------------------------------------------------------------------------------------------
物体的垂直方向上的速度怎么控制?我是说你那个例子只能支持物体的水平运动,我想要垂直运动,或者水平与垂直叠加的效果。
(19)sbljx:-------------------------------------------------------------------------------------------------------
以下是引用Three-m在2002-9-27 11:51:28的发言:
...
这样确实可以,但要想在程序中动态控制物体运动速度怎么办?不能每次都用手工改吧。呵呵,等你的程序满足了我的一系列要求后,就会发现,跟我的程序一样复杂了。
(20)sbljx:-------------------------------------------------------------------------------------------------------
以下是引用Three-m在2002-9-27 15:33:22的发言:
垂直运动一样的啊!在offset中设置即可。但是如果是一成非45度角度的,就要复杂一些
http://www.mobiusclub.com/bbs/uploadImages/200292715331646931.rar
当然要所有角度的。也就是说,垂直速度和水平速度是不同的。
(21)sbljx:-------------------------------------------------------------------------------------------------------
大家,我正在做一个运动系统,包括物体运动,碰撞,力的作用等等。呵呵,蛮有趣的,如果做得好说不定会有些价值
(23)sbljx:-------------------------------------------------------------------------------------------------------
对。所以针水平和垂直要有各自的计时器。我那个OBTimeup句柄之所以复杂,就是因为这个原因,另外还需要一个计时器,那就是控制物体刷新位置的频率。所以OBTimeup句柄中一共有三个计时器工作,所以看起来当然复杂。
另:我的运动系统是基于面向对象的。所以有一堆函数方法可以调用。呵呵,到时,我会给出相关文档。
(00)第一贴截止时间:2002-10-30 17:42:42
发起人:truka
发起时间:2002-11-6 17:40:11
网站链接:http://www.mobiusclub.com/bbs/dispbbs.asp?boardID=12&ID=577
(01)truka:-------------------------------------------------------------------------------------------------------
大家好,我第一次来学术讨论区。
看过大家的<关于碰撞问题的探讨>一贴,觉得焦点问题是集中在如何解决瞬间穿透上面。
之前我做过一个桌球游戏,也碰到过类似的一些问题,我想就我的一点经验谈谈我的看法。
任何游戏都是基于帧的形式来运作的,就是说游戏中的事物是跳动式进行的(无论它多么接近于连续进行,但用远不可能达到),而自然界的事物则是无限连续的。在检测碰撞这个问题上确实可以用提高检测频度来获得更精确的碰撞点位置,但是我觉得这样做的代价太大,一个游戏必须有稳定的帧速率,提高检测频度无疑会使游戏速度直线下降。所以我在制作桌球的时候放弃了这种思路,取而代之是另一种思路,我称它为穿透纠正。
如果要检测两个物体之间有没有发生碰撞,必须实时监测两者之间的距离(这个代价是很小的)。在两物体发生碰撞之前,它们的边缘距离(对于两个球来说就是它们的圆心距离减去它们的半径之和)必然是在逐渐缩小(否则就不会发生碰撞),在它们发生碰撞之后边缘距离必然是逐渐扩大。如果我们实时监测着这个边缘距离增量值,当检测到增量值由负变为正的一刻,程序就转入穿透纠正模块。
此时可以确定碰撞发生点肯定在物体此刻的位置和它的上一个位置(上一帧的位置)之间。如果要计算出足够精确的碰撞发生点,我们需要几个值:物体此刻的位置和速度,物体在前面一帧时的位置和速度。这4个值很容易得到。
接下来是计算足够精确的碰撞发生点。思路是这样的,让物体从上一帧的位置放慢速度慢慢前进到此刻的位置。当然这个过程需要放大,才能得到足够的精度,也就是说水平方向和垂直方向的速度必须分别取上一帧时的值后再按比例缩小。在这个过程中需要检测两物体之间的距离,当它们边缘距离的绝对值在一个允许的范围之内时便停止这个过程并认为此时物体的位置就是碰撞发生位置(对于不规则外形的物体可以用检测重叠函数代替检测边缘距离的过程)。当然在这个过程中如果两物体都在运动的话,两物体的穿透纠正需要同时进行。穿透纠正这个过程是快速的数值推算,是不可见的。另外说明一下,非直线非匀速运动同样适用。
以上方法在我的游戏中有应用,效果很满意。
(05)sbljx:-------------------------------------------------------------------------------------------------------
但是这个算法仍然有问题。
试想这样的情况,两个物体做追踪运动,A在前,B在后,一开始B的速度大于A,则他们之间的距离不断减小,但在B未追上A之前,A突然加速,且速度大于B,这是他们之间的距离就变大了,那么如何区分这不是碰撞呢?
再有,两个物体的运动轨迹如下图1所示,那么我肯定这两个物体的距离增量值曾经由负变为正,但是很显然,这两个物体很可能从来都没相遇过,那又怎么判断这种情况也不是碰撞呢?
还有一些类似的问题,不过等大家一起解决了这两个问题再说。我以为是不是可以这样,就是即使发生了一上的两种情况,依旧做穿透纠正运算,但真金不怕火炼,如果没有相遇过,那么运算结束后肯定不能得到相遇点,这样就可以判断了,truka还有好方法么?
靠,忘了传图了,等会补上
(06)sbljx:-------------------------------------------------------------------------------------------------------
图一
(07)truka:-------------------------------------------------------------------------------------------------------
第一个问题容易解决,如果由于发生突然加速或其他原因在整个穿透纠正的计算过程中两物体边缘距离的绝对值从未在一个允许的范围之内,碰到这种情况就认为碰撞没有发生,此过程结束,运动继续进行.
第二个问题,对不起,是我的一个笔误,现纠正一下.原来的"由正变为负"应该是"由负变为正"才对,感谢sbljx指正.
当然要让这个方法的使用范围更大还需要大家一起来优化它.
(08)sbljx:-------------------------------------------------------------------------------------------------------
第二个问题中,这两个物体的距离增量值肯定也有由负变为正的一刻。
另外,一般来讲,绝对值从未在一个允许的范围之内,这个范围是多少呢?与物体大小有关?
(09)truka:-------------------------------------------------------------------------------------------------------
1)许多时候确实会发生一些不必要的穿透纠正,也就是说穿透纠正的过程中得不到碰撞点的情况,如果不采取任何措施,程序一样可以正常运行,只是会增加一些不必要的运算量消耗资源。这可能是sbljx关心的问题。
如果把满足穿透纠正的条件改一下,增加一项检测就可以在很大程度上减少不必要的穿透纠正发生。这项检测就是两物体间边缘距离是否在满足发生碰撞的最大范围之内(对于两个圆形物体来说,这个范围略大于它们的半径之和,对于不规则物体还需要再扩大一点范围),如果不是则认为是一次不必要的穿透纠正。
2)这个范围与物体大小有关。对于不规则物体,这个范围不太好确定,此时采用重合函数检测比较可取,因为此时物体的移动可以认为是连续的(推算时的"移动速度"不大于1像素/帧)。
(10)sbljx:-------------------------------------------------------------------------------------------------------
明白,回来我总结一下,写成教程,呵呵。
(11)truka:-------------------------------------------------------------------------------------------------------
确实需要不断地完善,这种思路起源于桌球游戏中的碰撞点纠错算法。
但是桌球游戏的运动相对是比较单调的。永远是减速运动,运动路径多数是直线,全部是圆形物体。
我觉得sbljx的问题很有建设性,对完善具有普遍通用性的穿透纠正算法很有帮助:)
(12)sbljx:-------------------------------------------------------------------------------------------------------
呵呵,我算是明白了,你们敢情在底下已经研究了很多的东西,怪不得那么厉害,佩服中,希望以后多多把东西拿出来大家一起讨论啊,对于你们这样的高手来说,很可能平时的一点心得就能成为这里研究发生实质性突破的关键啊,记得常来哦。
(14)truka:-------------------------------------------------------------------------------------------------------
做了个例子。在这个实例中除了讨论过的穿透纠正以外,还有嵌入纠正的处理。
按下鼠标左键吸引红球,右键吸引白球。<shift>和<ctrl>可以辅助操作,建议操作时同时按下<shift>和<ctrl>。
http://www.mobiusclub.com/bbs/uploadImages/2002111112583833485.dir
(00)第二贴截止时间:2002-11-15 14:40:12
发起人:stiff
发起时间:2002-9-27 16:41:15
网站链接:http://www.mobiusclub.com/bbs/dispbbs.asp?boardID=12&ID=40
(01)stiff:--------------------------------------------------------------------------------------------------------
if sprite s intersects pGloveSprite then
if inside(sprite(s).loc, sprite(pGloveSprite).rect) then
if (sprite(s).locH - sprite(pGloveSprite).locH) <= pCatchDistance and
if sqrt(power(sprite(s).locH - sprite(pGloveSprite).locH,2)+?
看看老外,他用了好几个方法进行检测,但都没有sbljx来到专业和精确:)
http://www.mobiusclub.com/bbs/uploadImages/20029271641921617.zip
(02)sbljx:-------------------------------------------------------------------------------------------------------
呵呵,要解决碰撞的问题,先要确定是在那种运动机制下。
(03)sbljx:-------------------------------------------------------------------------------------------------------
我的意思:先要确定运动机制,才能讨论碰撞数学模型。所谓运动机制,咱们现在就知道两种:
1.改变单位时间的移动距离来改变物体运动快慢,这种机制会发生“瞬间穿透”
如sprite(1).loch=sprite(1).loch+2
sprite(1).loch=sprite(1).loch+5
2.每次物体移动的距离永远是1、-1、0,通过改变物体位置改变频率来改变物体运动快慢。这种机制不会发生“瞬间穿透”。
如:
每1秒触发一次:sprite(1).loch=sprite(1).loch+1
每10秒触发一次:sprite(1).loch=sprite(1).loch+1
这样就产生了不同的运动速度
先确定用哪种运动机制。然后才能对症下药啊。
(04)sbljx:-------------------------------------------------------------------------------------------------------
交错没关系,不穿透就可以。而且,一个像素的交错可以忽略不计。或者这么想,把这种一个像素的交错想象成两物体碰撞后产生的挤压。呵呵。
但穿透现象就无法令人接受了,因为计算机根本就检测不到发生穿透后的两个物体曾经相遇过。
(05)alphachi:---------------------------------------------------------------------------------------------------
关键是碰撞的瞬间如何检测到物体的状态, 可必须要保证足够高的播放帧数才能够得到足够高的精确率,恶性循环呀...
(06)sbljx:-------------------------------------------------------------------------------------------------------
以下是引用alphachi在2002-10-24 15:11:16的发言:
关键是碰撞的瞬间如何检测到物体的状态, 可必须要保证足够高的播放帧数才能够得到足够高的精确率,恶性循环呀...
是的,要在相当高的播放速度下才行,最高的播放速度是直接在死循环中处理所有事件。
(00)第三贴截止时间:2002-10-24 15:14:55
第一部分:论坛资料整理
sbljx:碰撞检测原理.doc