碰撞檢測在幾乎任何游戲都是很關(guān)鍵的一個部分,而碰撞檢測又決定了游戲的流暢性,它對流暢性的影響如何之大的原因,在于碰撞檢測算法越是精確到位,游戲?qū)\行得越緩慢。在碰撞檢測方面,很明顯需要在準(zhǔn)確性和性能之間進行權(quán)衡。
實現(xiàn)碰撞檢測最簡單和快速的方式是通過包圍盒算法。當(dāng)用一個包圍盒算法時,就需要在屏幕上的每個物體(紋理圖像)周圍“畫“一個盒子(矩形塊),然后檢查這些盒子是否相交,如果產(chǎn)生相交(怎么聽起來這么耳熟?),就即可判斷出是產(chǎn)生碰撞了。經(jīng)典的碰撞游戲可以看看如今某I設(shè)備上風(fēng)靡全球的小鳥
通過物理算法和碰撞檢測等實現(xiàn)這只小鳥欺負小豬的傳說,這點是很值得借鑒滴。
本篇學(xué)習(xí)文章將會有兩個紋理圖,一個圖片做為碰撞塊例如上圖的小鳥,另一個圖片做為需要在某一地方去檢測是否與之產(chǎn)生碰撞的紋理,例如上圖的小豬或者城墻。這兩張圖片分別是這樣的:
我是用來檢測是否有人撞到我的。。。。。
我沒事喜歡撞人。。。。。。
好了。素材己經(jīng)有了,下面就到了如何為這兩個紋理圖像添加各種出場的告白動作了。首先,還是國際慣例一把,先給出效果圖:
看上圖效果,天上掉下了好多尖尖的小塊呀,快逃命呀,不過小人跑不夠快,被一個尖尖的小塊砸到了,頓時滿臉是血,屏幕都被染紅了。悲催咯。。。。
要實現(xiàn)這個功能首先我們需要得到小人的碰撞點,和每一個三角形的碰撞點。以獲得小人碰撞點為例,需要得到小人所在的x 坐標(biāo)和y坐標(biāo),并且得到小人的寬度和高度。當(dāng)我們獲取到這個數(shù)據(jù)的時候,就可以為小人添加一個包圍圈也叫矩形檢測塊:
// 獲得小人的磁撞大小和碰撞的地點
//公式為:得到小人所在的x、y 地點,然后在那個x、y點的區(qū)域高寬
Rectangle personRectangle =
new Rectangle((int)personPosition.X, (int)personPosition.Y,
personTexture.Width, personTexture.Height);
當(dāng)?shù)玫竭@個矩形塊時。再依次獲取得到每個三角形的矩形和其位置使用矩形自帶的函數(shù)Intersects 來檢測兩個矩形之者是否產(chǎn)生交接:
// 與上面獲得小人的碰撞點類似
Rectangle blockRectangle =
new Rectangle((int)blockPositions[i].X, (int)blockPositions[i].Y,
blockTexture.Width, blockTexture.Height);
// 如果小人與其中某一個碰撞紋理的碰撞點產(chǎn)生碰撞
if (personRectangle.Intersects(blockRectangle))
personHit = true; //這時的碰撞檢測將生效
如上,如果產(chǎn)生交接即在調(diào)用Draw 的時候改變屏幕的顏色,即可產(chǎn)生碰撞時的效果,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坐標(biāo)
const int PersonMoveSpeed = 5; //小人移動速度
// Blocks
List<Vector2> blockPositions = new List<Vector2>(); //撞擊點集合
float BlockSpawnProbability = 0.1f; //控制撞擊點的下降個數(shù)
const int BlockFallSpeed = 10; //撞擊點下降速度
Random random = new Random();
bool personHit = false; //是否產(chǎn)生碰撞
Rectangle safeBounds;
const float SafeAreaPortion = 0.05f;
Viewport viewport; //獲得當(dāng)前窗口的寬高對象
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; //小人的坐標(biāo)縱向在屏幕居中
personPosition.Y = safeBounds.Height - personTexture.Height; //小人的坐標(biāo)豎向在屏幕底下居中
// 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) //隨機循環(huán)Double 型如果隨機的double 小于0.01f這樣做是避免產(chǎn)生的撞擊 點太多
{
float x = (float)random.NextDouble() *//在屏幕隨機出現(xiàn)
(graphics.GraphicsDevice.Viewport.Width - blockTexture.Width);//為了不超出屏幕
Vector2 v = new Vector2(x, 1);
blockPositions.Add(v);
}
// 獲得小人的磁撞大小和碰撞的地點
//公式為:得到小人所在的x、y 地點,然后在那個x、y點的區(qū)域高寬
Rectangle personRectangle =
new Rectangle((int)personPosition.X, (int)personPosition.Y,
personTexture.Width, personTexture.Height);
// Update each block
personHit = false; //默認(rèn)為不碰撞狀態(tài)
for (int i = 0; i < blockPositions.Count; i++) //循環(huán)所有在集合里面的碰撞紋理
{
// 使里面的所有元素全部下降
blockPositions[i] =
new Vector2(blockPositions[i].X, //X坐標(biāo)不變
blockPositions[i].Y + BlockFallSpeed); //豎坐標(biāo)為當(dāng)前的Y座標(biāo)每次加上下降的速度常量
// 與上面獲得小人的碰撞點類似
Rectangle blockRectangle =
new Rectangle((int)blockPositions[i].X, (int)blockPositions[i].Y,
blockTexture.Width, blockTexture.Height);
// 如果小人與其中某一個碰撞紋理的碰撞點產(chǎn)生碰撞
if (personRectangle.Intersects(blockRectangle))
personHit = true; //這時的碰撞檢測將生效
// 如果有碰撞紋理超屏幕
if (blockPositions[i].Y > graphics.GraphicsDevice.Viewport.Height)
{
//從集合里面移出該碰撞點
blockPositions.RemoveAt(i);
//當(dāng)刪除了其中一個碰撞點時,下一個碰撞點的索引將跟當(dāng)前移除的碰撞點是一樣的,所以循環(huán)變量自動減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) //當(dāng)產(chǎn)生碰撞
{
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);
}
}
輕輕松松的調(diào)用幾個現(xiàn)成的方法和利用刷新機制就可以實現(xiàn)這個碰撞檢測功能。當(dāng)然碰撞檢測還不止這么簡單,還可以更詳細的使用逐點檢測的方法檢測碰撞。