使用 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

原文地址:https://www.cnblogs.com/zoyobar/p/wpxna6.html