Bertujuan

Halaman Diperbarui :
Tanggal pembuatan halaman :

Catatan tentang Tips ini

Sampel ini didasarkan pada program yang dipublikasikan di situs berikut. Saya mengubah kode sedikit untuk membuatnya lebih mudah untuk memahami dan menjelaskannya dalam bahasa Jepang. Pada dasarnya, kami menggunakan kode asli apa adanya, jadi jika Anda benar-benar mengadopsinya dalam program permainan Anda, perbaiki tepat waktu dan gunakan.

Situs referensi

Selain itu, dijelaskan dengan asumsi bahwa Anda memiliki beberapa pengetahuan dasar tentang MonoGame dan XNA. Lihat Tips MonoGame dan Tips XNA untuk kemerahan.

Secara khusus, karena matematika, vektor, fungsi trigonometri, matriks, dll. sangat penting, jadi ketahuilah apa itu sampai batas tertentu.

lingkungan

balei-balei
  • Windows 10
  • Kode dapat digunakan pada platform berkemampuan MonoGame lainnya
Visual Studio
  • Studio Visual 2019
.NET Core
  • 3.1
MonoGame
  • 3.8

Tentang sampel

Cahaya berputar untuk melacak cahaya ke arah target.

Jika Anda memindahkan kucing dengan mouse atau kunci,

Cahaya mengikuti kucing.

Cara mengoperasikannya

Apa yang Harus Dilakukan Keyboard Gamepad (XInput) Mouse Touch
Gerakan Kucing ↑↓←→
  • Tongkat Kiri
  • DPad
Tombol Kiri Sentuh di mana saja
Akhir dari permainan Esc Belakang - -

Apa yang harus dipersiapkan

Gambar yang menggerakkan target dan lampu untuk dilacak.

rencana

Unduh program untuk semua kode.

konstan

/// <summary>猫が動くスピード。これはフレームあたりのピクセル数です。</summary>
const float CatSpeed = 10.0f;

/// <summary>スポットライトが回転する速度。これはフレームあたりのラジアンで表されます。</summary>
const float SpotlightTurnSpeed = 0.025f;

Gerakan kucing dan pelacakan cahaya tidak instan, tetapi bergerak dan memutar bingkai demi bingkai.

Ngomong-ngomong, karena waktu permainan tidak digunakan dalam sampel ini, operasi dan kecepatan waktu sebenarnya mungkin berbeda tergantung pada platform. Cobalah untuk menggunakan waktu permainan dalam permainan yang sebenarnya Anda buat.

kebun

readonly GraphicsDeviceManager _graphics;

/// <summary>画像を表示するための SpriteBatch です。</summary>
SpriteBatch _spriteBatch;

/// <summary>スポットライトのテクスチャー(画像)です。</summary>
Texture2D _spotlightTexture;

/// <summary>スポットライトの位置です。</summary>
Vector2 _spotlightPosition = new Vector2();

/// <summary>スポットライトの中心位置です、ここを中心に回転します。</summary>
Vector2 _spotlightOrigin = new Vector2();

/// <summary>スポットライトが現在向いている角度。単位はラジアンです。値 0 は右を指します。</summary>
float _spotlightAngle = 0.0f;


/// <summary>猫のテクスチャー(画像)です。</summary>
Texture2D _catTexture;

/// <summary>猫の位置です。</summary>
Vector2 _catPosition = new Vector2();

/// <summary>猫の中心位置です。</summary>
Vector2 _catOrigin = new Vector2();

Pada dasarnya, Anda hanya memiliki informasi untuk menampilkan sprite. Yang penting adalah _spotlightAngle menghitung secara otomatis untuk menunjuk ke target.

pembangun

public AimingGame()
{
  graphics = new GraphicsDeviceManager(this);
  ontent.RootDirectory = "Content";
  sMouseVisible = true;

  graphics.PreferredBackBufferWidth = 320;
  graphics.PreferredBackBufferHeight = 480;

  // フルスクリーンにしたい場合はコメントを外してください。
  //graphics.IsFullScreen = true;
}

Tidak ada yang perlu diingat selain resolusi yang lebih rendah untuk seluler.

Metode inisialisasi

protected override void Initialize()
{
  base.Initialize();

  // base.Initialize が完了すると、GraphicsDevice が作成され、ビューポートの大きさがわかります。
  // スポットライトを画面の中央に配置する必要があるため、ビューポートを使用してそれがどこにあるかを計算します。
  Viewport vp = _graphics.GraphicsDevice.Viewport;
  _spotlightPosition.X = vp.X + vp.Width / 2;
  _spotlightPosition.Y = vp.Y + vp.Height / 2;

  // もう一度ビューポートサイズを使用して、今度は猫を画面に配置します。位置は x=1/4 y=1/2 です。
  _catPosition.X = vp.X + vp.Width / 4;
  _catPosition.Y = vp.Y + vp.Height / 2;
}

