Visant

Page mise à jour :
Date de création de la page :

Notes sur ces conseils

Cet exemple est basé sur les programmes publiés sur les sites suivants. Je change un peu le code pour le rendre plus facile à comprendre et à expliquer en japonais. Fondamentalement, nous utilisons le code original tel quel, donc si vous l’adoptez réellement dans votre programme de jeu, corrigez-le en temps opportun et utilisez-le.

Site de référence

En outre, il est expliqué en supposant que vous avez quelques connaissances de base sur MonoGame et XNA. Voir Conseils MonoGame et Conseils XNA pour le ruddly.

En particulier, comme les mathématiques, les vecteurs, les fonctions trigonométriques, les matrices, etc. sont essentiels, sachez donc ce qu’ils sont dans une certaine mesure.

environnement

plateforme
  • Fenêtres 10
  • Le code peut être utilisé sur d’autres plates-formes compatibles MonoGame
Visual Studio
  • Visual Studio 2019
.NET Core
  • 3.1
MonoGame
  • 3.8

À propos des exemples

La lumière tourne pour suivre la lumière dans la direction de la cible.

Si vous déplacez le chat avec une souris ou une touche,

La lumière suit le chat.

Comment opérer

Que faire Clavier Gamepad (XInput) Souris Touch
Mouvement du chat ↑↓←→
  • Bâton gauche
  • Le DPad
Bouton gauche Touchez n’importe où
Fin de partie Esc Précédent - -

Ce qu’il faut préparer

Images qui déplacent la cible et les lumières à suivre.

programme

Téléchargez le programme pour tout le code.

constant

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

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

Le mouvement du chat et le suivi de la lumière ne sont pas instantanés, mais se déplacent et font pivoter image par image.

Soit dit en passant, étant donné que le temps de jeu n’est pas utilisé dans cet exemple, le fonctionnement réel du temps et la vitesse peuvent être différents en fonction de la plate-forme. Essayez d’utiliser le temps de jeu dans le jeu réel que vous créez.

champ

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

Fondamentalement, vous avez juste les informations pour afficher le sprite. L’important est _spotlightAngle d’avoir un calcul automatique pour pointer vers la cible.

constructeur

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

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

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

Il n’y a rien à garder à l’esprit autre que des résolutions inférieures pour mobile.

Initialize, méthode

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

Détermine la position initiale de chaque image. Notez que vous écrivez Viewport du base.Initialize() code après avoir utilisé .

LoadContent, méthode

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 Je crée et charge des textures.

De plus, la position centrale de l’image du chat et la position centrale (axe de rotation) du projecteur sont définies ici. Parce que la position centrale change en fonction de l’image Il est défini individuellement.

Méthode Update

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

La méthode HandleInput gère les opérations du joueur, qui seront discutées plus tard. La position du chat est déterminée par l’entrée. Nous utilisons égalementViewport pour empêcher le chat d’apparaître à l’écran.

TurnToFace La méthode fait pivoter le projecteur. La position du projecteur et la position du chat, l’angle actuel de la lumière et la vitesse de rotation maximale sont maintenant déterminés pour déterminer l’orientation de la lumière dans le cadre. Nous parlerons de ce dont nous parlons plus tard.

Soit dit en passant, je ne l’utilise pas dans ces conseils, mais vous devez utiliser la variable gameTime pour correspondre à la vitesse du jeu.

HandleInput, méthode

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

Le processus de saisie du joueur. Ce que nous faisons ici, c’est le mouvement du chat et l’opération de fin de jeu .

Les périphériques d’entrée sont pris en charge dans une variété de domaines: clavier, manette de jeu, souris, tactile .

Fondamentalement, je me déplace simplement avec une touche directionnelle, je la touche ou je pointe le chat sur la position sur laquelle j’ai cliqué, donc je n’entrerai pas trop dans les détails. En tant que point de contrôle détaillé,

  • Le bâton est inversé car la direction vers le haut est positive, tandis que la position de l’écran est positive dans la direction descendante.
  • Ne vous déplacez pas au-delà des points après que le chat a atteint la position souhaitée avec la souris ou le toucher
  • Une fois que vous avez déterminé la direction de déplacement, normalisez-la pour en faire un vecteur unitaire (direction uniquement) et enfin multipliez la vitesse de déplacement.

Il s’agit de.

TurnToFace, méthode

/// <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’est le processus principal pour tips. Processus de détermination de l’angle afin que le projecteur fasse face au chat. Passez la position du projecteur, la position du chat, l’angle actuel, la vitesse de rotation maximale comme arguments. La valeur de retour est la position de rotation finale. Ce n’est pas la quantité de rotation à partir de la position actuelle.

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

Comme mentionné dans les commentaires, la trigonométrie (fonctions trigonométriques inverses) est utilisée pour calculer les angles à partir des positions. Si vous expliquez le contenu des mathématiques, ce sera un conseil, vous devez donc savoir que vous devez utiliser arctangent ici. La méthode utilisée est Math.Atan2 . Math.Atan notez que ce n’est pas le cas.

Reportez-vous à la figure suivante pour connaître la valeur renvoyée. Renvoie une valeur de -π à +π avec la direction de +x comme 0. Notez que la direction +y renvoie un résultat positif, mais aux coordonnées de la fenêtre, le bas est la direction +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);

Une fois que vous avez un angle pour pointer la cible, le reste est déterminé l’angle de la position actuelle à l’angle souhaité. Ici, nous avons la vitesse maximale qui peut être tournée dans une image, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) ne dépasse pas la valeur maximale.

De plus, s’il est calculé simplement par plus ou moins, il tourne vers l’arrière au-delà de la plage de +π et -π, donc je l’ajuste avec la méthode WrapAngle. Cette méthode est décrite dans la section suivante.

Enfin, lorsque l’angle auquel il est tourné est déterminé, revenez.

WrapAngle, méthode

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

Si l’angle dépasse la plage de -π à +π, il peut tourner dans la direction opposée à la direction qui doit être tournée, Si cette plage est dépassée, ajoutez ou soustrayez 2π pour la maintenir dans la plage ci-dessus.

Draw, méthode

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

Une fois que vous avez calculé la position et la rotation, tout ce que vous avez à faire est de dessiner le sprite le long de ce nombre. Afin d’exprimer le chat comme si la lumière était frappée, une synthèse additive est effectuée lors du dessin du projecteur.

Résumé

Je pense qu’il est très important de connaître cette technique car il y a relativement beaucoup de situations dans le jeu où vous décidez dans quelle direction se trouve votre adversaire. Cette méthode est couramment utilisée dans les jeux 2D et peut être calculée à l’aide de quaternions dans les jeux 3D. Mais il y a beaucoup de choses qui utilisent des calculs 2D en 3D, il est donc prudent de le savoir.