【WP7進階】——XNA遊戲平面矩形碰撞檢測

 碰撞檢測在幾乎任何遊戲都是很關鍵的一個部分,而碰撞檢測又決定了遊戲的流暢性,它對流暢性的影響如何之大的緣由,在於碰撞檢測算法越是精確到位,遊戲將會運行得越緩慢。在碰撞檢測方面,很明顯須要在準確性和性能之間進行權衡。 算法

實現碰撞檢測最簡單和快速的方式是經過包圍盒算法。當用一個包圍盒算法時,就須要在屏幕上的每一個物體(紋理圖像)周圍「畫「一個盒子(矩形塊),而後檢查這些盒子是否相交,若是產生相交(怎麼聽起來這麼耳熟?),就便可判斷出是產生碰撞了。經典的碰撞遊戲能夠看看現在某I設備上風靡全球的小鳥dom

 

經過物理算法和碰撞檢測等實現這隻小鳥欺負小豬的傳說,這點是很值得借鑑滴。ide

 

本篇學習文章將會有兩個紋理圖,一個圖片作爲碰撞塊例如上圖的小鳥,另外一個圖片作爲須要在某一地方去檢測是否與之產生碰撞的紋理,例如上圖的小豬或者城牆。這兩張圖片分別是這樣的:函數

我是用來檢測是否有人撞到個人。。。。。性能

 

  我沒事喜歡撞人。。。。。。學習

好了。素材己經有了,下面就到了如何爲這兩個紋理圖像添加各類出場的告白動做了。首先,仍是國際慣例一把,先給出效果圖:ui

 

看上圖效果,天上掉下了好多尖尖的小塊呀,快逃命呀,不太小人跑不夠快,被一個尖尖的小塊砸到了,頓時滿臉是血,屏幕都被染紅了。悲催咯。。。。this

要實現這個功能首先咱們須要獲得小人的碰撞點,和每個三角形的碰撞點。以得到小人碰撞點爲例,須要獲得小人所在的x 座標和y座標,而且獲得小人的寬度和高度。當咱們獲取到這個數據的時候,就能夠爲小人添加一個包圍圈也叫矩形檢測塊:spa

//  得到小人的磁撞大小和碰撞的地點 
            
// 公式爲:獲得小人所在的x、y 地點,而後在那個x、y點的區域高寬
            Rectangle personRectangle  =
                
new  Rectangle(( int )personPosition.X, ( int )personPosition.Y,
                personTexture.Width, personTexture.Height); 

   

當獲得這個矩形塊時。再依次獲取獲得每一個三角形的矩形和其位置使用矩形自帶的函數Intersects 來檢測兩個矩形之者是否產生交接:code

  //  與上面得到小人的碰撞點相似
                Rectangle blockRectangle  =
                    
new  Rectangle(( int )blockPositions[i].X, ( int )blockPositions[i].Y,
                    blockTexture.Width, blockTexture.Height);

                
//  若是小人與其中某一個碰撞紋理的碰撞點產生碰撞
                 if  (personRectangle.Intersects(blockRectangle))

   personHit = true;   //這時的碰撞檢測將生效  

                  

如上,若是產生交接即在調用Draw 的時候改變屏幕的顏色,便可產生碰撞時的效果,DEMO源碼爲:

  ///   <summary>
    
///  This is the main type for your game
    
///   </summary>
     public   class  Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D personTexture;     // 小人紋理圖像
        Texture2D blockTexture;      // 撞擊點紋理圖像


        
//  Person 
        Vector2 personPosition;      // 小人2D座標
         const   int  PersonMoveSpeed  =   5 ;   // 小人移動速度 

        
//  Blocks
        List < Vector2 >  blockPositions  =   new  List < Vector2 > ();  // 撞擊點集合
         float  BlockSpawnProbability  =   0.1f ;      // 控制撞擊點的降低個數
         const   int  BlockFallSpeed  =   10 ;   // 撞擊點降低速度 

        Random random  =   new  Random();
         
        
bool  personHit  =   false ;          // 是否產生碰撞
         
        Rectangle safeBounds; 
        
const   float  SafeAreaPortion  =   0.05f ;
        Viewport viewport;   // 得到當前窗口的寬高對象

        
public  Game1()
        {
            graphics  =   new  GraphicsDeviceManager( this );
            Content.RootDirectory  =   " Content "
            
//  Frame rate is 30 fps by default for Windows Phone.
            TargetElapsedTime  =  TimeSpan.FromTicks( 333333 );
        }

        
///   <summary>
        
///  Allows the game to perform any initialization it needs to before starting to run.
        
///  This is where it can query for any required services and load any non-graphic
        
///  related content.  Calling base.Initialize will enumerate through any components
        
///  and initialize them as well.
        
///   </summary>
         protected   override   void  Initialize()
        {
            
//  TODO: Add your initialization logic here
              viewport  =  graphics.GraphicsDevice.Viewport;
            safeBounds  =   new  Rectangle(
                ( int )(viewport.Width  *  SafeAreaPortion),     // 40
                ( int )(viewport.Height  *  SafeAreaPortion),    // 24
                ( int )(viewport.Width  *  ( 1   -   2   *  SafeAreaPortion)),   // 720
                ( int )(viewport.Height  *  ( 1   -   2   *  SafeAreaPortion)));     // 432

            
//  Start the player in the center along the bottom of the screen
            
            
base .Initialize();
        }

        
