Kuria siekiama

Puslapis atnaujintas :
Puslapio sukūrimo data :

Pastabos apie šiuos patarimus

Šis pavyzdys pagrįstas programomis, paskelbtomis šiose svetainėse. Aš šiek tiek keičiu kodą, kad būtų lengviau jį suprasti ir paaiškinti japonų kalba. Iš esmės mes naudojame originalų kodą tokį, koks yra, taigi, jei jūs iš tikrųjų jį priimate savo žaidimo programoje, ištaisykite jį laiku ir naudokite.

Nuorodų svetainė

Be to, tai paaiškinama darant prielaidą, kad turite tam tikrų pagrindinių žinių apie MonoGame ir XNA. Žiūrėkite "MonoGame Tips" ir "XNA" patarimus , kad galėtumėte rudi.

Visų pirma, kadangi matematika, vektoriai, trigonometrinės funkcijos, matricos ir kt. yra būtini, todėl prašome žinoti, kas jie yra tam tikru mastu.

Aplinkos

platforma
  • "Windows 10"
  • Kodą galima naudoti kitose "MonoGame" palaikančiose platformose
Visual Studio
  • "Visual Studio 2019"
.NET Core
  • 3.1
MonoGame
  • 3.8

Apie mėginius

Šviesa sukasi, kad stebėtų šviesą taikinio kryptimi.

Jei perkeliate katę pele arba raktu,

Šviesa seka paskui katę.

Kaip valdyti

Ką daryti klaviatūros žaidimų pulto (XInput) pelės prisilietimas
Kačių judėjimas ↑↓←→
  • Kairysis lazda
  • DPad
Mygtukas kairėn Palieskite bet kur
Žaidimo pabaiga Esc Nugara - -

Ką paruošti

Vaizdai, kurie perkelia taikinį ir šviesas, kurias reikia sekti.

programa

Atsisiųskite programą visam kodui.

Nuolat

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

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

Kačių judėjimas ir šviesos sekimas nėra momentinis, bet judinkite ir pasukite rėmelį pagal rėmelį.

Beje, kadangi žaidimo laikas šiame pavyzdyje nenaudojamas, faktinis laiko veikimas ir greitis gali skirtis priklausomai nuo platformos. Pabandykite naudoti žaidimo laiką realiame žaidime, kurį darote.

laukas

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š esmės, jūs tiesiog turite informacijos, kad galėtumėte rodyti sprite. Svarbu, _spotlightAngle kad automatiškai apskaičiuotumėte, kad būtų nukreiptas į tikslą.

Konstruktorius

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

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

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

Nėra nieko, ką reikia nepamiršti, išskyrus mažesnes mobiliųjų telefonų skiriamąsias gebas.

Inicijavimo metodas

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

Nustato pradinę kiekvieno vaizdo padėtį. Atkreipkite dėmesį, kad po base.Initialize() naudojimo rašote Viewport kodą .

LoadContent metodas

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 Aš kuriu ir kraunau tekstūras.

Be to, čia nustatoma katės vaizdo centrinė padėtis ir prožektoriaus centrinė padėtis (sukimosi ašis). Kadangi centrinė padėtis keičiasi priklausomai nuo vaizdo Jis nustatomas individualiai.

Atnaujinimo metodas

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 metodas tvarko žaidėjo operacijas, kurios bus aptartos vėliau. Katės padėtį lemia įvestis. Mes taip patViewport naudojame, kad katė nepasirodytų ekrane.

TurnToFace Metodas pasuka prožektorių. Prožektoriaus padėtis ir katės padėtis, dabartinis šviesos kampas ir maksimalus sukimosi greitis dabar yra nustatyti šviesos orientaciją rėmelyje. Apie tai, apie ką kalbėsime vėliau, pakalbėsime vėliau.

Beje, aš jo nenaudoju šiame patarime, bet jums reikia naudoti "gameTime" kintamąjį, kad atitiktų žaidimo greitį.

