• 使用 HitArea 类在 XNA 中测试碰撞,WPXNA(六)


    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛。在这里分享一下经验,仅为了和各位朋友交流经验。平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXNA 吧,最后请高手绕道而行吧,以免浪费时间。(为了突出重点和减少篇幅,有些示例代码可能不够严谨。)

    碰撞测试

    在游戏中,碰撞测试是很重要的,这可能会影响到游戏的运行效率,当然有些游戏可能不需要碰撞测试。平方编写了一些简单的用于测试碰撞的类 HitArea。

    HitArea 包含三个主要方法:

    protected abstract bool containTesting ( int x, int y );
    
    internal bool ContainTest ( Vector2 location )
    {
        return this.ContainTest ( ( int ) location.X, ( int ) location.Y );
    }
    internal bool ContainTest ( float x, float y )
    { return this.ContainTest ( ( int ) x, ( int ) y ); }
    internal bool ContainTest ( int x, int y )
    {
    
        if ( !this.IsEnabled )
            return false;
    
        return this.containTesting ( x, y );
    }
    
    protected abstract bool hitTesting ( Rectangle rectangle );
    
    internal bool HitTest ( Rectangle rectangle )
    {
    
        if ( !this.IsEnabled )
            return false;
    
        return this.hitTesting ( rectangle );
    }
    
    protected abstract bool hitTesting ( HitArea area );
    
    internal bool HitTest ( HitArea area )
    {
    
        if ( !this.IsEnabled )
            return false;
    
        return this.hitTesting ( area );
    }

    方法 containTesting 用于测试碰撞区是否包含某一个点,你可以指定点的位置,点的 x 坐标和 y 坐标必须是 int 类型的。方法 hitTesting 用于测试碰撞区是否与某一个矩形或者另一个碰撞区发生了碰撞。HitArea 类并没有实现这些方法,而是由其派生类来实现。

    在测试的过程中,我们还判断了 HitArea 类的 IsEnabled 字段,他用来表示碰撞区是否可用。在默认情况下,IsEnabled 为 true。

    internal bool IsEnabled;
    
    protected HitArea ( )
        : this (
        true
        )
    { }
    protected HitArea ( bool isEnabled )
    { this.IsEnabled = isEnabled; }

    所有从 HitArea 派生的类还需要实现 Locate 方法,此方法用来移动碰撞区的位置。

    internal abstract void Locate ( Point position );

    单个矩形的测试

    平方定义了 SingleRectangleHitArea 类,用来完成单一矩形的测试。

    protected readonly Point location;
    protected Rectangle rectangle;
    
    internal Rectangle Rectangle
    {
        get { return this.rectangle; }
    }
    
    internal SingleRectangleHitArea ( Rectangle rectangle )
        : this ( rectangle,
        true
        )
    { }
    internal SingleRectangleHitArea ( Rectangle rectangle, bool isEnabled )
        : base ( isEnabled )
    {
        this.rectangle = rectangle;
        this.location = rectangle.Location;
    }

    字段 location 表示了碰撞区的原始注册位置,属性 Rectangle 表示用来测试的矩形。

    通过实现 containTesting 方法,SingleRectangleHitArea 类就可以测试是否与某一个点发生了碰撞。而实现方法 hitTesting,SingleRectangleHitArea 类可以测试是否与某一个矩形或者某一个碰撞区发生了碰撞。

    protected override bool containTesting ( int x, int y )
    { return this.rectangle.Contains ( x, y ); }
    
    protected override bool hitTesting ( Rectangle rectangle )
    { return this.rectangle.Intersects ( rectangle ); }
    
    protected override bool hitTesting ( HitArea area )
    { return area.HitTest ( this.rectangle ); }
    
    internal override void Locate ( Point location )
    { this.rectangle.Location = new Point ( this.location.X + location.X, this.location.Y + location.Y ); }

    多个矩形的测试

    单一矩形可以用于一些较小的精灵,而对于一些较大的精灵,可能需要使用多个矩形才能更好的进行碰撞测试。

    private readonly List<Point> subLocations = new List<Point> ( );
    private readonly List<Rectangle> subRectangles = new List<Rectangle> ( );
    
    internal MultiRectangleHitArea ( Rectangle rectangle, params Rectangle[] subRectangles )
        : this ( rectangle,
        true,
        subRectangles
        )
    { }
    internal MultiRectangleHitArea ( Rectangle rectangle, bool isEnabled, params Rectangle[] subRectangles )
        : base ( rectangle, isEnabled )
    {
        this.subRectangles.AddRange ( subRectangles );
    
        foreach ( Rectangle subRectangle in subRectangles )
            this.subLocations.Add ( subRectangle.Location );
    
    }

    MultiRectangleHitArea 类继承自 SingleRectangleHitArea 类,除了 SingleRectangleHitArea 中的矩形,还包括一些小矩形,这些小矩形被包含在 SingleRectangleHitArea 的矩形中。

    在测试时,我们首先检测目标是否与 SingleRectangleHitArea 的矩形发生了碰撞,如果发生了碰撞,我们再去检测是否与 MultiRectangleHitArea 中的多个小矩形发生了碰撞。

    protected override bool containTesting ( int x, int y )
    {
    
        if ( !base.containTesting ( x, y ) )
            return false;
    
        foreach ( Rectangle subRectangle in this.subRectangles )
            if ( subRectangle.Contains ( x, y ) )
                return true;
    
        return false;
    }
    
    protected override bool hitTesting ( Rectangle rectangle )
    {
    
        if ( !base.hitTesting ( rectangle ) )
            return false;
    
        foreach ( Rectangle subRectangle in this.subRectangles )
            if ( subRectangle.Intersects ( rectangle ) )
                return true;
    
        return false;
    }
    
    protected override bool hitTesting ( HitArea area )
    {
        
        if ( !base.hitTesting ( area ) )
            return false;
    
        foreach ( Rectangle subRectangle in this.subRectangles )
            if ( area.HitTest ( subRectangle ) )
                return true;
    
        return false;
    }

    一个小例子

    首先,我们定义了两个 SingleRectangleHitArea,并在构造函数中初始化他们。

    private readonly SingleRectangleHitArea hitArea1;
    private readonly SingleRectangleHitArea hitArea2;
    private int step = 1;
    
    public World ( Color backgroundColor )
        : base ( )
    {
        // ...
        
        this.hitArea1 = new SingleRectangleHitArea ( new Rectangle ( 0, 0, 100, 100 ) );
        this.hitArea2 = new SingleRectangleHitArea ( new Rectangle ( 200, 200, 100, 100 ) );
    }

    字段 hitArea1 的位置为 (0, 0),大小为 100。字段 hitArea2 的位置为 (200, 200),大小为 100。所以他们不会发生碰撞。

    private void OnUpdate ( object sender, GameTimerEventArgs e )
    {
    
        this.step++;
    
        if ( this.step == 10 )
            Debug.WriteLine ( "Hit? {0}", this.hitArea1.HitTest ( this.hitArea2 ) );
        else if ( this.step == 20 )
        {
            this.hitArea1.Locate ( new Point ( 150, 150 ) );
            Debug.WriteLine ( "Hit? {0}", this.hitArea1.HitTest ( this.hitArea2 ) );
        }
        else if ( this.step == 30 )
            Debug.WriteLine ( "Hit? {0}", this.hitArea1.ContainTest ( 200, 200 ) );
            
    }

    在上面的代码中,我们移动了 hitArea1 的位置,并重新测试,则 hitArea1 和 hitArea2 发生了碰撞。

    本期视频 http://v.youku.com/v_show/id_XNTY1NzMxODA0.html
    项目地址 http://wp-xna.googlecode.com/

    更多内容 WPXNA
    平方开发的游戏 http://zoyobar.lofter.com/
    QQ 群 213685539

    欢迎访问我在其他位置发布的同一文章:http://www.wpgame.info/post/decc4_65cbf7

  • 相关阅读:
    Oracle获取每分钟、每10分钟、每小时、每天、每周、每月、每年的时间或日期
    通过Oracle的GateWay,访问SQLServer的数据【方法二】
    ororacle中每月调用一次,Oracle Job的使用(定时执行)Interval 参数
    oracle的权限授予,oracle用户权限管理使用详解
    Yarn学习(三)Yarn Workspace介绍 + 适用场景 + 命令
    Yarn学习(一)介绍、安装
    Yarn学习(二)常用命令
    HanziWriter 小程序端开发注意事项
    安装 esxi 提示 boot
    spring Boot 相关问题以及修复(后续待补充)
  • 原文地址:https://www.cnblogs.com/zoyobar/p/wpxna6.html
Copyright © 2020-2023  润新知