Sikte

Sidan uppdaterad :
Datum för skapande av sida :

Anteckningar om detta tips

Det här exemplet baseras på programmen som publiceras på följande webbplatser. Jag ändrar koden lite för att göra det lättare att förstå och förklara den på japanska. I grund och botten använder vi den ursprungliga koden som den är, så om du faktiskt antar den i ditt spelprogram, korrigera den i tid och använd den.

Referensplats

Dessutom förklaras det under antagandet att du har grundläggande kunskaper om MonoGame och XNA. Se MonoGame Tips och XNA Tips för ruddly.

I synnerhet, eftersom matematik, vektorer, trigonometriska funktioner, matriser etc. är viktiga, så vänligen veta vad de är i viss utsträckning.

miljö

plattform
  • Windows 10
  • Kod kan användas på andra MonoGame-aktiverade plattformar
Visuell studio
  • Visual Studio 2019
.NET-kärna
  • 3.1
MonoGame
  • 3.8

Om prover

Ljuset roterar för att spåra ljuset i den riktning som målet är.

Om du flyttar katten med en mus eller nyckel,

Ljuset följer katten.

Hur man arbetar

Vad ska du göra tangentbord gamepad (XInput) Mouse Touch
Katt rörelse ↑↓←→
  • Vänster pinne
  • DPad
Vänster knapp Tryck var som helst
Spelets slut Esc Tillbaka - -

Vad man ska förbereda

Bilder som flyttar målet och lamporna för att spåra.

program

Ladda ner programmet för all kod.

konstant

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

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

Kattrörelser och ljusspårning är inte omedelbara, utan rör och roterar ram för ram.

Förresten, eftersom speltiden inte används i det här exemplet kan den faktiska tidsdriften och hastigheten vara olika beroende på plattformen. Försök att använda speltid i det faktiska spelet du gör.

fält

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

I grund och botten har du bara informationen för att visa spriten. Det viktiga är _spotlightAngle att automatiskt ha beräknat för att peka på målet.

konstruktor

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

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

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

Det finns inget att tänka på annat än lägre upplösningar för mobil.

Initiera metod

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

Bestämmer den ursprungliga positionen för varje bild. Observera att du Viewport skriver kod efter för att base.Initialize() använda .

LoadContent-metod

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 Jag skapar och laddar texturer.

Dessutom ställs kattbildens mittposition och strålkastarens mittposition (rotationsaxel) här. Eftersom mittpositionen ändras beroende på bilden Det är individuellt.

Uppdateringsmetod

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 hanterar spelarens verksamhet, som kommer att diskuteras senare. Kattens position bestäms av inmatning. Vi använder ocksåViewport för att förhindra att katten visas på skärmen.

TurnToFace Metoden roterar strålkastaren. Strålkastarens position och kattens position, ljusets strömvinkel och den maximala rotationshastigheten bestäms nu för att bestämma ljusets orientering i ramen. Vi pratar om vad vi handlar om senare.

Förresten, jag använder det inte i detta tips, men du måste använda gameTime-variabeln för att matcha spelhastigheten.

HandleInput-metod

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

Spelarens inmatningsprocess. Vad vi gör här är kattflytten och slutet av speloperationen .

Inmatningsenheter stöds inom en mängd olika områden: Tangentbord, Gamepad, Mus, Touch .

I grund och botten rör jag mig bara med en riktningsnyckel, rör vid den eller pekar katten på den position jag klickade på, så jag kommer inte att gå in för mycket i detalj. Som en detaljerad kontrollpunkt,

  • Pinnen är omvänd eftersom uppåtriktad riktning är positiv, medan skärmpositionen är positiv i nedåtriktad riktning.
  • Rör dig inte bortom punkter efter att katten når önskad position med musen eller beröring
  • När du har bestämt färdriktningen normaliserar du den så att den blir en enhetsvektor (endast riktning) och multiplicerar slutligen rörelsehastigheten.

Det blir så.

TurnToFace-metod

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

Detta är huvudprocessen för Tips. Processen att bestämma vinkeln så att strålkastaren vetter mot katten. Passera spotlightposition, kattposition, strömvinkel, maximal rotationshastighet som argument. Returvärdet är den slutliga rotationspositionen. Det är inte mängden rotation från den aktuella positionen.

// この図を参照してください。
// 
//      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ämnts i kommentarerna används trigonometriska (omvända trigonometriska funktioner) för att beräkna vinklar från positioner. Om du förklarar innehållet i matematik kommer det att vara ett tips, så du bör veta att du bör använda bågtangent här. Den metod som används är Math.Atan2 . Math.Atan observera att det inte är det.

Se följande siffra för returvärdet. Returnerar värdet -π till +π med riktningen +x som 0. Observera att +y-riktningen returnerar ett positivt resultat, men i fönstret samordnar botten är +y-riktningen.

// これで猫を向くために必要な設定角度がわかりました。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 för att peka målet bestäms resten vinkeln från den aktuella positionen till önskad vinkel. Här har vi den maximala hastigheten som kan roteras i en ram, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) överskrider inte det maximala värdet.

Om den beräknas helt enkelt med plus eller minus roterar den också bakåt utanför intervallet +π och -π, så jag justerar den med WrapAngle-metoden. Den här metoden beskrivs i nästa avsnitt.

Slutligen, när vinkeln som den roteras bestäms till, återvänd.

WrapAngle-metoden

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

Om vinkeln överskrider intervallet -π till +π, kan den rotera i motsatt riktning mot den riktning som ska roteras. Om detta intervall överskrids lägger du till eller subtraherar 2π för att hålla det inom ovanstående intervall.

Metoden Rita

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 beräknat position och rotation behöver du bara rita spriten längs det numret. För att uttrycka katten som om ljuset slås utförs additiv syntes när man ritar strålkastaren.

Sammanfattning

Jag tror att det är väldigt viktigt att veta om den här tekniken eftersom det finns relativt många situationer i spelet där du bestämmer vilken väg din motståndare är på. Denna metod används ofta i 2D-spel och kan beräknas med hjälp av kvartenor i 3D-spel. Men det finns många saker som använder 2D-beräkningar i 3D, så det är säkert att veta.