///   <summary>
        
///  LoadContent will be called once per game and is the place to load
        
///  all of your content.
        
///   </summary>
         protected   override   void  LoadContent()
        { 
            spriteBatch  =   new  SpriteBatch(GraphicsDevice);
            blockTexture  =  Content.Load < Texture2D > ( " Block " );
            personTexture  =  Content.Load < Texture2D > ( " Person " );
            personPosition.X  =  (safeBounds.Width  -  personTexture.Width)  /   2 ;     // 小人的座標縱向在屏幕居中
            personPosition.Y  =  safeBounds.Height  -  personTexture.Height;     // 小人的座標豎向在屏幕底下居中
         
            
//  TODO: use this.Content to load your game content here
        }

        
///   <summary>
        
///  UnloadContent will be called once per game and is the place to unload
        
///  all content.
        
///   </summary>
         protected   override   void  UnloadContent()
        {
            
//  TODO: Unload any non ContentManager content here
        }

        
///   <summary>
        
///  Allows the game to run logic such as updating the world,
        
///  checking for collisions, gathering input, and playing audio.
        
///   </summary>
        
///   <param name="gameTime"> Provides a snapshot of timing values. </param>
         protected   override   void  Update(GameTime gameTime)
        {
            
//  Allows the game to exit
             if  (GamePad.GetState(PlayerIndex.One).Buttons.Back  ==  ButtonState.Pressed)
                
this .Exit();

            TouchCollection touch  =  TouchPanel.GetState();
            
if  (touch.Count > 0 )
            {
                
if  (touch[ 0 ].Position.X  >  viewport.Width  /   2 )
                {
                    personPosition.X  +=  PersonMoveSpeed;
                }
                
else
                {
                    personPosition.X  -=  PersonMoveSpeed;
                }
            } 

            
double  ran  =  random.NextDouble();
            
//  TODO: Add your update logic here
             if  (ran  <  BlockSpawnProbability)     // 隨機循環Double 型若是隨機的double 小於0.01f這樣作是避免產生的撞擊 點太多
            { 
                
float  x  =  ( float )random.NextDouble()  * // 在屏幕隨機出現 
                    (graphics.GraphicsDevice.Viewport.Width  -  blockTexture.Width); // 爲了避免超出屏幕 
                Vector2 v  =   new  Vector2(x,  1 );
                blockPositions.Add(v);
            }

            
//  得到小人的磁撞大小和碰撞的地點 
            
// 公式爲:獲得小人所在的x、y 地點,而後在那個x、y點的區域高寬
            Rectangle personRectangle  =
                
new  Rectangle(( int )personPosition.X, ( int )personPosition.Y,
                personTexture.Width, personTexture.Height);

            
//  Update each block
            personHit  =   false ;   // 默認爲不碰撞狀態
             for  ( int  i  =   0 ; i  <  blockPositions.Count; i ++ )   // 循環全部在集合裏面的碰撞紋理
            {
                
//  使裏面的全部元素所有降低
                blockPositions[i]  =
                    
new  Vector2(blockPositions[i].X,     // X座標不變
                                blockPositions[i].Y  +  BlockFallSpeed);   // 豎座標爲當前的Y座標每次加上降低的速度常量 

                
//  與上面得到小人的碰撞點相似
                Rectangle blockRectangle  =
                    
new  Rectangle(( int )blockPositions[i].X, ( int )blockPositions[i].Y,
                    blockTexture.Width, blockTexture.Height);

                
//  若是小人與其中某一個碰撞紋理的碰撞點產生碰撞
                 if  (personRectangle.Intersects(blockRectangle))
                    personHit  =   true ;    // 這時的碰撞檢測將生效

                
//  若是有碰撞紋理超屏幕
                 if  (blockPositions[i].Y  >  graphics.GraphicsDevice.Viewport.Height)
                {   
                    
// 從集合裏面移出該碰撞點
                    blockPositions.RemoveAt(i);
                     
                    
// 當刪除了其中一個碰撞點時,下一個碰撞點的索引將跟當前移除的碰撞點是同樣的,因此循環變量自動減1 
                    i -- ;
                }
            }
            
base .Update(gameTime);
        }

        
///   <summary>
        
///  This is called when the game should draw itself.
        
///   </summary>
        
///   <param name="gameTime"> Provides a snapshot of timing values. </param>
         protected   override   void  Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            GraphicsDevice device  =  graphics.GraphicsDevice;
            
//  TODO: Add your drawing code here
             if  (personHit)   // 當產生碰撞
            {
                device.Clear(Color.Red);
            }
            
else
            {
                device.Clear(Color.CornflowerBlue);
            }


            spriteBatch.Begin();

            
//  Draw person
            spriteBatch.Draw(personTexture, personPosition, Color.White);

            
//  Draw blocks
             foreach  (Vector2 blockPosition  in  blockPositions)
                spriteBatch.Draw(blockTexture, blockPosition, Color.White);

            spriteBatch.End();

            
base .Draw(gameTime);
        }

     輕輕鬆鬆的調用幾個現成的方法和利用刷新機制就能夠實現這個碰撞檢測功能。固然碰撞檢測還不止這麼簡單,還能夠更詳細的使用逐點檢測的方法檢測碰撞。

相關文章
相關標籤/搜索