Gericht

Pagina bijgewerkt :
Aanmaakdatum van pagina :

Opmerkingen over deze tips

Dit voorbeeld is gebaseerd op de programma's die op de volgende sites zijn gepubliceerd. Ik verander de code een beetje om het gemakkelijker te begrijpen en uit te leggen in het Japans. Kortom, we gebruiken de originele code zoals deze is, dus als je het daadwerkelijk in je spelprogramma gebruikt, corrigeer het dan tijdig en gebruik het.

Referentie site

Daarnaast wordt uitgelegd in de veronderstelling dat je enige basiskennis hebt over MonoGame en XNA. Zie MonoGame Tips en XNA Tips voor de onbeschofte.

In het bijzonder, omdat wiskunde, vectoren, goniometrische functies, matrices, enz. essentieel zijn, dus weet tot op zekere hoogte wat ze zijn.

milieu

perron
  • Windows 10
  • Code kan worden gebruikt op andere MonoGame-enabled platforms
Visual Studio
  • Visual Studio 2019
.NET Core
  • 3.1
Monogame
  • 3.8

Over voorbeelden

Het licht draait om het licht te volgen in de richting waarin het doel zich bevindt.

Als u de kat met een muis of toets beweegt,

Het licht volgt de kat.

Hoe te werken

Wat moet er gebeuren Keyboard Gamepad (XInput) Mouse Touch
Kattenbeweging ↑↓←→
  • Linker Stick
  • Dpad
Linker knop Overal aanraken
Einde spel Esc Terug - -

Wat te bereiden

Beelden die het doel en de lichten verplaatsen om te volgen.

programma

Download het programma voor alle code.

constant

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

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

Cat beweging en lichttracking zijn niet onmiddellijk, maar verplaatsen en roteren frame voor frame.

Trouwens, omdat speltijd niet wordt gebruikt in dit voorbeeld, kunnen de werkelijke tijdsbewerking en snelheid verschillen, afhankelijk van het platform. Probeer de speeltijd te gebruiken in het eigenlijke spel dat je maakt.

veld

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

Kortom, je hebt gewoon de informatie om de sprite weer te geven. Het belangrijkste is _spotlightAngle om automatisch te laten berekenen om naar het doel te wijzen.

bouwer

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

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

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

Er is niets om in gedachten te houden, behalve lagere resoluties voor mobiel.

Initialiseren methode

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

Hiermee bepaalt u de beginpositie van elke afbeelding. Houd er rekening mee dat u Viewport code schrijft na het base.Initialize() gebruik .

LoadContent-methode

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 Ik maak en laad texturen.

Daarnaast zijn hier de middenpositie van het kattenbeeld en de middenpositie (rotatieas) van de spot ingesteld. Omdat de middelste positie verandert afhankelijk van het beeld Het wordt individueel ingesteld.

Update methode

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

De HandleInput-methode behandelt de bewerkingen van de speler, die later zullen worden besproken. De positie van de kat wordt bepaald door input. We gebruiken ookViewport om te voorkomen dat de kat op het scherm verschijnt.

TurnToFace De methode draait de schijnwerper. De positie van de spot en de positie van de kat, de huidige hoek van het licht en de maximale rotatiesnelheid worden nu bepaald om de oriëntatie van het licht in het frame te bepalen. We zullen het later hebben over waar we het over hebben.

Trouwens, ik gebruik het niet in deze Tips, maar je moet de gameTime-variabele gebruiken om de spelsnelheid te evenaren.

HandleInput-methode

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

Het invoerproces van de speler. Wat we hier doen is de katbeweging en het einde van de speloperatie .

Invoerapparaten worden op verschillende gebieden ondersteund: toetsenbord, gamepad, muis, aanraking .

Kortom, ik beweeg gewoon met een richtingstoets, raak deze aan of wijs de kat op de positie waarop ik heb geklikt, dus ik zal niet te veel in detail treden. Als een gedetailleerd controlepunt,

  • De stick is omgekeerd omdat de opwaartse richting positief is, terwijl de schermpositie positief is in de neerwaartse richting.
  • Ga niet verder dan punten nadat de kat de gewenste positie heeft bereikt met de muis of aanraking
  • Zodra u de rijrichting hebt bepaald, normaliseert u deze om er een eenheidsvector van te maken (alleen richting) en vermenigvuldigt u ten slotte de bewegingssnelheid.

Het komt erop neer.

TurnToFace-methode

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

Dit is het belangrijkste proces voor Tips. Het proces van het bepalen van de hoek zodat de schijnwerper naar de kat is gericht. Pass spotlight positie, cat positie, huidige hoek, maximale rotatiesnelheid als argumenten. De retourwaarde is de uiteindelijke rotatiepositie. Het is niet de hoeveelheid rotatie vanaf de huidige positie.

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

Zoals vermeld in de opmerkingen, wordt goniometrische (inverse goniometrische functies) gebruikt om hoeken vanuit posities te berekenen. Als je de inhoud van wiskunde uitlegt, zal het een tip zijn, dus je moet weten dat je hier boogtangent moet gebruiken. De gebruikte methode is Math.Atan2 . Math.Atan merk op dat dit niet het geval is.

Zie de volgende afbeelding voor de retourwaarde. Retourneert een waarde van -π op +π met de richting van +x als 0. Merk op dat de +y-richting een positief resultaat retourneert, maar bij venstercoördinaten is de onderkant de +y-richting.

// これで猫を向くために必要な設定角度がわかりました。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);

Zodra u een hoek hebt om het doel te richten, wordt de rest bepaald de hoek van de huidige positie naar de gewenste hoek. Hier hebben we de maximale snelheid die in één frame kan worden gedraaid, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) de maximumwaarde niet overschrijdt.

Ook als het eenvoudig met plus of min wordt berekend, roteert het achteruit buiten het bereik van +π en -π, dus ik pas het aan met de WrapAngle-methode. Deze methode wordt in de volgende sectie beschreven.

Ten slotte, wanneer de hoek waarin het wordt gedraaid is bepaald, keert u terug.

WrapAngle methode

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

Als de hoek het bereik van -π tot +π overschrijdt, kan deze in de tegenovergestelde richting draaien van de richting die moet worden gedraaid, Als dit bereik wordt overschreden, telt u 2π op of trekt u af om het binnen het bovenstaande bereik te houden.

Tekenmethode

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

Zodra u de positie en rotatie hebt berekend, hoeft u alleen maar de sprite langs dat getal te tekenen. Om de kat uit te drukken alsof het licht wordt geraakt, wordt additieve synthese uitgevoerd bij het tekenen van de schijnwerper.

Samenvatting

Ik denk dat het heel belangrijk is om deze techniek te kennen, omdat er relatief veel situaties in het spel zijn waarin je bepaalt in welke richting je tegenstander zich bevindt. Deze methode wordt vaak gebruikt in 2D-games en kan worden berekend met quaternionen in 3D-games. Maar er zijn veel dingen die 2D-berekeningen in 3D gebruiken, dus het is veilig om te weten.