Цел

Страницата се актуализира :
Дата на създаване на страница :

Бележки по този съвети

Тази извадка се основава на програмите, публикувани на следните сайтове. Променям кода малко, за да го направя по-лесен за разбиране и да го обясня на японски. По принцип използваме оригиналния код, какъвто е, така че ако всъщност го приемете във вашата игрова програма, коригирайте го своевременно и го използвайте.

Референтен сайт

Освен това се обяснява с предположението, че имате някои основни познания за MonoGame и XNA. Вижте Съвети за моногейм и Съвети за XNA за грубото.

По-специално, тъй като математиката, векторите, тригонометричните функции, матриците и т.н. са от съществено значение, така че моля, знайте какви са в известна степен.

околна среда

платформа
  • Windows 10
  • Код може да се използва на други monoGame-активирани платформи
Визуално студио
  • Визуално студио 2019
.NET Ядро
  • 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() използвате .

Метод на зарежданеСъдържание

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, за да съответства на скоростта на играта.

ДръжкаВ входен метод

/// <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. Този метод е описан в следващия раздел.

Накрая, когато се определи ъгълът, до който е завъртян, върнете се.

Метод на увиването

/// <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 игри, и може да се изчисли с помощта на quaternions в 3D игри. Но има много неща, които използват 2D изчисления в 3D, така че е безопасно да се знае.