Aspirant

Pàgina actualitzada :
Data de creació de la pàgina :

Notes sobre aquests consells

Aquesta mostra es basa en els programes publicats en els següents llocs. Canvio una mica el codi per fer-lo més fàcil d'entendre i explicar-lo en japonès. Bàsicament, utilitzem el codi original tal com és, de manera que si realment l'adopteu al vostre programa de joc, corregiu-lo de manera oportuna i utilitzeu-lo.

Lloc de referència

A més, s'explica en el supòsit que vostè té alguns coneixements bàsics sobre MonoGame i XNA. Vegeu Consells de MonoGame i Consells XNA per al groller.

En particular, com les matemàtiques, vectors, funcions trigonomètriques, matrius, etc. són essencials, així que si us plau, sapigueu què són fins a cert punt.

entorn

plataforma
  • Windows 10
  • El codi es pot utilitzar en altres plataformes habilitades per a MonoGame
Estudi visual
  • Visual Studio 2019
Nucli .NET
  • 3.1
Monojoi
  • 3.8

Quant a les mostres

La llum gira per seguir la llum en la direcció en què es troba l'objectiu.

Si moveu el gat amb un ratolí o una tecla,

La llum segueix el gat.

Com operar

Què s'ha de fer amb el teclat gamepad (XInput) Tàctil del ratolí
Moviment del Gat ↑↓←→
  • Pal esquerre
  • DPad
botó esquerre Toca a qualsevol lloc
Final de la partida Esc Esquena - -

Què preparar

Imatges que mouen l'objectiu i els llums a seguir.

programa

Descarrega't el programa per a tot el codi.

constant

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

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

El moviment del gat i el seguiment de la llum no són instantanis, sinó que mouen i giren fotograma per fotograma.

Per cert, atès que el temps de joc no s'utilitza en aquesta mostra, el temps real d'operació i velocitat pot ser diferent depenent de la plataforma. Intenta utilitzar el temps de joc en el joc real que fas.

camp

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

Bàsicament, només teniu la informació per mostrar el personatge. L'important és _spotlightAngle tenir calculat automàticament per apuntar a l'objectiu.

constructor

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

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

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

No hi ha res a tenir en compte a part de resolucions més baixes per a mòbils.

Inicialitza el mètode

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 posició inicial de cada imatge. Tingueu en compte que esteu Viewport escrivint codi després d'utilitzar base.Initialize() .

Mètode 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 Estic creant i carregant textures.

A més, la posició central de la imatge del gat i la posició central (eix de rotació) del focus s'estableixen aquí. Perquè la posició central canvia en funció de la imatge S'estableix de manera individual.

Mètode d'actualització

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

El mètode HandleInput s'encarrega de les operacions del jugador, que es discutiran més endavant. La posició del gat està determinada per l'entrada. TambéViewport utilitzem per evitar que el gat aparegui a la pantalla.

TurnToFace El mètode gira el focus. La posició del focus i la posició del gat, l'angle actual de la llum i la velocitat màxima de rotació estan ara determinats a determinar l'orientació de la llum en el marc. Parlarem del que parlarem més endavant.

Per cert, no l'estic utilitzant en aquests Consells, però heu d'utilitzar la variable gameTime per coincidir amb la velocitat del joc.

Mètode 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;
}

El procés d'entrada del jugador. El que estem fent aquí és el moviment del gat i el final de l'operació del joc .

Els dispositius d'entrada són compatibles amb una varietat de camps: Teclat, Gamepad, Ratolí, Tacte .

Bàsicament, només em moc amb una clau direccional, tocant-la o assenyalant el gat a la posició en què he fet clic, de manera que no entraré en massa detalls. Com a punt de control detallat,

  • El pal s'inverteix perquè la direcció cap amunt és positiva, mentre que la posició de la pantalla és positiva en la direcció descendent.
  • No et moguis més enllà dels punts després que el gat arribi a la posició desitjada amb el ratolí o el tacte
  • Un cop hagis determinat la direcció del viatge, normalitza-la per convertir-lo en un vector unitari (només direcció) i finalment multipliqui la velocitat de moviment.

Es tracta.

Mètode 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)
{
  // :
  // :
}

Aquest és el procés principal de tips. El procés de determinar l'angle de manera que el focus s'enfronti al gat. Passa la posició del focus, la posició del gat, l'angle actual, la velocitat màxima de rotació com a arguments. El valor de retorn és la posició de rotació final. No és la quantitat de rotació des de la posició actual.

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

Com s'esmenta en els comentaris, trigonomètric (funcions trigonomètriques inverses) s'utilitza per calcular angles des de posicions. Si expliqueu el contingut de les matemàtiques, serà un consell, de manera que heu de saber que heu d'utilitzar arctangent aquí. El mètode utilitzat és Math.Atan2 . Math.Atan Tingueu en compte que no ho és.

Vegeu la figura següent per al valor de retorn. Retorna un valor de -π a +π amb la direcció de +x com a 0. Tingueu en compte que la direcció +y retorna un resultat positiu, però a la finestra es coordina la part inferior és la direcció +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 vegada que es té un angle per apuntar l'objectiu, la resta es determina l'angle des de la posició actual fins a l'angle desitjat. Aquí tenim la velocitat màxima que es pot girar en un fotograma, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) no supera el valor màxim.

A més, si es calcula simplement per més o menys, gira cap enrere més enllà del rang de +π i -π, de manera que l'ajusto amb el mètode WrapAngle. Aquest mètode es descriu a la secció següent.

Finalment, quan es determini l'angle al qual es gira, torna.

Mètode 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;
}

Si l'angle supera l'interval de -π a +π, pot girar en direcció oposada a la direcció que s'ha de girar, Si se supera aquest interval, afegiu o restau 2π per mantenir-lo dins de l'interval anterior.

Mètode de dibuix

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

Un cop hàgiu calculat la posició i la rotació, tot el que heu de fer és dibuixar el personatge al llarg d'aquest número. Per tal d'expressar el gat com si es colpegés la llum, es realitza síntesi additiva quan es dibuixa el focus.

Resum

Crec que és molt important conèixer aquesta tècnica perquè hi ha relativament moltes situacions en el joc on tu decideixes en quina forma es troba el teu oponent. Aquest mètode s'utilitza comunament en jocs 2D, i es pot calcular utilitzant quaternions en jocs 3D. Però hi ha moltes coses que utilitzen càlculs 2D en 3D, de manera que és segur saber-ho.