Nhằm

Trang Cập Nhật :
Ngày tạo trang :

Ghi chú về Mẹo này

Mẫu này dựa trên các chương trình được công bố trên các trang web sau. Tôi thay đổi mã một chút để dễ hiểu và giải thích nó bằng tiếng Nhật. Về cơ bản, chúng tôi sử dụng mã gốc như hiện tại, vì vậy nếu bạn thực sự áp dụng nó trong chương trình trò chơi của mình, hãy sửa nó kịp thời và sử dụng nó.

Trang tham chiếu

Ngoài ra, nó được giải thích trên giả định rằng bạn có một số kiến thức cơ bản về MonoGame và XNA. Xem Mẹo MonoGameMẹo XNA cho bánh lái.

Đặc biệt, vì toán học, vectơ, hàm lượng giác, ma trận, v.v. là điều cần thiết, vì vậy hãy biết chúng là gì ở một mức độ nào đó.

môi trường

nền tảng
  • Windows 10
  • Mã có thể được sử dụng trên các nền tảng hỗ trợ MonoGame khác
Visual Studio
  • Visual Studio 2019
.NET Core
  • 3.1
MonoGame
  • 3.8

Giới thiệu về mẫu

Ánh sáng xoay để theo dõi ánh sáng theo hướng mục tiêu.

Nếu bạn di chuyển con mèo bằng chuột hoặc chìa khóa,

Ánh sáng đi theo con mèo.

Cách vận hành

Làm gì để làm Bàn phím Gamepad (XInput) Mouse Touch
Chuyển động của mèo ↑↓←→
  • Thanh Trái
  • DPad
Nút Trái Chạm vào bất cứ đâu
Kết thúc trò chơi Esc Lưng - -

Những gì cần chuẩn bị

Hình ảnh di chuyển mục tiêu và đèn để theo dõi.

chương trình

Tải xuống chương trình cho tất cả các mã.

hằng

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

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

Chuyển động của mèo và theo dõi ánh sáng không phải là tức thời, nhưng di chuyển và xoay khung hình theo khung hình.

Nhân tiện, vì thời gian trò chơi không được sử dụng trong mẫu này, hoạt động và tốc độ thời gian thực tế có thể khác nhau tùy thuộc vào nền tảng. Cố gắng sử dụng thời gian trò chơi trong trò chơi thực tế mà bạn thực hiện.

trường

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

Về cơ bản, bạn chỉ cần có thông tin để hiển thị sprite. Điều quan trọng là _spotlightAngle phải tự động tính toán để chỉ vào mục tiêu.

Constructor

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

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

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

Không có gì để ghi nhớ ngoài độ phân giải thấp hơn cho điện thoại di động.

Khởi tạo phương pháp

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

Xác định vị trí ban đầu của mỗi hình ảnh. Lưu ý rằng bạn đang Viewport viết mã sau khi base.Initialize() sử dụng .

Phương pháp 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 Tôi đang tạo và tải kết cấu.

Ngoài ra, vị trí trung tâm của hình ảnh mèo và vị trí trung tâm (trục quay) của đèn chiếu sáng được đặt ở đây. Bởi vì vị trí trung tâm thay đổi tùy thuộc vào hình ảnh Nó được thiết lập riêng lẻ.

Phương pháp cập nhật

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

Phương pháp HandleInput xử lý các hoạt động của người chơi, sẽ được thảo luận sau. Vị trí của con mèo được xác định bởi đầu vào. Chúng tôi cũngViewport sử dụng để ngăn chặn con mèo xuất hiện trên màn hình.

TurnToFace Phương pháp xoay ánh đèn sân khấu. Vị trí của đèn chiếu sáng và vị trí của con mèo, góc hiện tại của ánh sáng và tốc độ quay tối đa hiện được xác định để xác định hướng của ánh sáng trong khung hình. Chúng ta sẽ nói về những gì chúng ta sẽ nói về sau.

Nhân tiện, tôi không sử dụng nó trong Mẹo này, nhưng bạn cần sử dụng biến GameTime để phù hợp với tốc độ trò chơi.