Menentukan posisi awal setiap gambar. Perhatikan bahwa Anda Viewport menulis kode setelah base.Initialize() menggunakan .

Metode LoadContent

protected override void LoadContent()
{
  // テクスチャをロードし、スプライトバッチを作成します。
  _spotlightTexture = Content.Load<Texture2D>("spotlight");
  _catTexture = Content.Load<Texture2D>("cat");
  _spriteBatch = new SpriteBatch(_graphics.GraphicsDevice);

  // テクスチャをロードしたので、それらを使用して、描画時に使用するいくつかの値を計算できます。
  // スポットライトを描くときは、光源の周りを回転する必要があります。
  // 今回用意した画像は左中央が光源なのでその位置を中心位置として設定します。
  _spotlightOrigin.X = 0;
  _spotlightOrigin.Y = _spotlightTexture.Height / 2;

  // 猫の中心位置を決定します。とりあえず画像の真ん中とします。
  _catOrigin.X = _catTexture.Width / 2;
  _catOrigin.Y = _catTexture.Height / 2;
}

SpriteBatch Saya membuat dan memuat tekstur.

Selain itu, posisi tengah gambar kucing dan posisi tengah (sumbu rotasi) sorotan diatur di sini. Karena posisi tengah berubah tergantung pada gambar Itu diatur secara individual.

Metode pembaruan

protected override void Update(GameTime gameTime)
{
  HandleInput();

  // 猫が画面外に出ないように制御します。
  Viewport vp = _graphics.GraphicsDevice.Viewport;
  _catPosition.X = MathHelper.Clamp(_catPosition.X, vp.X, vp.X + vp.Width);
  _catPosition.Y = MathHelper.Clamp(_catPosition.Y, vp.Y, vp.Y + vp.Height);

  // TurnToFace 関数を使用して、_spotlightAngle を更新して猫の方を向くようにします。
  _spotlightAngle = TurnToFace(_spotlightPosition, _catPosition, _spotlightAngle, SpotlightTurnSpeed);

  base.Update(gameTime);
}

Metode HandleInput menangani operasi pemain, yang akan dibahas nanti. Posisi kucing ditentukan oleh input. Kami jugaViewport digunakan untuk mencegah kucing muncul di layar.

TurnToFace Metode ini memutar sorotan. Posisi lampu sorot dan posisi kucing, sudut cahaya saat ini dan kecepatan rotasi maksimum sekarang ditentukan untuk menentukan orientasi cahaya dalam bingkai. Kita akan berbicara tentang apa yang kita tentang nanti.

Ngomong-ngomong, saya tidak menggunakannya di Tips ini, tetapi Anda perlu menggunakan variabel gameTime agar sesuai dengan kecepatan permainan.

Metode HandleInput

