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