Jugement de collision balle à balle

Page mise à jour :
Date de création de la page :

résumé

Une sphère qui englobe chaque modèle est utilisée pour porter un jugement sur les coups. Dans cet exemple, la détection de collision est effectuée pour deux modèles de sphères.

球と球のあたり判定

Environnement d’exploitation

Conditions préalables

Versions XNA prises en charge
  • 4.0
Plates-formes prises en charge
  • Windows (XP SP2 ou version ultérieure, Vista, 7)
  • Xbox 360 (en anglais)
  • Windows Phone 7
Version du nuanceur de vertex requise par Windows 2.0
Version de Pixel Shader requise par Windows 2.0

Environnement d’exploitation

plateforme
  • Fenêtres 7
  • Xbox 360 (en anglais)
  • Émulateur Windows Phone 7

Comment utiliser l’échantillon

Fonctionne avec le clavierManette Xbox 360Souris tactile
Balle en mouvement 1 ↑↓←→ Joystick gauche Bouton gauche et glissement -

substance

À propos de Hit Judgment

Dans les jeux de tir et les jeux d’action, diverses collisions se produisent, telles que des collisions entre les personnages et les balles, et des collisions entre les personnages, il est donc nécessaire d’écrire un programme pour les juger. Dans le programme, on parle généralement de détection de collision. Il existe différents modèles de jugement des coups, du plus simple au plus complexe mathématiquement et physiquement. En général, les jeux sont souvent conçus pour réduire la charge de traitement plutôt que la précision, et s’il n’y a pas de déviation extrême dans la zone, cela n’a pas beaucoup d’importance si ce n’est pas précis.

Cette astuce décrit la détection de frappe « balle à balle » la plus courante et la moins fastidieuse. Puisqu’il s’agit d’une sphère et d’une sphère, nous parlons de jugement de collision dans l’espace tridimensionnel, mais il est possible de substituer presque le même processus aux collisions entre cercles dans l’espace bidimensionnel.

Comment fonctionne le jugement de la balle et de la frappe de balle

Deux paramètres sont utilisés pour déterminer la collision des sphères et des sphères : la « position » de chaque modèle et le « rayon » de la taille du modèle. Afin de déterminer si ces deux paramètres sont corrects, il est facile de comprendre en regardant la figure ci-dessous.

  • P : Position du modèle L : Distance entre deux points (P2-P1) R : Rayon de la sphère

当たっていない 接触衝突

Si la « distance entre les deux modèles » est « L » et que la « somme des rayons des deux modèles » est « R », alors « L < R » signifie qu’ils entrent en collision. En revanche, s’il s’agit de « L > R », cela signifie qu’il n’y a pas de collision. La détection de collision de « L = R » n’a pas d’importance pour l’un ou l’autre.

champ

/// <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;

Vous pouvez écrire votre propre programme pour le processus de détection des coups, mais le framework XNA a une structure appelée « BoundingSphere » qui facilite la gestion des paramètres de la sphère et de la détection des coups, donc j’aimerais l’utiliser.

Chaque ModelMesh de la classe Model chargée contient déjà l’information de sphère « BoundingSphere » qui englobe le modèle, nous avons donc préparé un champ « baseBoundingSphere » pour le récupérer.

D’autres caractéristiques incluent la position de chaque sphère, une sphère englobante utilisée pour déterminer l’impact de deux sphères et un indicateur de collision.

Charger

// モデルを作成 
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;

Chaque ModelMesh d’un modèle possède une propriété BoundingSphere qui vous permet d’obtenir des informations sur la sphère qui englobe le modèle dans le ModelMesh. Étant donné que le modèle que nous utilisons n’a qu’un seul ModelMesh, nous avons copié la sphère inclusive du premier index vers la baseBoundingSphere.

Étant donné que le rayon de la sphère est fixe, le rayon est prédéfini dans la propriété Radius des deux sphères englobantes.

Frapper Jugement

// 衝突判定用の球を設定 
this.sphere1BoundingSphere.Center = 
    this.sphere1Position + this.baseBoundingSphere.Center; 
this.sphere2BoundingSphere.Center = 
    this.sphere2Position + this.baseBoundingSphere.Center; 

// 衝突判定 
this.isCollision = 
    this.sphere1BoundingSphere.Intersects(this.sphere2BoundingSphere);

Utilisez la méthode BoundingSphere.Intersects pour déterminer les collisions sphère-sphère. Étant donné que nous avons déjà défini un rayon pour les deux sphères englobantes, nous définissons la propriété BoundingSphere.Center sur la position de la sphère plus les coordonnées centrales de la sphère parapluie d’origine. Les coordonnées centrales de la sphère d’origine sont ajoutées car le centre de la sphère inclusive n’est pas nécessairement l’origine.

Si vous spécifiez l’autre BoundingSphere en tant qu’argument de BoundingSphere.Intersects, il renverra un booléen pour voir s’il entre en collision ou non, nous l’obtenons. Cet indicateur est utilisé pour dessiner des caractères afin de voir s’ils sont corrects.

Comment calculer le jugement de frappe

Le framework XNA fournit une structure de détection de collision utile appelée BoundingSphere, qui peut être utilisée non seulement pour les sphères, mais aussi pour les collisions avec différentes formes telles que les rayons (lignes) et les boîtes.

Toutefois, s’il s’agit d’une collision entre sphères, elle peut être déterminée par un calcul simple sans utiliser BoundingSphere.Intersects.

  • Résultat (s’il est touché) = Distance entre la position de la sphère 2 et la position de la sphère 1 < Rayon de la sphère 1 + Rayon de la sphère 2

Si vous l’écrivez par programmation

this.isCollision = (this.sphere2Position - this.sphere1Position).Length() <
                   (this.sphere1BoundingSphere.Radius + this.sphere2BoundingSphere.Radius);

Il s’agit de. (Le processus de détection de collision ci-dessus suppose que le centre de la sphère inclusive est l’origine, comme une sphère parfaite, de sorte qu’il ne prend pas en compte le centre de la sphère inclusive du modèle.) Si vous y réfléchissez, cela ressemblera à ce qui suit)

this.isCollision = ((this.sphere2Position + this.baseBoundingSphere.Center) -
                        (this.sphere1Position + this.baseBoundingSphere.Center)).Length() <
                   (this.sphere1BoundingSphere.Radius + this.sphere2BoundingSphere.Radius);

Tous les codes

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);
        }
    }
}