/// <summary>
/// 入力を処理します。
/// </summary>
void HandleInput()
{
  KeyboardState currentKeyboardState = Keyboard.GetState();
  GamePadState currentGamePadState = GamePad.GetState(PlayerIndex.One);
  MouseState currentMouseState = Mouse.GetState();
  TouchCollection currentTouchState = TouchPanel.GetState();

  // ゲーム終了操作を確認します。
  if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
      currentGamePadState.Buttons.Back == ButtonState.Pressed)
  {
    Exit();
  }

  // ユーザーが猫を動かしたいかどうかを確認します。 catMovement というベクトルを作成します。
  // これは、すべてのユーザーの入力の合計を格納します。
  Vector2 catMovement = currentGamePadState.ThumbSticks.Left;

  // y を反転:スティックでは、下は -1 ですが、画面では、下がプラスです。
  catMovement.Y *= -1;

  if (currentKeyboardState.IsKeyDown(Keys.Left) ||
      currentGamePadState.DPad.Left == ButtonState.Pressed)
  {
    catMovement.X -= 1.0f;
  }
  if (currentKeyboardState.IsKeyDown(Keys.Right) ||
      currentGamePadState.DPad.Right == ButtonState.Pressed)
  {
    catMovement.X += 1.0f;
  }
  if (currentKeyboardState.IsKeyDown(Keys.Up) ||
      currentGamePadState.DPad.Up == ButtonState.Pressed)
  {
    catMovement.Y -= 1.0f;
  }
  if (currentKeyboardState.IsKeyDown(Keys.Down) ||
      currentGamePadState.DPad.Down == ButtonState.Pressed)
  {
    catMovement.Y += 1.0f;
  }

  // タッチポイントに向かって移動します。
  // CatSpeed からタッチポイントまでの距離内に入ると、猫の速度を落とします。
  float smoothStop = 1;

  //if (currentTouchState != null )
  {
    if (currentTouchState.Count > 0)
    {
      Vector2 touchPosition = currentTouchState[0].Position;
      if (touchPosition != _catPosition)
      {
        catMovement = touchPosition - _catPosition;
        float delta = CatSpeed - MathHelper.Clamp(catMovement.Length(), 0, CatSpeed);
        smoothStop = 1 - delta / CatSpeed;
      }
    }
  }

  Vector2 mousePosition = new Vector2(currentMouseState.X, currentMouseState.Y);
  if (currentMouseState.LeftButton == ButtonState.Pressed && mousePosition != _catPosition)
  {
    catMovement = mousePosition - _catPosition;
    float delta = CatSpeed - MathHelper.Clamp(catMovement.Length(), 0, CatSpeed);
    smoothStop = 1 - delta / CatSpeed;
  }

  // ユーザーの入力を正規化して、猫が CatSpeed より速く進むことができないようにします。
  if (catMovement != Vector2.Zero)
  {
    catMovement.Normalize();
  }

  _catPosition += catMovement * CatSpeed * smoothStop;
}

Proses input pemain. Apa yang kami lakukan di sini adalah gerakan kucing dan akhir dari operasi permainan .

Perangkat input didukung dalam berbagai bidang: Keyboard, Gamepad, Mouse, Touch .

Pada dasarnya, saya hanya bergerak dengan tombol arah, menyentuhnya, atau mengarahkan kucing ke posisi yang saya klik, jadi saya tidak akan terlalu detail. Sebagai titik kontrol yang terperinci,

  • Tongkat dibalik karena arah atas positif, sedangkan posisi layar positif ke arah bawah.
  • Jangan bergerak melampaui titik setelah kucing mencapai posisi yang diinginkan dengan mouse atau sentuhan.
  • Setelah Anda menentukan arah perjalanan, menormalkannya untuk menjadikannya vektor unit (hanya arah) dan akhirnya kalikan kecepatan gerakan.

Itu datang ke.

Metode TurnToFace

/// <summary>
/// オブジェクトの位置、ターゲットの位置、現在の角度、および最大回転速度を指定して、
/// オブジェクトが直面する必要のある角度を計算します。
/// </summary>
/// <param name="position">オブジェクトの位置。ここではスポットライトの位置。</param>
/// <param name="faceThis">ターゲットの位置。ここでは猫の位置。</param>
/// <param name="currentAngle">現在の角度。</param>
/// <param name="turnSpeed">最大回転速度。</param>
/// <returns>決定された角度。</returns>
private static float TurnToFace(Vector2 position, Vector2 faceThis, float currentAngle, float turnSpeed)
{
  // :
  // :
}

Ini adalah proses utama untuk tips. Proses penentuan sudut sehingga sorotan menghadap kucing. Lewati posisi spotlight, posisi kucing, sudut saat ini, kecepatan rotasi maksimum sebagai argumen. Nilai pengembalian adalah posisi rotasi akhir. Ini bukan jumlah rotasi dari posisi saat ini.

// この図を参照してください。
// 
//      C 
//     /|
//    / |
//   /  | y
//  / o |
// S----
//     x
// 
// ここで、S はスポットライトの位置、C は猫の位置、o は猫を指すためにスポットライトが向いている角度です。
// o の値を知る必要があります。
// これには三角法を使用して算出します。
// 
//      tan(theta)       = 高さ / 底辺
//      tan(o)           = y / x
// 
// この方程式の両辺のアークタンジェントを取ると
// 
//      arctan( tan(o) ) = arctan( y / x )
//      o                = arctan( y / x )
// 
// したがって、x と y を使用して、「desiredAngle」である o を見つけることができます。
// x と y は、2つのオブジェクト間の位置の違いにすぎません。
float x = faceThis.X - position.X;
float y = faceThis.Y - position.Y;

// Atan2 関数を使用します。Atanは、y / x のアークタンジェントを計算し、x と y の符号を使用して、
// 結果を入れるデカルト象限を決定するという追加の利点があります。
// https://docs.microsoft.com/dotnet/api/system.math.atan2
float desiredAngle = (float)Math.Atan2(y, x);

