Mira

Pagina aggiornata :
Data di creazione della pagina :

Note su questo Suggerimenti

Questo esempio si basa sui programmi pubblicati nei seguenti siti. Cambio un po 'il codice per renderlo più facile da capire e spiegarlo in giapponese. Fondamentalmente, usiamo il codice originale così com'è, quindi se lo adotti effettivamente nel tuo programma di gioco, correggilo in modo tempestivo e usalo.

Sito di riferimento

Inoltre, è spiegato sul presupposto che tu abbia alcune conoscenze di base su MonoGame e XNA. Vedi Suggerimenti monogioco e Suggerimenti XNA per il ruddly.

In particolare, poiché la matematica, i vettori, le funzioni trigonometriche, le matrici, ecc. sono essenziali, quindi per favore sappi cosa sono in una certa misura.

ambiente

piattaforma
  • Finestre 10
  • Il codice può essere utilizzato su altre piattaforme abilitate per MonoGame
Visual Studio
  • Visual Studio 2019
.NET Core
  • 3.1
Monogioco
  • 3.8

Informazioni sugli esempi

La luce ruota per tracciare la luce nella direzione in cui si trova il bersaglio.

Se muovi il gatto con un mouse o un tasto,

La luce segue il gatto.

Come operare

Cosa fare Tastiera Gamepad (XInput) Mouse Touch
Movimento del gatto ↑↓←→
  • Levetta sinistra
  • DPad ·
Pulsante sinistro Tocca ovunque
Fine del gioco Esc Indietro - -

Cosa preparare

Immagini che spostano il bersaglio e le luci da tracciare.

programma

Scarica il programma per tutto il codice.

costante

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

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

Il movimento del gatto e il tracciamento della luce non sono istantanei, ma si muovono e ruotano fotogramma per fotogramma.

A proposito, poiché il tempo di gioco non viene utilizzato in questo esempio, il funzionamento e la velocità effettivi del tempo potrebbero essere diversi a seconda della piattaforma. Prova a usare il tempo di gioco nel gioco reale che fai.

campo

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

Fondamentalmente, hai solo le informazioni per visualizzare lo sprite. L'importante è _spotlightAngle avere calcolato automaticamente di puntare al target.

costruttore

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

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

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

Non c'è nulla da tenere a mente se non risoluzioni più basse per i dispositivi mobili.

Metodo Initialize

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

Determina la posizione iniziale di ogni immagine. Si noti che si Viewport sta scrivendo codice dopo aver base.Initialize() utilizzato .

Metodo 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 Sto creando e caricando texture.

Inoltre, la posizione centrale dell'immagine del gatto e la posizione centrale (asse di rotazione) del faretto sono impostate qui. Perché la posizione centrale cambia a seconda dell'immagine È impostato individualmente.

Metodo di aggiornamento

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

Il metodo HandleInput gestisce le operazioni del giocatore, che verranno discusse in seguito. La posizione del gatto è determinata dall'input. Usiamo ancheViewport per impedire al gatto di apparire sullo schermo.

TurnToFace Il metodo ruota il riflettore. La posizione del faretto e la posizione del gatto, l'angolo corrente della luce e la velocità massima di rotazione sono ora determinati per determinare l'orientamento della luce nel fotogramma. Parleremo di ciò di cui parleremo più avanti.

A proposito, non lo sto usando in questo Suggerimenti, ma è necessario utilizzare la variabile gameTime per abbinare la velocità di gioco.

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

Processo di input del giocatore. Quello che stiamo facendo qui è la mossa del gatto e la fine dell'operazione di gioco .

I dispositivi di input sono supportati in una varietà di campi: tastiera, gamepad, mouse, tocco .

Fondamentalmente, mi sto solo muovendo con un tasto direzionale, toccandolo o puntando il gatto nella posizione in cui ho cliccato, quindi non entrerò troppo nei dettagli. Come punto di controllo dettagliato,

  • Lo stick è invertito perché la direzione verso l'alto è positiva, mentre la posizione dello schermo è positiva nella direzione verso il basso.
  • Non andare oltre i punti dopo che il gatto ha raggiunto la posizione desiderata con il mouse o il tocco
  • Una volta determinata la direzione di marcia, normalizzarla per renderla un vettore unitario (solo direzione) e infine moltiplicare la velocità di movimento.

Si tratta di.

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

Questo è il processo principale per Tips. Il processo di determinazione dell'angolo in modo che il riflettore sia rivolto verso il gatto. Passare la posizione del faretto, la posizione del gatto, l'angolo di corrente, la massima velocità di rotazione come argomenti. Il valore restituito è la posizione di rotazione finale. Non è la quantità di rotazione dalla posizione corrente.

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

Come accennato nei commenti, trigonometrico (funzioni trigonometriche inverse) viene utilizzato per calcolare gli angoli dalle posizioni. Se spieghi i contenuti della matematica, sarà un suggerimento, quindi dovresti sapere che dovresti usare arctangent qui. Il metodo utilizzato è Math.Atan2 . Math.Atan si noti che non lo è.

Per il valore restituito, vedere la figura riportata di seguito. Restituisce un valore compreso tra -π e +π con la direzione +x come 0. Si noti che la direzione +y restituisce un risultato positivo, ma alle coordinate della finestra la parte inferiore è la direzione +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);

Una volta che hai un angolo per puntare il bersaglio, il resto viene determinato l'angolo dalla posizione corrente all'angolo desiderato. Qui abbiamo la velocità massima che può essere ruotata in un fotogramma, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) non supera il valore massimo.

Inoltre, se calcolato semplicemente da più o meno, ruota all'indietro oltre l'intervallo di +π e -π, quindi lo regolo con il metodo WrapAngle. Questo metodo è descritto nella sezione successiva.

Infine, quando viene determinato l'angolo a cui viene ruotato, tornare.

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

Se l'angolo supera l'intervallo da -π a +π, può ruotare nella direzione opposta alla direzione che deve essere ruotata, Se questo intervallo viene superato, aggiungere o sottrarre 2π per mantenerlo all'interno dell'intervallo precedente.

Metodo Draw

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

Una volta calcolata la posizione e la rotazione, tutto ciò che devi fare è disegnare lo sprite lungo quel numero. Per esprimere il gatto come se la luce fosse colpita, la sintesi additiva viene eseguita quando si disegna il riflettore.

Sommario

Penso che sia molto importante conoscere questa tecnica perché ci sono relativamente molte situazioni nel gioco in cui decidi in che modo si trova il tuo avversario. Questo metodo è comunemente usato nei giochi 2D e può essere calcolato utilizzando i quaternioni nei giochi 3D. Ma ci sono molte cose che usano i calcoli 2D in 3D, quindi è sicuro saperlo.