מכוון

עודכן דף :
תאריך יצירת דף :

הערות על עצות אלה

דוגמה זו מבוססת על התוכניות המתפרסמות באתרים הבאים. אני משנה את הקוד קצת כדי להקל על ההבנה ולהסביר את זה ביפנית. בעיקרון, אנו משתמשים בקוד המקורי כפי שהוא, כך שאם אתה באמת לאמץ אותו בתוכנית המשחק שלך, לתקן אותו בזמן ולהשתמש בו.

אתר הפניה

בנוסף, הוא מוסבר על ההנחה כי יש לך קצת ידע בסיסי על MonoGame ו XNA. ראה טיפים MonoGame וטיפים XNA עבור ruddly.

בפרט, כמו מתמטיקה, וקטורים, פונקציות טריגונומטריות, מטריצות, וכו 'הם חיוניים, אז בבקשה לדעת מה הם במידה מסוימת.

סביבה

פלטפורמה
  • Windows 10
  • ניתן להשתמש בקוד בפלטפורמות אחרות התומכות ב- MonoGame
Visual Studio
  • Visual Studio 2019
.NET Core
  • 3.1
מונוג-משחק
  • 3.8

אודות דוגמאות

האור מסתובב כדי לעקוב אחר האור בכיוון היעד.

אם תזיזו את החתול עם עכבר או מפתח,

האור עוקב אחר החתול.

כיצד לפעול

מה ניתן לעשות לוח המשחקים של לוח המשחקים של המקלדת (XInput) מגע עכבר
תנועת חתולים ↑↓←→
  • מקל שמאלי
  • DPad
לחצן שמאלי גע בכל מקום
סוף המשחק Esc גב - -

מה להכין

תמונות שמזיזות את המטרה ואת האורות למעקב.

תוכנית

הורד את התוכנית עבור כל הקוד.

קבוע

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

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

תנועת חתולים ומעקב אחר אור אינם מיידיים, אלא הזזה וסיבוב של מסגרת אחר מסגרת.

אגב, מאז זמן המשחק אינו משמש במדגם זה, פעולת הזמן בפועל ומהירות עשוי להיות שונה בהתאם לפלטפורמה. נסה לנצל את זמן המשחק במשחק שאתה עושה בפועל.

שדה

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

בעיקרון, יש לך רק את המידע כדי להציג את הספרייט. הדבר החשוב הוא _spotlightAngle לחשב באופן אוטומטי כדי להצביע על היעד.

בנאי

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

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

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

אין מה לזכור מלבד רזולוציות נמוכות יותר עבור ניידים.

אתחל פעולת שירות

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

קובע את המיקום ההתחלתי של כל תמונה. שים Viewport לב שאתה כותב קוד לאחר base.Initialize() השימוש ב- .

שיטת 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 אני יוצר וטוען מרקמים.

בנוסף, המיקום המרכזי של תמונת החתול והמיקום המרכזי (ציר הסיבוב) של אור הזרקורים מוגדרים כאן. מכיוון שהמיקום המרכזי משתנה בהתאם לתמונה הוא מוגדר בנפרד.

שיטת עדכון

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 מטפלת בפעולות השחקן, אשר יידונו מאוחר יותר. מיקום החתול נקבע על ידי קלט. אנו משתמשים גםViewport כדי למנוע מהחתול להופיע על המסך.

TurnToFace השיטה מסובבת את אור הזרקורים. מיקום אור הזרקורים ומיקום החתול, הזווית הנוכחית של האור ומהירות הסיבוב המרבית נקבעים כעת כדי לקבוע את כיוון האור במסגרת. נדבר על מה שאנחנו עומדים מאוחר יותר.

דרך אגב, אני לא משתמש בו בטיפים אלה, אבל אתה צריך להשתמש במשתנה GameTime כדי להתאים למהירות המשחק.

שיטת 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;
}

תהליך הקלט של השחקן. מה שאנחנו עושים כאן הוא מהלך החתול וסוף פעולת המשחק .

התקני קלט נתמכים במגוון תחומים: מקלדת, Gamepad, עכבר, מגע .

בעיקרון, אני פשוט זז עם מפתח כיווני, נוגע בו, או מכוון את החתול לעמדה שלחצתי עליה, אז אני לא אכנס ליותר מדי פרטים. כנקודת בקרה מפורטת,

  • המקל הפוך מכיוון שהכיוון למעלה חיובי, בעוד שמיקום המסך חיובי בכיוון כלפי מטה.
  • אין לנוע מעבר לנקודות לאחר שהחתול מגיע למיקומו הרצוי באמצעות העכבר או המגע
  • לאחר שקבעתם את כיוון הנסיעה, תרגלו אותו כדי להפוך אותו לווקטור יחידה (כיוון בלבד) ולבסוף הכפלו את מהירות התנועה.

זה מגיע ל.

שיטת 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)
{
  // :
  // :
}

זהו התהליך העיקרי עבור טיפים. תהליך קביעת הזווית כך שאור הזרקורים פונה לחתול. מעבר מיקום זרקור, מיקום חתול, זווית נוכחית, מהירות סיבוב מקסימלית כארגומנטים. ערך ההחזרה הוא מיקום הסיבוב הסופי. זו לא כמות הסיבוב מהמיקום הנוכחי.

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

כפי שצוין בהערות, טריגונומטרי (פונקציות טריגונומטריות הפוכות) משמש לחישוב זוויות ממיקומים. אם אתה מסביר את התוכן של המתמטיקה, זה יהיה טיפ, אז אתה צריך לדעת שאתה צריך להשתמש arctangent כאן. השיטה המשמשת היא Math.Atan2 . Math.Atan שים לב שזה לא.

עיין באיור הבא עבור ערך ההחזרה. החזרת ערך של -π ל- +π עם הכיוון של +x כ- 0. שים לב שהכיוון +y מחזיר תוצאה חיובית, אך בקואורדינטות החלון החלק התחתון הוא הכיוון +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);

ברגע שיש לך זווית לכוון את המטרה, השאר נקבע הזווית מהמיקום הנוכחי לזווית הרצויה. כאן יש לנו את המהירות המרבית שניתן לסובב במסגרת אחת, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) אינו חורג מהערך המרבי.

כמו כן, אם מחושב פשוט על ידי פלוס או מינוס, הוא מסתובב אחורה מעבר לטווח של +π ו- -π, אז אני מתאים אותו בשיטת 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;
}

אם הזווית חורגת מהטווח של -π ל- +π, היא עשויה להסתובב בכיוון ההפוך לכיוון שיש לסובב, אם אירעה חריגה מטווח זה, הוסף או הפחת 2π כדי להשאיר אותו בטווח שלעיל.

שיטת ציור

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

לאחר חישוב המיקום והסיבוב, כל שעליך לעשות הוא לצייר את הספרייט לאורך המספר הזה. על מנת לבטא את החתול כאילו האור נפגע, סינתזת תוסף מבוצעת בעת ציור אור הזרקורים.

תקציר

אני חושב שזה מאוד חשוב לדעת על טכניקה זו כי יש יחסית הרבה מצבים במשחק שבו אתה מחליט באיזה כיוון היריב שלך נמצא. שיטה זו משמשת בדרך כלל במשחקים דו-ממדיים, וניתן לחשב אותה באמצעות רבעונים במשחקי תלת-ממד. אבל יש הרבה דברים שמשתמשים בחישובים דו-ממדיים בתלת-ממד, כך שבטוח לדעת.