Nowpaper 十五英寸的世界

Rich Games Developer

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

      Flyer04讲过了如何产生敌人并且开始进行漂亮的“摇摆”,真正的困难才刚刚开始,因为现在主角是完全无敌的,那些什么什么东西必须对主角造成伤害才显得有趣,对于游戏中如何进行伤害判定是一个有趣的问题,要先解决的是如何判定是否碰撞,在这段当中,我们将完成碰撞的检测以及生命值的减少,搏斗现在就开始了。

      在游戏开发中,对于碰撞的检测有很多,3D一般用射线判定法(Vector),而2D游戏就不需要那么复杂,虽然使用向量能作出最准确的判定,但是在一个平面中有更加简便的解决方案,当然,这取决于开发者是否严格要求最准确的碰撞判定。

      我简单介绍两种2D游戏中最准确的判定方法:射线判定和色值判定

      第一,射线判定来自于一个数学判断一个点是否在任意一个封闭形状内的方法:一个点向任意一个方向发出射线如果这条线经过线的数量是奇数,那么这个点就在这个形状内。

很显然这种方法不但复杂还很麻烦,需要计算经过线的数量还要对很多个点进行判断,我相信大多数人都不会对这个方法感兴趣。

      第二,色值判定,这个原理通过对比两张图像的色值看看是否有重合点判断是否碰撞,使用数组保存所有的色值,然后进行“与”运算,如果这个数组中出现了“true”,那么就可以判断是有重合点,这个判定方法非常准确,但是准确的代价是效率的降低,当游戏中有n个图片对象,进行的对比判定将是一个n的几何数字,进行如此之海量的判定,玩家的机器和不会买单。

      上面介绍的是两种最准确的方法,但是很显然在实现上还是在效率上都不是我们所期望的解决方案,所以,伟大的游戏开发者先驱们在更加伟大的数学面前,找出了更为简单及更为有效率的方案——圆形判定和矩形判定,具体请参看图片。

      圆形判定方法来自一个经典的数学导论,两个圆的中心点的距离如果小于两个圆的半径之和,那么它们两个必然重合,具体做法也是很简单,首先求出两个点之间的向量,然后求出长度即可,这方面没学好的朋友可以研究一下勾股定理,中国人早就明白如何取得弦的长度。

      矩形判定更加容易,只需要对四个点进行逐次判断,取得两个矩形是否重合。

      为什么游戏开发者比较普遍用后面的两种法,因为第一在游戏中对于碰撞的检测并非那么非常严格,而是达到目的即可,第二游戏中也不太可能设计非常复杂的奇怪形状,第三使用图形进行判断(注意是图形而不是图像)较为好控制,而且效率也高。

      那么,我们在这里使用的方法就是矩形判断法。

      为了更加直观,为每个可碰撞的角色加入一个Rectangle

 

Rectangle _rectangle = new Rectangle()
{ Width = 32, Height = 32, Stroke = new SolidColorBrush(Colors.Red) };

 

 

      然后在主要逻辑里进行Rect的判断,看起来是一件很容易的事,但是难度可不小,往往在这里容易“卡壳”,要知道这是两个控件之间的交互,飞行员和固体组,在这里需要进行特定的技巧才可以达到应有的目的,两个不同的类互相之间的交互应在主循环中完成逻辑运算。可是我们使用了两个Timer分别来处理主角的移动和Solid移动,为了让它们之间产生交互,需要将两个循环合并到一起,因此,需要对ClassSolidGroup、ClassFlyer的Loop进行修改,使用MainPage的主循环去分别调用它们两个的分循环。

public void TickGameFrameLoop(object sender, EventArgs e)
{
solidgroup.TickGameFrameLoop(sender, e);
Hero.TickGameFrameLoop(sender, e);
}

      要为每个需要检测的物体取得Rect

public Rect MyRect
{
get
{
return new Rect(X, Y, _rectangle.Width, _rectangle.Height);
}
}

         利用下段代码,完成碰撞检测。

Rect herorect = Hero.MyRect;
foreach(ClassSolid solid in solidgroup.Children)
{
Rect rt
= solid.MyRect;
rt.Intersect(herorect);
if (!double.IsInfinity(rt.Height) && !double.IsInfinity(rt.Width))
{
if (!Hero.isLose)
flyerlife.Lose(
10);
Hero.Flyerstate
= EmFlyerState.击中;

}
}

上述代码部分比较分散,要看还是直接看代码吧。代码在这里

posted on 2010-02-25 20:11  nowpaper  阅读(1754)  评论(1编辑  收藏  举报