Julgamento de colisão bola a bola
resumo
Uma esfera que engloba cada modelo é usada para fazer um julgamento de acerto. Nesta amostra, a detecção de colisão é realizada para dois modelos de esfera.
Ambiente operacional
Pré-requisitos
| Versões XNA suportadas | 
 | 
| Plataformas suportadas | 
 | 
| Versão do sombreador de vértice necessária do Windows | 2.0 | 
| Versão do sombreador de pixel necessária do Windows | 2.0 | 
Ambiente operacional
| plataforma | 
 | 
Como trabalhar com a amostra
| Funciona tecladoXbox | 360 controllermouse | touch | ||
|---|---|---|---|---|
| Bola em movimento 1 | ↑↓←→ | Vara Esquerda | Botão esquerdo & Arraste | - | 
substância
Sobre o Hit Judgment
Em jogos de tiro e jogos de ação, várias colisões ocorrem, como colisões entre personagens e balas, e colisões entre personagens, por isso é necessário escrever um programa para julgá-los. No programa, isso é geralmente referido como detecção de colisão. Existem vários padrões de julgamento de acertos, do simples ao complexo matematicamente e fisicamente. Em geral, os jogos são muitas vezes feitos para reduzir a carga de processamento em vez de precisão, e se não houver nenhum desvio extremo na área, não importa muito se não for preciso.
Esta dica descreve a detecção de acerto "bola a bola" mais comum e menos onerosa. Como é uma esfera e uma esfera, estamos falando de julgamento de colisão no espaço tridimensional, mas é possível substituir quase o mesmo processo por colisões entre círculos no espaço bidimensional.
Como funciona o julgamento da bola e da bola batida
Dois parâmetros são usados para determinar a colisão de esferas e esferas: a "posição" de cada modelo e o "raio" do tamanho do modelo. Para determinar se esses dois parâmetros estão corretos, é fácil entender observando a figura abaixo.
- P: Posição do modelo L: Distância entre dois pontos (P2-P1) R: Raio da esfera
 
Se a "distância entre os dois modelos" é "L" e a "soma dos raios dos dois modelos" é "R", então "L < R" significa que eles estão colidindo. Por outro lado, se for "L > R", significa que não há colisão. A detecção de colisão de "L = R" não importa para nenhum dos dois.
campo
 <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;
Você pode escrever seu próprio programa para o processo de detecção de hits, mas o XNA Framework tem uma estrutura chamada "BoundingSphere" que facilita o manuseio dos parâmetros da esfera e da detecção de hits, então eu gostaria de usá-lo.
Cada ModelMesh da classe Model carregada já contém as informações de esfera "BoundingSphere" que englobam o modelo, então preparamos um campo "baseBoundingSphere" para recuperá-lo.
Outras características incluem a posição de cada esfera, uma BoundingSphere usada para determinar o impacto de duas esferas, e uma bandeira de colisão.
Carga
// モデルを作成 
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;
Cada ModelMesh em um Model tem uma propriedade BoundingSphere que permite obter informações sobre a esfera que engloba o modelo no ModelMesh. Como o modelo que estamos usando tem um único ModelMesh, copiamos a esfera inclusiva do primeiro índice para o baseBoundingSphere.
Como o raio da esfera é fixo, o raio é predefinido na propriedade Radius das duas BoundingSpheres.
Acertar o julgamento
// 衝突判定用の球を設定 
this.sphere1BoundingSphere.Center = 
    this.sphere1Position + this.baseBoundingSphere.Center; 
this.sphere2BoundingSphere.Center = 
    this.sphere2Position + this.baseBoundingSphere.Center; 
// 衝突判定 
this.isCollision = 
    this.sphere1BoundingSphere.Intersects(this.sphere2BoundingSphere);
Use o método BoundingSphere.Intersects para determinar colisões esfera-a-esfera. Como já temos um raio definido para as duas BoundingSpheres, definimos a propriedade BoundingSphere.Center para a posição da esfera mais as coordenadas centrais da esfera guarda-chuva original. As coordenadas centrais da esfera original são adicionadas porque o centro da esfera inclusiva não é necessariamente a origem.
Se você especificar o outro BoundingSphere como um argumento para BoundingSphere.Intersects, ele retornará um bool para ver se ele está colidindo ou não, então nós o obtemos. Esse sinalizador é usado para desenhar caracteres para ver se eles estão corretos.
Como calcular o julgamento de acerto
O XNA Framework fornece uma estrutura de detecção de colisão útil chamada BoundingSphere, que pode ser usada não apenas para esferas, mas também para colisões com diferentes formas, como raios (linhas) e caixas.
No entanto, se for uma colisão entre esferas, ela pode ser determinada por um cálculo simples sem usar BoundingSphere.Intersects.
- Resultado (se é atingido) = Distância entre a posição da Esfera 2 e a posição da Esfera 1 < Raio da Esfera 1 + Raio da Esfera 2
Se você escrevê-lo programaticamente
this.isCollision = (this.sphere2Position - this.sphere1Position).Length() <
                   (this.sphere1BoundingSphere.Radius + this.sphere2BoundingSphere.Radius);
Chega. (O processo de detecção de colisão acima assume que o centro da esfera inclusiva é a origem, como uma esfera perfeita, por isso não leva em conta o centro da esfera inclusiva do modelo.) Se você considerá-lo, será parecido com o seguinte)
this.isCollision = ((this.sphere2Position + this.baseBoundingSphere.Center) -
                        (this.sphere1Position + this.baseBoundingSphere.Center)).Length() <
                   (this.sphere1BoundingSphere.Radius + this.sphere2BoundingSphere.Radius);
Todos os códigos
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);
        }
    }
}