Seperti disebutkan dalam komentar, trigonometri (fungsi trigonometri terbalik) digunakan untuk menghitung sudut dari posisi. Jika Anda menjelaskan isi matematika, itu akan menjadi tip, jadi Anda harus tahu bahwa Anda harus menggunakan arctangent di sini. Metode yang digunakan adalah Math.Atan2 . Math.Atan Perhatikan bahwa itu tidak.

Lihat gambar berikut untuk nilai pengembalian. Mengembalikan nilai -π ke +π dengan arah +x sebagai 0. Perhatikan bahwa arah +y mengembalikan hasil positif, tetapi pada koordinat jendela bagian bawah adalah arah +y.

// これで猫を向くために必要な設定角度がわかりました。turnSpeed (回転スピード) に制約されていなければ簡単です。
// desiredAngle を返すだけです。
// 代わりに回転量を計算し、それが turnSpeed を超えないようにする必要があります。

// まず、WrapAngle を使用して、-Pi から Pi(-180度から180度)の結果を取得し、
// どれだけ回転させたいかを判断します。
// これは猫の方向に向くのに必要な回転角度です。
float difference = WrapAngle(desiredAngle - currentAngle);

// -turnSpeed と turnSpeed の間にクランプします。
// 要は1フレームの回転角度上限を超えないようにします。
difference = MathHelper.Clamp(difference, -turnSpeed, turnSpeed);

// したがって、ターゲットに最も近いのは currentAngle + difference です。
// もう一度 WrapAngle を使用して、それを返します。
return WrapAngle(currentAngle + difference);

Setelah Anda memiliki sudut untuk mengarahkan target, sisanya ditentukan sudut dari posisi saat ini ke sudut yang diinginkan. Di sini kita memiliki kecepatan maksimum yang dapat diputar dalam satu frame, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) tidak melebihi nilai maksimum.

Juga, jika dihitung hanya dengan plus atau minus, itu berputar mundur di luar kisaran +π dan -π, jadi saya menyesuaikannya dengan metode WrapAngle. Metode ini dijelaskan di bagian berikutnya.

Akhirnya, ketika sudut yang diputar ditentukan, kembali.

Metode WrapAngle

/// <summary>
/// -Pi と Pi の間のラジアンで表される角度を返します。
/// 例えば degree で -200°なら +360°して 160°とします。反対側も同様です。
/// </summary>
private static float WrapAngle(float radians)
{
  while (radians < -MathHelper.Pi)
  {
    radians += MathHelper.TwoPi;
  }
  while (radians > MathHelper.Pi)
  {
    radians -= MathHelper.TwoPi;
  }
  return radians;
}

Jika sudut melebihi kisaran -π hingga +π, itu dapat berputar ke arah yang berlawanan dengan arah yang harus diputar, Jika rentang ini terlampaui, tambahkan atau kurangi 2π untuk menyimpannya dalam kisaran di atas.

Metode menggambar

protected override void Draw(GameTime gameTime)
{
  GraphicsDevice device = _graphics.GraphicsDevice;

  device.Clear(Color.Black);

  // 猫を描画します。
  _spriteBatch.Begin();
  _spriteBatch.Draw(_catTexture, _catPosition, null, Color.White, 0.0f, _catOrigin, 1.0f, SpriteEffects.None, 0.0f);
  _spriteBatch.End();

  // 加算合成でスプライトバッチを開始し、スポットライトを当てます。 加算合成は、ライトや火などの効果に非常に適しています。
  _spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive);
  _spriteBatch.Draw(_spotlightTexture, _spotlightPosition, null, Color.White, _spotlightAngle, _spotlightOrigin, 1.0f, SpriteEffects.None, 0.0f);
  _spriteBatch.End();

  base.Draw(gameTime);
}

Setelah Anda menghitung posisi dan rotasi, yang harus Anda lakukan adalah menggambar sprite di sepanjang angka itu. Untuk mengekspresikan kucing seolah-olah cahaya dipukul, sintesis aditif dilakukan saat menggambar sorotan.

Ringkasan

Saya pikir sangat penting untuk mengetahui tentang teknik ini karena ada relatif banyak situasi dalam permainan di mana Anda memutuskan ke arah mana lawan Anda berada. Metode ini biasanya digunakan dalam game 2D, dan dapat dihitung menggunakan quaternions dalam game 3D. Tetapi ada banyak hal yang menggunakan perhitungan 2D dalam 3D, jadi aman untuk diketahui.