[源码解读]Silverlight 4 中对不规则对象进行碰撞检测(在游戏中常使用的是否碰撞怪物边界等原理)...

在以前的Silverlight中,有个HitTest方法可以用来完成碰撞的检测。
But,Older versions (pre 3.0) did have a HitTest method!
在Silverlight4中就不可以使用HitTest方法来完成了。那么我们要该怎么做?

下面我会解读一个国外的源代码,让大家了解怎么进行碰撞检测。
会使用到一个方法FindElementsInHostCoordinates,这个是用来替代没有HitTest来检测碰撞。
还有一个方法作为基础就是Intersect方法,用来确立相交的范围。

原博客地址(可下载源码): http://www.andybeaulieu.com/Default.aspx?tabid=67&EntryID=95

碰撞原理:
我们把不规则的两个元素用矩形框来框起来,表示最大的范围,当两个矩形框想碰撞时,我们取出相交的范围,用Intersect方法,但是矩形相交不代表实际的对象是相交的,所以我们还需要遍历交集范围内的每一个点像素,看相交的两个物体是否都在这个点像素上,用FindElementsInHostCoordinates,如果都在,则表示碰撞。
代码展示(我已加上中文注释,根据原理加上注释可以和方便的理解下面代码):
 
   
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace HitTest
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();


}

private void UserControl_MouseMove( object sender, MouseEventArgs e)
{
Point pt
= e.GetPosition(cnvHitTest);

ship.SetValue(Canvas.LeftProperty, pt.X);
ship.SetValue(Canvas.TopProperty, pt.Y);

// lets get pointers to the actual UI elements we care about:
// 获得飞船的Path坐标
Path shipShell = ship.FindName( " ShipShell " ) as Path;
// 获得陨石的path坐标
Path cnvAsteroid = asteroid.FindName( " asteroidBig " ) as Path;

// 检测碰撞
// ship 飞船的用户控件(矩形大范围)
// shipShell 飞船的外壳Path坐标
// asteroid 陨石的用户控件(矩形大范围)
// cnvAsteroid 陨石的外壳Path坐标
if (CheckCollision(ship, shipShell, asteroid, cnvAsteroid))
txtStatus.Text
= " Collision! " ;
else
txtStatus.Text
= " no collision " ;
}

private bool CheckCollision(FrameworkElement control1, FrameworkElement controlElem1, FrameworkElement control2, FrameworkElement controlElem2)
{
// first see if sprite rectangles collide
// 用户控件使用的是Canvas,我们现在要把用户控件的Canvas转换成矩形表示
// UserControlBounds()用于转换为矩形来表示编辑
// rect1 为飞船矩形
// rect2 为陨石矩形
// control1 为飞船用户控件(矩形)
// control2 为运行用户控件(矩形)
Rect rect1 = UserControlBounds(control1);
Rect rect2
= UserControlBounds(control2);

// Intersect(Rect) 查找当前矩形和指定矩形的交集,并将结果存储为当前矩形。
rect1.Intersect(rect2);
if (rect1 == Rect.Empty) // 为空为矩形没有交集,那么飞船和陨石没有碰撞
{
// no collision - GET OUT!
return false ;
}
else // 不为空,有交集,返回交集,但不表示飞船就和陨石有碰撞,需要进行更细致的判断
{
bool bCollision = false ; // 是否碰撞
Point ptCheck = new Point(); // 点检测

// now we do a more accurate pixel hit test
// 进行精确的点测试
// 以行为单位,循环扫描矩形内的每个点
// 在这里rect1为交集的矩形
for ( int x = Convert.ToInt32(rect1.X); x < Convert.ToInt32(rect1.X + rect1.Width); x ++ )
{
// 遍历行中的每个点
for ( int y = Convert.ToInt32(rect1.Y); y < Convert.ToInt32(rect1.Y + rect1.Height); y ++ )
{
// 行增加

// 设置检测点的坐标
ptCheck.X = x;
ptCheck.Y
= y;

// 用FindElementsInHostCoordinates方法找出飞船用户控件中在点ptCheck上的element元素
List < UIElement > hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(ptCheck, control1) as List < UIElement > ;
// controlElem1 为实际的飞船
if (hits.Contains(controlElem1)) // hits里面装的是飞船用户控件中所有在点ptCheck中的元素,看实际的飞船是否在其中
{
// we have a hit on the first control elem, now see if the second elem has a similar hit
// 我们检测的飞船,还需要看点是否也在陨石中
// 同上面获取hits的方法,找出陨石控件中在点ptCheck上的element元素
List < UIElement > hits2 = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(ptCheck, control2) as List < UIElement > ;
// controlElem2 为实际的陨石
if (hits2.Contains(controlElem2)) // hits2里面装的是陨石用户控件中所有在点ptCheck中的元素,看实际的陨石是否在其中
{
// 够满足条件,是碰撞
bCollision = true ;
break ;
}
}
}
if (bCollision) break ;
}
return bCollision;
}


}




public Rect UserControlBounds(FrameworkElement control)
{
Point ptTopLeft
= new Point(Convert.ToDouble(control.GetValue(Canvas.LeftProperty)), Convert.ToDouble(control.GetValue(Canvas.TopProperty)));
Point ptBottomRight
= new Point(Convert.ToDouble(control.GetValue(Canvas.LeftProperty)) + control.Width, Convert.ToDouble(control.GetValue(Canvas.TopProperty)) + control.Height);

return new Rect(ptTopLeft, ptBottomRight);
}


}
}
代码很少,但是当你理解了碰撞的原理和使用的核心方法,只要你有想法,都可以做出很复杂的碰撞检测。

转载于:https://www.cnblogs.com/bruceli/archive/2011/04/25/2027233.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值