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


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

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

    碰撞原理:
    我们把不规则的两个元素用矩形框来框起来,表示最大的范围,当两个矩形框想碰撞时,我们取出相交的范围,用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);
    }


    }
    }
    代码很少,但是当你理解了碰撞的原理和使用的核心方法,只要你有想法,都可以做出很复杂的碰撞检测。
  • 相关阅读:
    个人作业——软件工程实践总结作业
    团队作业第二次—项目选题报告
    结对第二次—文献摘要热词统计及进阶需求
    结对第一次—原型设计(文献摘要热词统计)
    第一次作业-准备篇
    Java面向对象课程设计——购物车
    第04次作业-树
    第03次作业-栈和队列
    第02次作业-线性表
    01——绪论作业
  • 原文地址:https://www.cnblogs.com/bruceli/p/2027233.html
Copyright © 2020-2023  润新知