DIRECTOR 之游戏碰撞学一
来源:互联网 发布:ubuntu搜狗输入法 乱码 编辑:程序博客网 时间:2024/05/29 17:57
碰撞检测原理前言
开门见山,我们将要研究一下如何检测和处理物体与物体之间的碰撞事件。
先来概括了解一下,我们说,碰撞是物体运动过程中发生的事件,所以我们要从研究物体运动的原理入手,尤其是用计算机模拟的物体运动的原理,搞清楚这个,才能在此基础上进一步探讨碰撞的发生及检测处理。
不知大家都是如何另一个物体运动的,也许你会说,这个easy!改变它的位置就行了,比如,在Director中,我们想要让一个精灵水平向右移动,最简单的方法就是一句lingo:
sprite(whichsprite).loch= sprite(whichsprite).loch+1
通过改变物体的位置来形成物体的运动效果,OK!这个没有问题,但是问题出在这里,当我们向要改变物体运动的快慢怎么办呢??比如说,我们想要上面的那个精灵的运动快上5倍,该如何做呢?我以前是这样做的:
sprite(whichsprite).loch= sprite(whichsprite).loch+5
表面看来,这没有问题,这样的运动速度确实提高了5倍,对于简单的运动来说,这个没什么缺陷,但如果物体的运动比较复杂,尤其是有了碰撞事件,这就产生了很大的麻烦。来看看是什么样的麻烦。
拿两个像素点的一维运动和碰撞来举例,分别称之为Point1、Point2。假设我们赋予Point1的水平移动速度为1象素/秒,而赋予Point2的水平移动速度为-10象素/秒,假设Point1的初位置为(0,0),Point2的初位置为(30,0),如下图:
回到我们最初物体运动机制的探讨,还是那句lingo:
sprite(whichsprite).loch= sprite(whichsprite).loch+5
显然,我们是想要让精灵以每次移动5象素的速度来运动,现在,为了避免“瞬间穿透”,我们将只能使用:sprite(whichsprite).loch= sprite(whichsprite).loch+1,那么怎样改变物体运动的快慢呢?很简单,原本每秒钟移动一个像素距离,这回我让它每0.2秒移动一个像素,怎么样,这样,运动加快了吧。总而言之,就是通过控制触发物体位置改变的频率来控制物体运动的快慢,到这里,我们来对比一下两种方法的区别,本来物体运动速度都是1象素/秒,即:
sprite(whichsprite).loch= sprite(whichsprite).loch+5
现在,两种方法都以达到5象素/秒的效果为目的。
l
sprite(whichsprite).loch= sprite(whichsprite).loch+5
l
每0.2秒执行一句sprite(whichsprite).loch= sprite(whichsprite).loch+1
OK!基本原理就是这样,我们说讨论物体的碰撞,就是要在这两种不同的运动机制下分别讨论。对于机制一,会产生“瞬间穿透”,所以讨论的要点就是如何避免这个问题;对于机制二,“瞬间穿透”倒是没有了,所以对于简单的碰撞,直接判断两物体是否接触就可以了,但遗憾的是,我们发现了另外一个问题,那就是基于此运动机制的运动速度上限的问题。怎么会产生上限呢?接着上面的例子,我们使用第二种机制,把物体运动速度提高到
10象素/秒,OK,只要每0.l秒移动一象素就行,那么1000象素/秒呢?每一毫秒移动一象素!那么,10000象素/秒呢,0.1毫秒??请问,lingo中哪来的0.1毫秒,即使是更低级的编程语言,恐怕也有个最小时间单位难以逾越吧。这样,对于lingo, 1000象素/秒就成为了速度上限。更何况,如果需要处理的运动物体多了,恐怕1000象素/秒也只是个理论值而已。我们只能想方设法在某种程度上解决这个问题,这个咱们以后再详述。所以,到这里,总结一下第二种机制下需要解决和研究的问题,那就是解决速度上限所带来的种种弊端,更为本质地说,就是要解决我们计算机中无法避免的最小时间单位的问题。好!下一讲我们继续。
碰撞检测原理之绝对运动篇
在前言中,我们知道要研究碰撞,就要分两种不同的运动机制来研究,第一种是基于改变触发位置改变事件的频率的机制,我们不妨称之为绝对运动机制,第二种基于改变每次位置的改变量的机制,说起来很饶口,不清楚的话,一定要再去回顾一下前言哦!
这次我们就绝对运动机制来研究一下。主要研究目的有两个:
1. 如何实现绝对运动,包括绝对运动的速度控制。
2. 绝对运动下的物体碰撞处理。
实现绝对运动的关键是控制物体运动速度的处理,由绝对运动原理,我们知道,物体每次改变的位置的偏移量是不变的,永远为1,所以我们要很方便的控制触发位置改变事件的频率,因此,要用到计时器的概念。所谓计时器,就是每隔一定的时间间隔,就触发某一指定事件的机制,下面是一个简单的计时器的例子:
Time=Time+1--时钟
LastTime=Time—上次触发超时事件的时间
Timer=5--触发超时事件时间间隔
if Time-LastTime> Timer then
end if
不难看出,如果我们的TimeUp()句柄如果是这样:
on TimeUp
end
那么改变计时器中Timer的值就可以改变触发位置改变事件的频率,也就达到了改变物体运动快慢的要求。
以上只说明一下原理,那还远远不够,因为我们并不习惯于改变触发位置改变事件的频率而改变物体运动的速度,而是希望能够直接设置物体的运动速度。这就需要我们自定义一个函数来分析我们传递的希望达到的速度值从而再由程序内部处理改变相应的触发位置改变事件的频率。形象点说,我们希望物体运动速度为1象素/秒,那么好,程序就将自动分析,哦,我应该每秒钟让物体的位置改变1象素,如果我们希望物体运动速度为5象素/秒,程序就会每0.2秒让物体的位置改变1象素。
另外,一维的运动显然不够,如果要产生更为复杂的二维运动效果,就需要为物体的另一维上的运动也增加一个计时器。
还有一个机制也很重要,那就是我们运动物体的运动必然要体现在图像的移动上,物体的位置改变了,就需要在舞台的新位置上重新描绘改物体的图像,不妨称之为图像刷新,那么,为了尽可能的减少系统的负担,我们不能每时每刻都进行图像刷新,只要在需要刷新时再刷新就好。于是我们整个物体运动的运算都先是仅对对象的位置属性进行处理,图像刷新的频率也由我们定,图像刷新越高,运动越平滑,但系统的负担越大,图像刷新越低,物体运动效果越跳跃,但系统的负担会轻些。
原理叙述完毕,下面是一个比较完整的实例(源文件ABSMoveMent.dir),采用面向对象的机制。
首先在通道1、2上添加两个精灵,精灵的位置随大家好啦,然后建立一个父脚本,脚本内容如下:
Property MySprite--本对象所使用的精灵
Property speed_x,speed_y--运动物体的水平上和垂直方向上的绝对运动速度,其值只能为-1,0,1
Property MyTime--本对象的时钟系统
Property MoveXTimer,MoveXRate--本对象的水平运动计时器和触发水平位置改变的频率
Property MoveYTimer,MoveYRate--本对象的垂直运动计时器和触发水平位置改变的频率
Property RefreshTimer,RefreshRate--本对象物体图像刷新计时器和刷新频率
Property MyLocation--本对象用来控制物体位置的变量
on new me,fSpriteNum
end
--总计时器触发事件
on TimeUp me
end
--根据希望达到的水平速度数值改变水平位置改变事件的触发频率
on setspeedxme,fspeedvalue
end
--根据希望达到的竖直速度数值改变竖直位置改变事件的触发频率
on setspeedyme,fspeedvalue
end
建立一个电影脚本,在电影开始时建立两个对象,分别使用前面的两个精灵,并将两个对象的水平速度分别初始化。
on startmovie
end
然后在电影脚本中用一个死循环来触发两个一建立对象的超时事件,这是为了获得最高的事件触发频率。我们可以用一个个更合理的总计时器来更好地控制各个对象的超时事件的触发。另外,我们将时时判断,如果鼠标按下,则结束程序。
on idle
end
好,运行电影,看看物体如愿以偿的运动起来了,改变两个物体的初始速度,看看灵不灵?表面看上去,这与我们以往的效果差不多,但如果我们现在来进行一下碰撞检测,会发现,这个过程变得很简单直接。我们只需时时判断两个物体是否重合相遇就行了,即使运动速度再大,也不用担心会发生“瞬间穿透”现象。
但是,这个运动机制还有很多的问题需要解决,而且,要想应用这种运动机制,需要完善的地方还很多,我们不妨暂时了解这个机制到这种程度,等待未来水到渠成的那一天。
此次研究的目的还有一个,那就是绝对运动下的物体碰撞处理。也许大家会问,怎么,这种机制下的碰撞不是直接判断就可以了吗?是的,这种机制带来的指时最底层碰撞判断的方便,但是想想一下,如果有多个运动物体,且每个物体的运动都很复杂,那么同时检测所有物体之间的相互碰撞就变成了一个庞大复杂的过程,怎样最高效合理地实现这个过程就是我们要解决的一个难题。这个,到现在我也没有一个满意的结果,就交给大家去探究了,所以,此次研究就到这里,不了了之?^_^
碰撞检测原理之模拟运动篇
之所以我们会考虑绝对运动的机制,是因为这种运动机制能够避免“瞬间穿透现象”。但不可否认,目前,想要马上应用这种运动机制是不现实的。那么,如果我们保留原有的运动机制,能否通过一些手段来避免或者说纠正“瞬间穿透现象”呢?答案是肯定的。这里,我们感谢网友Truka的技术支持,正是他的研究成果,使我们的碰撞得到了最现实的解决方案。
以下一段话摘自Truka的技术文章:
任何游戏都是基于帧的形式来运作的,就是说游戏中的事物是跳动式进行的(无论它多么接近于连续进行,但用远不可能达到),而自然界的事物则是无限连续的。在检测碰撞这个问题上确实可以用提高检测频度来获得更精确的碰撞点位置,但是我觉得这样做的代价太大,一个游戏必须有稳定的帧速率,提高检测频度无疑会使游戏速度直线下降。所以我在制作桌球的时候放弃了这种思路,取而代之是另一种思路,我称它为穿透纠正。
如果要检测两个物体之间有没有发生碰撞,必须实时监测两者之间的距离(这个代价是很小的)。在两物体发生碰撞之前,它们的边缘距离(对于两个球来说就是它们的圆心距离减去它们的半径之和)必然是在逐渐缩小(否则就不会发生碰撞),在它们发生碰撞之后边缘距离必然是逐渐扩大。如果我们实时监测着这个边缘距离增量值,当检测到增量值由负变为正的一刻,程序就转入穿透纠正模块。
许多时候会发生一些不必要的穿透纠正,也就是说穿透纠正的过程中得不到碰撞点的情况,如果不采取任何措施,程序一样可以正常运行,只是会增加一些不必要的运算量消耗资源。
这个范围与物体大小有关。对于不规则物体,这个范围不太好确定,此时采用重合函数检测比较可取,因为此时物体的移动可以认为是连续的(推算时的"移动速度"不大于1像素/帧)。
以上就是原理说明,并且感谢Truka提供的源文件范例(TowBalls.dir),在这个实例中除了讨论过的穿透纠正以外,还有嵌入纠正的处理。
不难看出,基于改变每次位置的改变量的运动机制所面临的问题仅仅是在物体碰撞一刹那可能会发生“瞬间穿透”现象,因此我们就要跟踪物体的运动情况和物体之间的距离变化情况以判断物体之间是否发生过碰撞现象,然后再经过计算得出碰撞的精确位置,根据碰撞后的计算结果调整物体的运动状态,这种碰撞解决方案是在Director常用的运动机制的基础上进行的纠正计算,因此,不仅能够保留Director动画机制的优势,由有效地将其劣势减到最低,确实是一种合理的方案。
- DIRECTOR 之游戏碰撞学一
- DIRECTOR 之游戏碰撞学二
- 游戏中的碰撞(一)
- 《游戏觉醒之Cocos2d-x3.10游戏开发》- 导演(Director)
- 游戏开发之碰撞检测
- Java游戏之碰撞检测
- 一步一步学做游戏 第四回:熊碰撞边界处理
- 一步一步学做游戏 第五回:熊碰撞蘑菇处理
- Java简单游戏开发之碰撞检测
- [Unity3D]Unity3D 游戏开发之碰撞检测
- Unity游戏开发之“分层碰撞”
- [Unity3D]Unity3D 游戏开发之碰撞检测
- C++游戏开发之碰撞检测算法
- Android研究之游戏开发碰撞检测
- java游戏开发之碰撞检测
- 算法与游戏之OBB碰撞算法
- Android游戏开发之检测游戏碰撞的原理实现
- 13、Cocos2dx 3.0游戏开发找小三之3.0中的Director :郝萌主,一统江湖
- 凭什么要用面向对象编程——面向对象重要设计原则概述
- 常见屏幕显示像素规格
- instrument下的工具
- Linux平台进程与线程底层实现详解
- computer vision
- DIRECTOR 之游戏碰撞学一
- 新浪微博(二十一)微博布局文件(weibo_content.xml)
- Discuzx2.5 数据库连接问题 Host '***.***.***.***' is not allowed to connect to this MySQL server
- ubuntu 解压 打包 命令全集
- hdu2276-nyoj300矩阵快速幂
- ARINC 653
- Android/linux(earlysuspend、lateresume)睡眠唤醒机制简介
- 一次性能测试总结
- android Webservice 学习(记录)