Phương pháp 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;
}

Quy trình nhập liệu của người chơi. Những gì chúng tôi đang làm ở đây là di chuyển mèokết thúc hoạt động trò chơi .

Các thiết bị đầu vào được hỗ trợ trong nhiều lĩnh vực khác nhau: Bàn phím, Gamepad, Chuột, Cảm ứng .

Về cơ bản, tôi chỉ di chuyển bằng chìa khóa định hướng, chạm vào nó hoặc chỉ con mèo vào vị trí tôi nhấp vào, vì vậy tôi sẽ không đi sâu vào chi tiết. Như một điểm kiểm soát chi tiết,

  • Thanh được đảo ngược vì hướng lên là tích cực, trong khi vị trí màn hình là tích cực theo hướng đi xuống.
  • Không di chuyển vượt quá các điểm sau khi con mèo đạt đến vị trí mong muốn của nó với chuột hoặc chạm vào
  • Một khi bạn đã xác định được hướng di chuyển, hãy bình thường hóa nó để biến nó thành một vector đơn vị (chỉ hướng) và cuối cùng nhân tốc độ di chuyển.

Nó đến rồi.

Phương pháp 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)
{
  // :
  // :
}

Đây là quá trình chính cho các mẹo. Quá trình xác định góc để ánh đèn sân khấu đối mặt với con mèo. Vượt qua vị trí đèn chiếu sáng, vị trí mèo, góc hiện tại, tốc độ quay tối đa như các đối số. Giá trị trả về là vị trí quay cuối cùng. Nó không phải là số lượng luân chuyển từ vị trí hiện tại.

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

Như đã đề cập trong các nhận xét, lượng giác (hàm lượng giác nghịch đảo) được sử dụng để tính toán các góc từ các vị trí. Nếu bạn giải thích nội dung của toán học, nó sẽ là một mẹo, vì vậy bạn nên biết rằng bạn nên sử dụng arctangent ở đây. Phương pháp được sử dụng là Math.Atan2 . Math.Atan Lưu ý rằng nó không phải.

Xem hình sau đây cho giá trị trả về. Trả về giá trị -π thành +π với hướng +x là 0. Lưu ý rằng hướng +y trả về kết quả dương tính, nhưng ở cửa sổ tọa độ phía dưới là hướng +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);

Một khi bạn có một góc để chỉ mục tiêu, phần còn lại được xác định góc từ vị trí hiện tại đến góc mong muốn. Ở đây chúng tôi có tốc độ tối đa có thể được xoay trong một khung hình, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) không vượt quá giá trị tối đa.

Ngoài ra, nếu tính đơn giản bằng cộng hoặc trừ, nó xoay ngược vượt quá phạm vi +π và -π, vì vậy tôi điều chỉnh nó bằng phương pháp WrapAngle. Phương pháp này được mô tả trong phần tiếp theo.

Cuối cùng, khi góc mà nó được xoay được xác định, trở lại.

Phương pháp 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;
}

Nếu góc vượt quá phạm vi -π đến +π, nó có thể xoay theo hướng ngược lại với hướng cần được xoay, Nếu phạm vi này vượt quá, hãy cộng hoặc trừ 2π để giữ nó trong phạm vi trên.

Phương pháp vẽ

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

Một khi bạn đã tính toán vị trí và xoay, tất cả những gì bạn phải làm là vẽ sprite dọc theo số đó. Để thể hiện con mèo như thể ánh sáng bị đánh, tổng hợp phụ gia được thực hiện khi vẽ ánh đèn sân khấu.

Tóm tắt

Tôi nghĩ rằng điều rất quan trọng là phải biết về kỹ thuật này bởi vì có tương đối nhiều tình huống trong trò chơi mà bạn quyết định đối thủ của bạn đang ở theo cách nào. Phương pháp này thường được sử dụng trong các trò chơi 2D và có thể được tính toán bằng cách sử dụng tứ giác trong các trò chơi 3D. Nhưng có rất nhiều thứ sử dụng tính toán 2D trong 3D, vì vậy nó an toàn để biết.