球與球碰撞判斷
總結
包含每個模型的球體用於做出命中判斷。 在此示例中,對兩個球體模型執行碰撞檢測。
操作環境
先決條件
支援的 XNA 版本 |
|
支援的平臺 |
|
Windows 所需的頂點著色器版本 | 2.0 |
Windows 所需的像素著色器版本 | 2.0 |
操作環境
平臺 |
|
如何使用範例
工作鍵盤Xbox | 360控制器滑鼠 | 觸摸 | ||
---|---|---|---|---|
移動球 1 | ↑↓←→ | 左搖桿 | 左鍵 & 拖動 | - |
物質
關於Hit Judgment
在射擊遊戲和動作遊戲中,會發生各種碰撞,例如角色與子彈之間的碰撞,以及角色之間的碰撞,因此需要編寫程式來判斷它們。 在程式中,這通常稱為碰撞檢測。 命中判斷有多種模式,從簡單的數學和物理上到複雜的。 一般來說,遊戲通常是為了減少處理負荷而不是準確性而製作的,如果該區域沒有極端偏差,那麼不準確也沒什麼關係。
此提示描述了最常見且負擔最少的「球對球」命中檢測。 由於它是一個球體和一個球體,我們談論的是三維空間中的碰撞判斷,但是可以用幾乎相同的過程來代替二維空間中圓之間的碰撞。
球和球擊中判斷如何運作
兩個參數用於確定球體和球體的碰撞:每個模型的“位置”和模型大小的“半徑”。 為了確定這兩個參數是否正確,通過查看下圖很容易理解。
- P:模型的位置 L:兩點之間的距離(P2-P1) R:球體的半徑
如果「兩個模型之間的距離」是“L”,“兩個模型的半徑之和”是“R”,那麼“L<R”表示它們正在碰撞。 另一方面,如果是“L > R”,則表示沒有碰撞。 “L = R”的碰撞檢測對兩者都無關緊要。
田
<summary>
モデル
</summary>
private Model model = null;
<summary>
モデルの基本包括球
</summary>
private BoundingSphere baseBoundingSphere = new BoundingSphere();
<summary>
球1の位置
</summary>
private Vector3 sphere1Position = new Vector3(0.0f, 0.0f, 0.0f);
<summary>
球2の位置
</summary>
private Vector3 sphere2Position = new Vector3(1.5f, 0.0f, -3.0f);
<summary>
球1の包括球
</summary>
private BoundingSphere sphere1BoundingSphere = new BoundingSphere();
<summary>
球2の包括球
</summary>
private BoundingSphere sphere2BoundingSphere = new BoundingSphere();
<summary>
衝突フラグ
</summary>
private bool isCollision = false;
您可以為命中檢測過程編寫自己的程式,但是 XNA Framework 有一個名為“BoundingSphere”的結構,可以輕鬆處理球體和命中檢測的參數,因此我想使用它。
加載的 Model 類的每個 ModelMesh 都已經包含包含模型的球體資訊“BoundingSphere”,因此我們準備了一個字段“baseBoundingSphere”來檢索它。
其他功能包括每個球體的位置、用於確定兩個球體影響的 BoundingSphere 和碰撞標誌。
負荷
// モデルを作成
this.model = this.Content.Load<Model>("Sphere");
// 包括球取得
this.baseBoundingSphere = this.model.Meshes[0].BoundingSphere;
// 各モデル用の包括球半径設定
this.sphere1BoundingSphere.Radius = this.baseBoundingSphere.Radius;
this.sphere2BoundingSphere.Radius = this.baseBoundingSphere.Radius;
模型中的每個 ModelMesh 都有一個 BoundingSphere 屬性,該屬性允許您獲取有關 ModelMesh 中包含模型的球體的資訊。 由於我們使用的模型只有一個 ModelMesh,因此我們已將包含球體從第一個索引複製到baseBoundingSphere。
由於球體的半徑是固定的,因此半徑預設在兩個 BoundingSpheres 的 Radius 屬性中。
命中判斷
// 衝突判定用の球を設定
this.sphere1BoundingSphere.Center =
this.sphere1Position + this.baseBoundingSphere.Center;
this.sphere2BoundingSphere.Center =
this.sphere2Position + this.baseBoundingSphere.Center;
// 衝突判定
this.isCollision =
this.sphere1BoundingSphere.Intersects(this.sphere2BoundingSphere);
使用 BoundingSphere.Intersects 方法確定球體與球體之間的碰撞。 由於我們已經為兩個 BoundingSpheres 設置了半徑,因此我們將 BoundingSphere.Center 屬性設置為球體的位置加上原始傘形球體的中心座標。 添加原始球體的中心座標是因為包含球體的中心不一定是原點。
如果將另一個 BoundingSphere 指定為 BoundingSphere.Intersects 的參數,它將返回一個布爾值以查看它是否發生衝突,因此我們得到了它。 此標誌用於繪製字元以查看它們是否正確。
如何計算命中判斷
XNA 框架提供了一個有用的碰撞檢測結構,稱為 BoundingSphere,它不僅可以用於球體,還可以用於具有不同形狀的碰撞,例如射線(線)和盒子。
但是,如果是球體之間的碰撞,則可以通過簡單的計算來確定,而無需使用 BoundingSphere.Intersects。
- 結果(是否命中)= 球體 2 的位置與球體 1 的位置之間的距離 < 球體 1 的半徑 + 球體 2 的半徑
如果以程式設計方式編寫
this.isCollision = (this.sphere2Position - this.sphere1Position).Length() <
(this.sphere1BoundingSphere.Radius + this.sphere2BoundingSphere.Radius);
它來了。 (上面的碰撞檢測過程假設包納球體的中心是原點,就像一個完美的球體,所以它沒有考慮模型的包羅布體球體的中心。 如果你考慮一下,它將如下所示)
this.isCollision = ((this.sphere2Position + this.baseBoundingSphere.Center) -
(this.sphere1Position + this.baseBoundingSphere.Center)).Length() <
(this.sphere1BoundingSphere.Radius + this.sphere2BoundingSphere.Radius);
所有代碼
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
#if WINDOWS_PHONE
using Microsoft.Xna.Framework.Input.Touch;
#endif
namespace CollisionSphereAndSphere
{
<summary>
ゲームメインクラス
</summary>
public class GameMain : Microsoft.Xna.Framework.Game
{
<summary>
グラフィックデバイス管理クラス
</summary>
private GraphicsDeviceManager graphics = null;
<summary>
スプライトのバッチ化クラス
</summary>
private SpriteBatch spriteBatch = null;
<summary>
スプライトでテキストを描画するためのフォント
</summary>
private SpriteFont font = null;
<summary>
モデル
</summary>
private Model model = null;
<summary>
モデルの基本包括球
</summary>
private BoundingSphere baseBoundingSphere = new BoundingSphere();
<summary>
球1の位置
</summary>
private Vector3 sphere1Position = new Vector3(0.0f, 0.0f, 0.0f);
<summary>
球2の位置
</summary>
private Vector3 sphere2Position = new Vector3(1.5f, 0.0f, -3.0f);
<summary>
球1の包括球
</summary>
private BoundingSphere sphere1BoundingSphere = new BoundingSphere();
<summary>
球2の包括球
</summary>
private BoundingSphere sphere2BoundingSphere = new BoundingSphere();
<summary>
衝突フラグ
</summary>
private bool isCollision = false;
<summary>
前回のマウスの状態
</summary>
private MouseState oldMouseState;
<summary>
GameMain コンストラクタ
</summary>
public GameMain()
{
// グラフィックデバイス管理クラスの作成
this.graphics = new GraphicsDeviceManager(this);
// ゲームコンテンツのルートディレクトリを設定
this.Content.RootDirectory = "Content";
#if WINDOWS_PHONE
// Windows Phone のデフォルトのフレームレートは 30 FPS
this.TargetElapsedTime = TimeSpan.FromTicks(333333);
// バックバッファサイズの設定
this.graphics.PreferredBackBufferWidth = 480;
this.graphics.PreferredBackBufferHeight = 800;
// フルスクリーン表示
this.graphics.IsFullScreen = true;
#endif
}
<summary>
ゲームが始まる前の初期化処理を行うメソッド
グラフィック以外のデータの読み込み、コンポーネントの初期化を行う
</summary>
protected override void Initialize()
{
// TODO: ここに初期化ロジックを書いてください
// コンポーネントの初期化などを行います
base.Initialize();
}
<summary>
ゲームが始まるときに一回だけ呼ばれ
すべてのゲームコンテンツを読み込みます
</summary>
protected override void LoadContent()
{
// テクスチャーを描画するためのスプライトバッチクラスを作成します
this.spriteBatch = new SpriteBatch(this.GraphicsDevice);
// フォントをコンテントパイプラインから読み込む
this.font = this.Content.Load<SpriteFont>("Font");
// モデルを作成
this.model = this.Content.Load<Model>("Sphere");
// 包括球取得
this.baseBoundingSphere = this.model.Meshes[0].BoundingSphere;
// 各モデル用の包括球半径設定
this.sphere1BoundingSphere.Radius = this.baseBoundingSphere.Radius;
this.sphere2BoundingSphere.Radius = this.baseBoundingSphere.Radius;
// あらかじめパラメータを設定しておく
foreach (ModelMesh mesh in this.model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
// デフォルトのライト適用
effect.EnableDefaultLighting();
// ビューマトリックスをあらかじめ設定
effect.View = Matrix.CreateLookAt(
new Vector3(0.0f, 10.0f, 1.0f),
Vector3.Zero,
Vector3.Up
);
// プロジェクションマトリックスをあらかじめ設定
effect.Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45.0f),
(float)this.GraphicsDevice.Viewport.Width /
(float)this.GraphicsDevice.Viewport.Height,
1.0f,
100.0f
);
}
}
}
<summary>
ゲームが終了するときに一回だけ呼ばれ
すべてのゲームコンテンツをアンロードします
</summary>
protected override void UnloadContent()
{
// TODO: ContentManager で管理されていないコンテンツを
// ここでアンロードしてください
}
<summary>
描画以外のデータ更新等の処理を行うメソッド
主に入力処理、衝突判定などの物理計算、オーディオの再生など
</summary>
<param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
protected override void Update(GameTime gameTime)
{
KeyboardState keyboardState = Keyboard.GetState();
MouseState mouseState = Mouse.GetState();
GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);
// Xbox 360 コントローラ、Windows Phone の BACK ボタンを押したときに
// ゲームを終了させます
if (gamePadState.Buttons.Back == ButtonState.Pressed)
{
this.Exit();
}
float speed = 0.1f;
// 球1の位置を移動させる
if (gamePadState.IsConnected)
{
this.sphere1Position.X += gamePadState.ThumbSticks.Left.X * speed;
this.sphere1Position.Z -= gamePadState.ThumbSticks.Left.Y * speed;
}
if (keyboardState.IsKeyDown(Keys.Left))
{
this.sphere1Position.X -= speed;
}
if (keyboardState.IsKeyDown(Keys.Right))
{
this.sphere1Position.X += speed;
}
if (keyboardState.IsKeyDown(Keys.Down))
{
this.sphere1Position.Z += speed;
}
if (keyboardState.IsKeyDown(Keys.Up))
{
this.sphere1Position.Z -= speed;
}
if (mouseState.LeftButton == ButtonState.Pressed)
{
// 直前にマウスの左ボタンが押されていない場合は差分を0にする
if (this.oldMouseState.LeftButton == ButtonState.Released)
{
this.oldMouseState = mouseState;
}
this.sphere1Position += new Vector3((mouseState.X - this.oldMouseState.X) * 0.01f,
0,
(mouseState.Y - this.oldMouseState.Y) * 0.01f);
}
// マウスの状態記憶
this.oldMouseState = mouseState;
// 衝突判定用の球を設定
this.sphere1BoundingSphere.Center =
this.sphere1Position + this.baseBoundingSphere.Center;
this.sphere2BoundingSphere.Center =
this.sphere2Position + this.baseBoundingSphere.Center;
// 衝突判定
this.isCollision =
this.sphere1BoundingSphere.Intersects(this.sphere2BoundingSphere);
// 登録された GameComponent を更新する
base.Update(gameTime);
}
<summary>
描画処理を行うメソッド
</summary>
<param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
protected override void Draw(GameTime gameTime)
{
// 画面を指定した色でクリアします
this.GraphicsDevice.Clear(Color.CornflowerBlue);
// 深度バッファを有効にする
this.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
foreach (ModelMesh mesh in this.model.Meshes)
{
// 球1を描画
foreach (BasicEffect effect in mesh.Effects)
{
// ワールドマトリックス(位置指定)
effect.World = Matrix.CreateTranslation(this.sphere1Position);
}
mesh.Draw();
// 球2を描画
foreach (BasicEffect effect in mesh.Effects)
{
// ワールドマトリックス(位置指定)
effect.World = Matrix.CreateTranslation(this.sphere2Position);
}
mesh.Draw();
}
// スプライトの描画準備
this.spriteBatch.Begin();
// 衝突判定表示
this.spriteBatch.DrawString(this.font,
"IsCollision : " + this.isCollision,
new Vector2(30.0f, 30.0f), Color.White);
// スプライトの一括描画
this.spriteBatch.End();
// 登録された DrawableGameComponent を描画する
base.Draw(gameTime);
}
}
}