HandleInput metodas

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

Žaidėjo indėlio procesas. Tai, ką mes čia darome , yra katės judėjimas ir žaidimo operacijos pabaiga .

Įvesties įrenginiai palaikomi įvairiuose laukuose: Klaviatūra, Žaidimų pultas, Pelė, Prisilietimas .

Iš esmės aš tiesiog judu su kryptiniu raktu, paliesdamas jį arba nukreipdamas katę į poziciją, kurią spustelėjau, todėl nesigilinsiu į per daug detalių. Kaip išsamus kontrolės taškas,

  • Lazdelė yra atvirkštinė, nes kryptis aukštyn yra teigiama, o ekrano padėtis yra teigiama žemyn.
  • Neviršykite taškų po to, kai katė pasiekia norimą padėtį pele ar prisilietimu
  • Nustatę kelionės kryptį, normalizuokite ją, kad ji taptų vieneto vektoriumi (tik kryptimi) ir galiausiai padauginkite judėjimo greitį.

Jis ateina.

TurnToFace metodas

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

Tai yra pagrindinis patarimų procesas. Kampo nustatymo procesas taip, kad prožektorius nukreiptų į katę. Praleiskite prožektorių padėtį, katės padėtį, srovės kampą, maksimalų sukimosi greitį kaip argumentus. Grąžinimo vertė yra galutinė sukimosi padėtis. Tai nėra sukimosi kiekis iš dabartinės padėties.

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

Kaip minėta komentaruose, trigonometrinė (atvirkštinės trigonometrinės funkcijos) naudojama kampams apskaičiuoti iš pozicijų. Jei paaiškinsite matematikos turinį, tai bus patarimas, todėl turėtumėte žinoti, kad čia turėtumėte naudoti arktangentą. Naudojamas metodas yra Math.Atan2 . Math.Atan Atkreipkite dėmesį, kad taip nėra.

Žiūrėkite toliau pateiktą grąžinimo vertės skaičių. Grąžina reikšmę -π į +π, kurios kryptis +x yra 0. Atkreipkite dėmesį, kad +y kryptis pateikia teigiamą rezultatą, tačiau lango koordinatėse apačioje yra +y kryptis.

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

Kai turite kampą, nukreiptą į tikslą, likusi dalis nustatoma kampu nuo dabartinės padėties iki norimo kampo. Čia mes turime maksimalų greitį, kurį galima pasukti viename kadre, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) neviršija maksimalios vertės.

Be to, jei apskaičiuojamas tiesiog pliusu arba minusu, jis sukasi atgal už +π ir -π diapazono, todėl jį pritaikau "WrapAngle" metodu. Šis metodas aprašytas kitame skyriuje.

Galiausiai, kai nustatomas kampas, į kurį jis pasukamas, grįžkite.

WrapAngle metodas

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

Jei kampas viršija -π diapazoną iki +π, jis gali suktis priešinga kryptimi į kryptį, kurią reikia pasukti, Jei šis diapazonas viršijamas, pridėkite arba atimkite 2π, kad jis liktų aukščiau esančiame diapazone.

Piešimo metodas

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

Kai apskaičiuosite padėtį ir sukimąsi, viskas, ką jums reikia padaryti, tai nubrėžti sprite išilgai to skaičiaus. Norint išreikšti katę taip, tarsi šviesa būtų pataikyta, atkreipiant prožektorių atliekama priedų sintezė.

Suvestinė

Manau, kad labai svarbu žinoti apie šią techniką, nes žaidime yra gana daug situacijų, kuriose jūs nusprendžiate, kokiu būdu yra jūsų priešininkas. Šis metodas dažniausiai naudojamas 2D žaidimuose ir gali būti apskaičiuojamas naudojant 3D žaidimų kvartionus. Tačiau yra daug dalykų, kurie naudoja 2D skaičiavimus 3D, todėl tai saugu žinoti.