Menyasarkan

Laman dikemaskini :
Tarikh penciptaan halaman :

Nota mengenai Petua ini

Sampel ini berdasarkan program yang diterbitkan di laman web berikut. Saya menukar kod sedikit untuk menjadikannya lebih mudah difahami dan menerangkannya dalam bahasa Jepun. Pada asasnya, kami menggunakan kod asal seperti sedia ada, jadi jika anda benar-benar menggunakannya dalam program permainan anda, betulkannya tepat pada masanya dan gunakannya.

Laman rujukan

Di samping itu, ia dijelaskan dengan andaian bahawa anda mempunyai pengetahuan asas mengenai MonoGame dan XNA. Lihat Petua MonoGame dan Petua XNA untuk ruddly.

Khususnya, sebagai matematik, vektor, fungsi trigonometri, matriks, dan lain-lain adalah penting, jadi sila tahu apa yang mereka sedikit sebanyak.

Persekitaran

Platform
  • Windows 10
  • Kod boleh digunakan pada platform didayakan MonoGame yang lain
Visual Studio
  • Studio Visual 2019
Teras .NET
  • 3.1
MonoGame
  • 3.8

Mengenai sampel

Cahaya berputar untuk mengesan cahaya ke arah sasaran.

Jika anda menggerakkan kucing dengan tetikus atau kekunci,

Cahaya mengikuti kucing.

Bagaimana untuk beroperasi

Sentuhan
Apa yang Perlu Dilakukan Tetikus Gamepad Papan Kekunci (XInput)
Pergerakan Kucing ↑↓←→
  • Batang Kiri
  • DPad
Butang kiri Sentuh di mana-mana sahaja
Akhir permainan Esc Kembali - -

Apa yang perlu disediakan

Imej yang menggerakkan sasaran dan lampu untuk dijejaki.

Program

Muat turun program untuk semua kod.

Malar

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

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

Pergerakan kucing dan penjejakan cahaya tidak seketika, tetapi bergerak dan memutar bingkai mengikut bingkai.

Dengan cara ini, kerana masa permainan tidak digunakan dalam sampel ini, operasi dan kelajuan masa sebenar mungkin berbeza bergantung pada platform. Cuba gunakan masa permainan dalam permainan sebenar yang anda buat.

Bidang

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 asasnya, anda hanya mempunyai maklumat untuk memaparkan sprite. Yang penting ialah _spotlightAngle mengira secara automatik untuk menunjuk ke sasaran.

Constructor

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 mudah alih.

Memulakan kaedah

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 kedudukan awal setiap imej. Perhatikan bahawa anda Viewport menulis kod selepas base.Initialize() menggunakan .

LoadContent method

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 mencipta dan memuatkan tekstur.

Di samping itu, kedudukan tengah imej kucing dan kedudukan tengah (paksi putaran) sorotan ditetapkan di sini. Kerana kedudukan tengah berubah bergantung pada imej Ia ditetapkan secara individu.

Kaedah kemas kini

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

Kaedah HandleInput mengendalikan operasi pemain, yang akan dibincangkan kemudian. Kedudukan kucing ditentukan oleh input. Kami jugaViewport menggunakan untuk mengelakkan kucing daripada muncul di skrin.

TurnToFace Kaedah ini memutarkan sorotan. Kedudukan lampu sorot dan kedudukan kucing, sudut semasa cahaya dan kelajuan putaran maksimum kini ditentukan untuk menentukan orientasi cahaya dalam bingkai. Kita akan berbicara tentang apa yang kita tentang nanti.

Dengan cara ini, saya tidak menggunakannya dalam petua ini, tetapi anda perlu menggunakan pembolehubah gameTime untuk dipadankan dengan kelajuan permainan.

Kaedah 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 kita lakukan di sini adalah gerakan kucing dan akhir operasi permainan .

Peranti input disokong dalam pelbagai medan: Papan Kekunci, Gamepad, Tetikus, Sentuh .

Pada asasnya, saya hanya bergerak dengan kunci arah, menyentuhnya, atau menunjuk kucing pada kedudukan yang saya klik, jadi saya tidak akan terlalu terperinci. Sebagai titik kawalan terperinci,

  • Batang diterbalikkan kerana arah atas adalah positif, manakala kedudukan skrin adalah positif dalam arah ke bawah.
  • Jangan bergerak di luar mata selepas kucing mencapai kedudukan yang dikehendaki dengan tetikus atau sentuhan
  • Sebaik sahaja anda telah menentukan arah perjalanan, menormalkannya untuk menjadikannya vektor unit (arah sahaja) dan akhirnya darabkan kelajuan pergerakan.

Ia datang kepada.

Kaedah 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 Petua. Proses menentukan sudut supaya sorotan menghadap kucing. Lulus kedudukan sorotan, kedudukan kucing, sudut semasa, kelajuan putaran maksimum sebagai hujah. Nilai pulangan adalah kedudukan putaran akhir. Ia bukan jumlah putaran dari kedudukan semasa.

// この図を参照してください。
// 
//      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 yang dinyatakan dalam komen, trigonometri (fungsi trigonometri songsang) digunakan untuk mengira sudut dari kedudukan. Jika anda menerangkan kandungan matematik, ia akan menjadi tip, jadi anda harus tahu bahawa anda harus menggunakan arctangent di sini. Kaedah yang digunakan ialah Math.Atan2 . Math.Atan ambil perhatian bahawa ia tidak.

Lihat angka berikut untuk nilai pulangan. Mengembalikan nilai -π kepada +π dengan arah +x sebagai 0. Perhatikan bahawa arah +y mengembalikan hasil yang positif, tetapi pada tetingkap menyelaraskan bahagian 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);

Sebaik sahaja anda mempunyai sudut untuk menghalakan sasaran, selebihnya ditentukan sudut dari kedudukan semasa ke sudut yang dikehendaki. Di sini kita mempunyai kelajuan maksimum yang boleh diputar dalam satu bingkai, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) tidak melebihi nilai maksimum.

Juga, jika dikira hanya dengan tambah atau tolak, ia berputar ke belakang di luar julat +π dan -π, jadi saya menyesuaikannya dengan kaedah WrapAngle. Kaedah ini diterangkan dalam bahagian seterusnya.

Akhirnya, apabila sudut yang diputar ditentukan, kembali.

Kaedah 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 julat -π hingga +π, ia mungkin berputar ke arah yang bertentangan dengan arah yang harus diputar, Jika julat ini melebihi, tambah atau tolak 2π untuk mengekalkannya dalam julat di atas.

Kaedah cabutan

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

Sebaik sahaja anda telah mengira kedudukan dan putaran, yang perlu anda lakukan ialah melukis sprite di sepanjang nombor itu. Untuk menyatakan kucing seolah-olah cahaya terkena, sintesis tambahan dilakukan apabila melukis sorotan.

Ringkasan

Saya fikir sangat penting untuk mengetahui tentang teknik ini kerana terdapat banyak situasi dalam permainan di mana anda memutuskan cara lawan anda berada. Kaedah ini biasanya digunakan dalam permainan 2D, dan boleh dikira menggunakan quaternions dalam permainan 3D. Tetapi terdapat banyak perkara yang menggunakan pengiraan 2D dalam 3D, jadi selamat untuk diketahui.