Sigter

Side opdateret :
Dato for oprettelse af side :

Bemærkninger til dette tip

Denne prøve er baseret på de programmer, der er offentliggjort på følgende websteder. Jeg ændrer koden lidt for at gøre det lettere at forstå og forklare det på japansk. Grundlæggende bruger vi den originale kode, som den er, så hvis du rent faktisk vedtager den i dit spilprogram, skal du rette den rettidigt og bruge den.

Reference websted

Derudover forklares det ud fra den antagelse, at du har en vis grundlæggende viden om MonoGame og XNA. Se MonoGame Tips og XNA Tips for ruddly.

Især da matematik, vektorer, trigonometriske funktioner, matricer osv. er afgørende, så vær venlig at vide, hvad de er til en vis grad.

miljø

perron
  • Vinduer 10
  • Kode kan bruges på andre MonoGame-aktiverede platforme
Visual Studio
  • Visual Studio 2019
.NET-kerne
  • 3.1
Monogame
  • 3.8

Om prøver

Lyset roterer for at spore lyset i den retning, målet er.

Hvis du flytter katten med en mus eller nøgle,

Lyset følger katten.

Sådan betjenes

Hvad skal man gøre Keyboard Gamepad (XInput) Mouse Touch
Kattebevægelse ↑↓←→
  • Venstre pind
  • DPad
Venstre knap Tryk hvor som helst
Slutningen af spillet Esc Ryg - -

Hvad skal man forberede sig på?

Billeder, der flytter målet og lysene til at spore.

program

Download programmet til al koden.

konstant

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

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

Kattebevægelse og lyssporing er ikke øjeblikkelige, men bevæger og roterer ramme for ramme.

Forresten, da spilletid ikke bruges i denne prøve, kan den faktiske tidsdrift og hastighed være forskellig afhængigt af platformen. Prøv at bruge spilletid i det faktiske spil, du laver.

mark

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

Dybest set har du bare oplysningerne til at vise sprite. Det vigtige er _spotlightAngle at have automatisk beregne for at pege på målet.

konstruktør

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

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

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

Der er intet at huske på andet end lavere opløsninger til mobil.

Initialiser metode

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

Bestemmer den oprindelige position for hvert billede. Bemærk, at du Viewport skriver kode efter brug base.Initialize() .

LoadContent-metode

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 Jeg opretter og indlæser teksturer.

Derudover er midterpositionen af kattebilledet og spotlightets midterposition (rotationsakse) indstillet her. Fordi midterpositionen ændres afhængigt af billedet Det indstilles individuelt.

Opdater metode

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

HandleInput-metoden håndterer spillerens operationer, som vil blive diskuteret senere. Kattens position bestemmes af input. Vi bruger ogsåViewport til at forhindre katten i at blive vist på skærmen.

TurnToFace Metoden roterer rampelyset. Placeringen af spotlightet og kattens position, lysets strømvinkel og den maksimale rotationshastighed bestemmes nu for at bestemme lysets orientering i rammen. Vi vil tale om, hvad vi handler om senere.

Forresten bruger jeg det ikke i disse tip, men du skal bruge gameTime-variablen til at matche spilhastigheden.

HandleInput metode

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

Spillerens inputproces. Det, vi laver her , er katteflytningen og slutningen af spiloperationen .

Inputenheder understøttes på en række forskellige områder: Tastatur, Gamepad, Mus, Touch .

Dybest set bevæger jeg mig bare med en retningsnøgle, rører ved den eller peger katten på den position, jeg klikkede på, så jeg vil ikke gå for meget i detaljer. Som et detaljeret kontrolpunkt,

  • Pinden vendes, fordi opretningen er positiv, mens skærmpositionen er positiv i nedadgående retning.
  • Flyt ikke ud over punkter, når katten når sin ønskede position med musen eller rører ved
  • Når du har bestemt kørselsretningen, skal du normalisere den for at gøre den til en enhedsvektor (kun retning) og til sidst multiplicere bevægelseshastigheden.

Det kommer til.

TurnToFace-metode

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

Dette er den vigtigste proces for tips. Processen med at bestemme vinklen, så spotlightet vender mod katten. Pass spotlight position, kat position, strømvinkel, maksimal rotationshastighed som argumenter. Returværdien er den endelige rotationsposition. Det er ikke mængden af rotation fra den aktuelle position.

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

Som nævnt i kommentarerne bruges trigonometriske (inverse trigonometriske funktioner) til at beregne vinkler fra positioner. Hvis du forklarer indholdet af matematik, vil det være et tip, så du skal vide, at du skal bruge arctangent her. Den anvendte metode er Math.Atan2 . Math.Atan Bemærk, at det ikke er det.

Se følgende figur for returværdien. Returnerer en værdi på -π til +π med retningen +x som 0. Bemærk, at +y-retningen returnerer et positivt resultat, men ved vindueskoordinater er bunden +y-retningen.

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

Når du har en vinkel til at pege på målet, bestemmes resten vinklen fra den aktuelle position til den ønskede vinkel. Her har vi den maksimale hastighed, der kan drejes i en ramme, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) overstiger ikke den maksimale værdi.

Også, hvis den beregnes blot med plus eller minus, roterer den baglæns ud over området +π og -π, så jeg justerer den med WrapAngle-metoden. Denne metode er beskrevet i næste afsnit.

Endelig, når den vinkel, som den drejes til, bestemmes, skal du vende tilbage.

WrapAngle metode

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

Hvis vinklen overstiger området fra -π til +π, kan den rotere i modsat retning af den retning, der skal drejes, Hvis dette interval overskrides, skal du tilføje eller trække 2π for at holde det inden for ovenstående interval.

Tegn metode

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

Når du har beregnet positionen og rotationen, er alt hvad du skal gøre, at trække sprite langs dette tal. For at udtrykke katten som om lyset er ramt, udføres additiv syntese, når man tegner rampelyset.

Resumé

Jeg synes, det er meget vigtigt at kende til denne teknik, fordi der er relativt mange situationer i spillet, hvor du bestemmer, hvilken vej din modstander er i. Denne metode bruges ofte i 2D-spil og kan beregnes ved hjælp af kvatertioner i 3D-spil. Men der er mange ting, der bruger 2D-beregninger i 3D, så det er sikkert at vide.