Palli-palli kokkupõrke otsus
Kokkuvõte
Iga mudelit hõlmavat sfääri kasutatakse tabamuse otsuse tegemiseks. Selles proovis tehakse kokkupõrke tuvastamine kahe sfäärimudeli jaoks.
Töökeskkond
Eeltingimused
Toetatud XNA versioonid |
|
Toetatud platvormid |
|
Windowsi nõutav vertex shaderi versioon | 2.0 |
Windowsi nõutav versioon Pixel Shader | 2.0 |
Töökeskkond
platvorm |
|
Kuidas prooviga töötada
Töötab klaviatuurXbox | 360 kontrollermouse | touch | ||
---|---|---|---|---|
Liikuv pall 1 | ↑↓←→ | Vasak kepp | Vasak nupp ja lohistamine | - |
aine
Hit Judgmenti kohta
Tulistamismängudes ja märulimängudes tekivad mitmesugused kokkupõrked, näiteks tegelaste ja kuulide kokkupõrked ning tegelaste kokkupõrked, mistõttu on vaja kirjutada programm nende hindamiseks. Programmis nimetatakse seda tavaliselt kokkupõrke tuvastamiseks. Löögiotsuse mustreid on erinevaid, alates lihtsast kuni matemaatiliselt ja füüsiliselt keerukani. Üldiselt tehakse mänge sageli pigem töötlemiskoormuse kui täpsuse vähendamiseks ja kui piirkonnas ei ole äärmuslikke kõrvalekaldeid, ei ole see oluline, kui see ei ole täpne.
See näpunäide kirjeldab kõige tavalisemat ja kõige vähem koormavat "pallilt pallile" tabamuse tuvastamist. Kuna tegemist on sfääri ja sfääriga, räägime kokkupõrkeotsusest kolmemõõtmelises ruumis, kuid kahemõõtmelises ruumis olevate ringide kokkupõrgete puhul on võimalik asendada peaaegu sama protsess.
Kuidas pall ja pall tabavad
Sfääride ja sfääride kokkupõrke määramiseks kasutatakse kahte parameetrit: iga mudeli "asukoht" ja mudeli suuruse "raadius". Selleks, et teha kindlaks, kas need kaks parameetrit on õiged, on allpool toodud joonist vaadates lihtne mõista.
- P: mudeli asukoht L: kahe punkti vaheline kaugus (P2-P1) R: kera raadius
Kui "kahe mudeli vaheline kaugus" on "L" ja "kahe mudeli raadiuste summa" on "R", siis "L < R" tähendab, et nad põrkuvad. Teisest küljest, kui see on "L > R", tähendab see, et kokkupõrget ei toimu. "L = R" kokkupõrke tuvastamine ei ole kummagi jaoks oluline.
põld
<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;
Tabamuse tuvastamise protsessi jaoks saate kirjutada oma programmi, kuid XNA raamistikul on struktuur nimega "BoundingSphere", mis muudab sfääri parameetrite ja tabamuse tuvastamise lihtsaks, nii et tahaksin seda kasutada.
Iga laaditud mudeliklassi ModelMesh sisaldab juba sfääriteavet "BoundingSphere", mis hõlmab mudelit, seega oleme selle hankimiseks ette valmistanud välja "baseBoundingSphere".
Muude funktsioonide hulka kuuluvad iga sfääri asukoht, kahe sfääri mõju määramiseks kasutatav BoundingSphere ja kokkupõrkelipp.
Laadima
// モデルを作成
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;
Igal mudeli ModelMeshil on omadus BoundingSphere, mis võimaldab teil saada teavet sfääri kohta, mis hõlmab mudelit ModelMeshis. Kuna meie kasutataval mudelil on üks ModelMesh, oleme kopeerinud kaasava sfääri esimesest indeksist baseBoundingSphere'i.
Kuna kera raadius on fikseeritud, on raadius eelseadistatud kahe BoundingSphere'i raadiuse omaduses.
Lööge kohtuotsus
// 衝突判定用の球を設定
this.sphere1BoundingSphere.Center =
this.sphere1Position + this.baseBoundingSphere.Center;
this.sphere2BoundingSphere.Center =
this.sphere2Position + this.baseBoundingSphere.Center;
// 衝突判定
this.isCollision =
this.sphere1BoundingSphere.Intersects(this.sphere2BoundingSphere);
Kasutage sfääridevaheliste kokkupõrgete määramiseks meetodit BoundingSphere.Intersects. Kuna meil on juba kahe BoundingSphere'i jaoks määratud raadius, seadsime atribuudi BoundingSphere.Center sfääri asendisse pluss algse vihmavarjusfääri keskkoordinaadid. Algse sfääri keskkoordinaadid lisatakse, sest kaasava sfääri keskpunkt ei pruugi olla päritolu.
Kui määrate teise BoundingSphere'i argumendiks BoundingSphere.Intersects, tagastab see booli, et näha, kas see põrkub kokku või mitte, nii et me saame selle. Seda lippu kasutatakse märkide joonistamiseks, et näha, kas need on õiged.
Kuidas arvutada tabatud kohtuotsust
XNA raamistik pakub kasulikku kokkupõrke tuvastamise struktuuri nimega BoundingSphere, mida saab kasutada mitte ainult sfääride, vaid ka erineva kujuga kokkupõrgete, näiteks kiirte (joonte) ja kastide jaoks.
Kui aga tegemist on sfääride kokkupõrkega, saab selle määrata lihtsa arvutusega ilma BoundingSphere.Intersects kasutamata.
- Tulemus (kas see on tabamus) = sfääri 2 asukoha ja sfääri 1 asukoha vaheline kaugus < sfääri 1 raadius + sfääri 2 raadius
Kui kirjutate selle programmiliselt
this.isCollision = (this.sphere2Position - this.sphere1Position).Length() <
(this.sphere1BoundingSphere.Radius + this.sphere2BoundingSphere.Radius);
See on. (Ülaltoodud kokkupõrke tuvastamise protsess eeldab, et kaasava sfääri keskpunkt on päritolu, nagu täiuslik sfäär, nii et see ei võta arvesse mudeli kaasava sfääri keskpunkti.) Kui te seda kaalute, näeb see välja järgmine)
this.isCollision = ((this.sphere2Position + this.baseBoundingSphere.Center) -
(this.sphere1Position + this.baseBoundingSphere.Center)).Length() <
(this.sphere1BoundingSphere.Radius + this.sphere2BoundingSphere.Radius);
Kõik koodid
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);
}
}
}