Scopul

Pagina actualizată :
Data creării paginii :

Note cu privire la acest Sfaturi

Acest eșantion se bazează pe programele publicate pe următoarele site-uri. Am schimba codul un pic pentru a face mai ușor să înțeleagă și să explice în japoneză. Practic, folosim codul original așa cum este, așa că, dacă îl adoptați efectiv în programul de joc, corectați-l în timp util și utilizați-l.

Site-ul de referință

În plus, se explică pe presupunerea că aveți câteva cunoștințe de bază despre MonoGame și XNA. A se vedea MonoGame Sfaturi și XNA Sfaturi pentru ruddly.

În special, deoarece matematica, vectorii, funcțiile trigonometrice, matricile etc. sunt esențiale, așa că vă rugăm să știți care sunt acestea într-o anumită măsură.

mediu

peron
  • Windows 10
  • Codul poate fi utilizat pe alte platforme compatibile cu MonoGame
Visual Studio
  • Visual Studio 2019
.NET Core
  • 3.1
MonoGame
  • 3.8

Despre probe

Lumina se rotește pentru a urmări lumina în direcția în care este ținta.

Dacă mutați pisica cu un șoarece sau o cheie,

Lumina urmează pisica.

Cum să operezi

Ce este de a face Tastatura Gamepad (XInput) Mouse Touch
Mișcarea pisicilor ↑↓←→
  • Stick stânga
  • DPad
Butonul din stânga Atinge oriunde
Sfârșitul jocului Esc Spate - -

Ce să pregătești

Imagini care mută ținta și luminile de urmărit.

program

Descărcați programul pentru tot codul.

constantă

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

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

Mișcarea pisicilor și urmărirea luminii nu sunt instantanee, ci se mișcă și se rotesc cadru cu cadru.

Apropo, deoarece timpul de joc nu este utilizat în acest eșantion, funcționarea efectivă a timpului și viteza pot fi diferite în funcție de platformă. Încercați să utilizați timpul de joc în jocul real pe care îl faceți.

câmp

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

Practic, aveți doar informații pentru a afișa sprite. Important este _spotlightAngle să calculați automat pentru a indica spre țintă.

constructor

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

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

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

Nu este nimic de reținut, în afară de rezoluțiile mai mici pentru dispozitive mobile.

Metoda de inițializare

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

Stabilește poziția inițială a fiecărei imagini. Rețineți că scrieți Viewport cod după ce utilizați base.Initialize() .

Metoda 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 Creez și încarc texturi.

În plus, poziția centrală a imaginii pisicii și poziția centrală (axa de rotație) a reflectoarelor sunt setate aici. Deoarece poziția centrală se modifică în funcție de imagine Acesta este stabilit individual.

Metoda de actualizare

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

Metoda HandleInput se ocupă de operațiunile jucătorului, care vor fi discutate mai târziu. Poziția pisicii este determinată de intrare. De asemenea,Viewport folosim pentru a preveni apariția pisicii pe ecran.

TurnToFace Metoda rotește lumina reflectoarelor. Poziția reflectoarelor și poziția pisicii, unghiul curent al luminii și viteza maximă de rotație sunt acum determinate pentru a determina orientarea luminii în cadru. Vom vorbi despre ce este vorba mai târziu.

Apropo, eu nu sunt folosind-o în acest Sfaturi, dar aveți nevoie pentru a utiliza variabila gameTime pentru a se potrivi cu viteza de joc.

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

Procesul de intrare al jucătorului. Ceea ce facem aici este mișcarea pisicii și sfârșitul operațiunii jocului .

Dispozitivele de intrare sunt acceptate într-o varietate de domenii: Tastatură, Gamepad, Mouse, Atingere .

Practic, mă mișc doar cu o cheie direcțională, o ating sau arăt pisica în poziția în care am făcut clic, așa că nu voi intra în prea multe detalii. Ca punct de control detaliat,

  • Stick-ul este inversat, deoarece direcția în sus este pozitivă, în timp ce poziția ecranului este pozitivă în direcția descendentă.
  • Nu vă deplasați dincolo de puncte după ce pisica ajunge în poziția dorită cu mouse-ul sau atingeți
  • Odată ce ați determinat direcția de deplasare, normalizați-o pentru a o face un vector unitate (numai direcția) și, în cele din urmă, înmulțiți viteza de mișcare.

Este vorba de a.

Metoda 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)
{
  // :
  // :
}

Acesta este procesul principal pentru Sfaturi. Procesul de determinare a unghiului, astfel încât lumina reflectoarelor să se confrunte cu pisica. Treceți poziția reflectoarelor, poziția pisicii, unghiul curent, viteza maximă de rotație ca argumente. Valoarea returnată este poziția finală de rotație. Nu este cantitatea de rotație din poziția curentă.

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

După cum sa menționat în comentarii, trigonometrice (funcții trigonometrice inverse) este utilizat pentru a calcula unghiurile din poziții. Dacă explicați conținutul matematicii, acesta va fi un sfat, deci trebuie să știți că ar trebui să utilizați arctangent aici. Metoda utilizată este Math.Atan2 . Math.Atan rețineți că nu este.

Vedeți următoarea figură pentru valoarea returnată. Returnează o valoare de -π la +π cu direcția de +x ca 0. Rețineți că direcția +y returnează un rezultat pozitiv, dar la coordonatele ferestrei partea de jos este direcția +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);

Odată ce aveți un unghi pentru a indica ținta, restul este determinat unghiul de la poziția curentă la unghiul dorit. Aici avem viteza maximă care poate fi rotită într-un singur cadru, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) nu depășește valoarea maximă.

De asemenea, dacă este calculat pur și simplu prin plus sau minus, se rotește înapoi dincolo de intervalul de +π și -π, așa că îl ajustez cu metoda WrapAngle. Această metodă este descrisă în secțiunea următoare.

În cele din urmă, când se determină unghiul la care este rotit, reveniți.

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

Dacă unghiul depășește intervalul de -π la +π, acesta se poate roti în direcția opusă direcției care trebuie rotită, Dacă acest interval este depășit, adăugați sau scădeți 2π pentru a-l păstra în intervalul de mai sus.

Metoda desenării

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

După ce ați calculat poziția și rotația, tot ce trebuie să faceți este să desenați spritele de-a lungul acelui număr. Pentru a exprima pisica ca și cum lumina ar fi lovită, sinteza aditivă se efectuează atunci când se desenează lumina reflectoarelor.

Rezumat

Cred că este foarte important să știi despre această tehnică pentru că există relativ multe situații în joc în care decizi în ce fel se află adversarul tău. Această metodă este utilizată în mod obișnuit în jocurile 2D și poate fi calculată folosind cuaternioni în jocurile 3D. Dar există multe lucruri care folosesc calcule 2D în 3D, deci este sigur de știut.