Спрямований

Сторінка оновлюється :
Дата створення сторінки :

Примітки до цієї поради

Цей зразок базується на програмах, опублікованих на наступних сайтах. Я трохи змінюю код, щоб було легше зрозуміти і пояснити його японською мовою. В основному, ми використовуємо оригінальний код як є, тому, якщо ви дійсно приймаєте його у своїй ігровій програмі, вчасно виправте його та використовуйте.

Довідковий сайт

Крім того, це пояснюється припущенням, що у вас є деякі базові знання про MonoGame і XNA. Дивіться Поради Щодо MonoGame та поради XNA для грубо.

Зокрема, оскільки математика, вектори, тригонометричні функції, матриці тощо мають важливе значення, тому, будь ласка, знайте, що вони до певної міри.

середовище

платформа
  • Windows 10
  • Код можна використовувати на інших платформах з підтримкою MonoGame
Візуальна студія
  • Візуальна студія 2019
.NET Core
  • 3.1
Моногаграма
  • 3.8

Про зразки

Світло обертається, щоб відстежувати світло в напрямку цілі.

Якщо ви переміщуєте кішку мишкою або ключем,

Світло слідує за кішкою.

Як працювати

Що робити клавіатурі геймпад (XInput) миші
Рух кішок ↑↓←→
  • Ліва паличка
  • DPad
Ліва кнопка Доторкніться до будь-якого місця
Кінець гри Esc Задній - -

До чого готуватися

Зображення, які переміщують ціль і світло для відстеження.

програма

Завантажити програму для всього коду.

константа

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

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

Рух кішки і відстеження світла не є миттєвими, а рухаються і обертаються кадр за кадром.

До речі, оскільки ігровий час в даному зразку не використовується, фактична часова операція і швидкість можуть бути різними в залежності від платформи. Спробуйте використовувати ігровий час у фактичній грі, яку ви робите.

поле

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

В основному, у вас просто є інформація для відображення спрайту. Важливо мати автоматичний _spotlightAngle розрахунок, щоб вказати на ціль.

будівник

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

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

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

Немає нічого, що слід мати на увазі, крім більш низької роздільної здатності для мобільних пристроїв.

Метод ініціалізації

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

Визначає початкове положення кожного зображення. Зауважте, що ви пишете Viewport код після base.Initialize() використання .

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 Я створюю і завантажую текстури.

Крім того, тут встановлюються центральне положення зображення кішки і центральне положення (вісь обертання) прожектора. Тому що центральне положення змінюється в залежності від зображення Встановлюється індивідуально.

Метод оновлення

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

Метод HandleInput обробляє операції гравця, про які піде мова далі. Положення кішки визначається входом. Ми такожViewport використовуємо, щоб кішка не з'являлася на екрані.

TurnToFace Метод обертає прожектор. Положення прожектора і положення кішки, поточний кут світла і максимальна швидкість обертання тепер визначаються для визначення орієнтації світла в кадрі. Про те, про що ми поговоримо пізніше.

До речі, я не використовую його в цих порадах, але вам потрібно використовувати змінну gameTime, щоб відповідати швидкості гри.

Метод 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;
}

Процес введення гравця. Те, що ми робимо тут, - це котячий рух і кінець ігрової операції.

Пристрої вводу підтримуються в самих різних областях: Клавіатура, Геймпад, Миша, Дотик .

В основному, я просто рухаюся з спрямованою клавішею, торкаюся її або вказую коту на позицію, на яку я натиснув, тому я не буду вдаватися в занадто багато деталей. Як детальний контрольний пункт,

  • Палиця змінюється, тому що напрямок вгору позитивний, в той час як положення екрану позитивне в напрямку вниз.
  • Не виходьте за межі точок після того, як кішка досягне бажаного положення мишкою або дотиком
  • Після того, як ви визначили напрямок руху, нормалізують його, щоб зробити його одиницею вектора (тільки напрямок) і, нарешті, помножити швидкість руху.

Це доходить до.

Метод 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)
{
  // :
  // :
}

Це основний процес для порад. Процес визначення кута так, щоб прожектор зіткнувся з кішкою. Пропустіть положення прожектора, положення кішки, поточний кут, максимальну швидкість обертання в якості аргументів. Значення, що повертається, є кінцевою позицією обертання. Це не сума обертання з поточної позиції.

// この図を参照してください。
// 
//      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);

Як згадувалося в коментарях, тригонометричні (обернені тригонометричні функції) використовуються для обчислення кутів від позицій. Якщо ви поясните зміст математики, це буде підказка, тому ви повинні знати, що тут слід використовувати арктанген. Використовуваним методом є Math.Atan2 . Math.Atan Зверніть увагу, що це не так.

Дивіться наведену нижче цифру для значення, що повертається. Повертає значення від -π до +π з напрямком +x як 0. Зауважте, що напрямок +y повертає позитивний результат, але у вікні координати знизу є напрямок +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);

Після того, як у вас є кут, щоб вказати на ціль, решта визначається кутом від поточного положення до потрібного кута. Тут ми маємо максимальну швидкість, яку можна повернути в одному кадрі, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) не перевищує максимального значення.

Крім того, якщо розрахувати просто за плюс-мінус, він обертається назад за межі діапазону +π і -π, тому я налаштовую його методом WrapAngle. Цей метод описаний в наступному розділі.

Нарешті, коли визначається кут, до якого він обертається, повертається.

Метод 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;
}

Якщо кут перевищує діапазон від -π до +π, він може обертатися в протилежному напрямку в напрямку, який слід повернути, Якщо цей діапазон перевищено, додайте або відніміть 2π, щоб зберегти його в межах наведеного вище діапазону.

Метод малювання

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

Після того, як ви розрахували положення і обертання, все, що вам потрібно зробити, це намалювати спрайт уздовж цього числа. Для того щоб виразити кішку так, ніби попадання світла, при малюванні уваги проводиться адитивний синтез.

Зведення

Я думаю, що дуже важливо знати про цю техніку, тому що є відносно багато ситуацій в грі, де ви вирішуєте, в який бік знаходиться ваш опонент. Цей метод зазвичай використовується в 2D-іграх і може бути розрахований за допомогою кватерніонів в 3D-іграх. Але є багато речей, які використовують 2D-обчислення в 3D, тому це безпечно знати.