Mające

Strona zaktualizowana :
Data utworzenia strony :

Uwagi na ten temat Wskazówki

Ten przykład jest oparty na programach opublikowanych w następujących witrynach. Zmieniam trochę kod, aby ułatwić zrozumienie i wyjaśnienie go po japońsku. Zasadniczo używamy oryginalnego kodu takim, jaki jest, więc jeśli faktycznie zaadaptujesz go w swoim programie gry, popraw go w odpowiednim czasie i użyj go.

Strona referencyjna

Ponadto jest to wyjaśnione przy założeniu, że masz podstawową wiedzę na temat MonoGame i XNA. Zobacz MonoGame Tips i XNA Tips dla rumianych.

W szczególności, ponieważ matematyka, wektory, funkcje trygonometryczne, macierze itp. są niezbędne, więc proszę wiedzieć, czym one są do pewnego stopnia.

środowisko

podest
  • System Windows 10
  • Kod może być używany na innych platformach obsługujących MonoGame
Visual Studio
  • Program Visual Studio 2019
Platforma .NET Core
  • 3.1
MonoGame
  • 3.8

Informacje o próbkach

Światło obraca się, aby śledzić światło w kierunku, w którym znajduje się cel.

Jeśli poruszasz kotem za pomocą myszy lub klucza,

Światło podąża za kotem.

Jak obsługiwać

What's Do Keyboard Gamepad (XInput) Mouse Touch
Ruch kota ↑↓←→
  • Lewy drążek
  • DPad
Lewy przycisk Dotknij w dowolnym miejscu
Koniec gry Esc Wstecz - -

Co przygotować

Obrazy, które poruszają cel i światła do śledzenia.

program

Pobierz program dla całego kodu.

stały

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

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

Ruch kota i śledzenie światła nie są natychmiastowe, ale poruszają się i obracają klatka po klatce.

Nawiasem mówiąc, ponieważ czas gry nie jest używany w tym przykładzie, rzeczywista operacja czasu i prędkość mogą się różnić w zależności od platformy. Spróbuj wykorzystać czas gry w rzeczywistej grze, którą tworzysz.

pole

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

Zasadniczo masz tylko informacje, aby wyświetlić duszka. Ważne jest _spotlightAngle , aby automatycznie obliczyć, aby wskazać cel.

konstruktor

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

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

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

Nie ma nic do zapamiętania poza niższymi rozdzielczościami dla urządzeń mobilnych.

Metoda inicjowania

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

Określa początkową pozycję każdego obrazu. Zauważ, że piszesz Viewport kod po użyciu programu base.Initialize() .

Metoda 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 Tworzę i ładuję tekstury.

Ponadto ustawiono tutaj środkową pozycję obrazu kota i środkową pozycję (oś obrotu) reflektora. Ponieważ pozycja środkowa zmienia się w zależności od obrazu Jest on ustawiany indywidualnie.

Metoda aktualizacji

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

Metoda HandleInput obsługuje operacje odtwarzacza, które zostaną omówione później. Pozycja kota jest określana przez dane wejściowe. Używamy równieżViewport , aby zapobiec pojawieniu się kota na ekranie.

TurnToFace Metoda obraca reflektor. Położenie reflektora i położenie kota, aktualny kąt światła i maksymalna prędkość obrotowa są teraz określane w celu określenia orientacji światła w ramie. Porozmawiamy o tym, o czym mówimy później.

Nawiasem mówiąc, nie używam go w tych wskazówkach, ale musisz użyć zmiennej gameTime, aby dopasować prędkość gry.

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

Proces wprowadzania danych przez gracza. To, co tutaj robimy , to ruch kota i zakończenie operacji gry .

Urządzenia wejściowe są obsługiwane w różnych dziedzinach: klawiatura, gamepad, mysz, dotyk .

Zasadniczo po prostu poruszam się za pomocą kierunkowego, dotykam go lub kieruję kota w pozycję, którą kliknąłem, więc nie będę wchodził w zbyt wiele szczegółów. Jako szczegółowy punkt kontrolny,

  • Drążek jest odwrócony, ponieważ kierunek w górę jest dodatni, podczas gdy pozycja ekranu jest dodatnia w kierunku w dół.
  • Nie przesuwaj się poza punkty po tym, jak kot osiągnie żądaną pozycję za pomocą myszy lub dotyku
  • Po określeniu kierunku jazdy znormalizuj go, aby stał się wektorem jednostkowym (tylko kierunek) i ostatecznie pomnóż prędkość ruchu.

Chodzi o to.

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

Jest to główny proces dla wskazówek. Proces określania kąta tak, aby reflektor był skierowany w stronę kota. Podaj pozycję reflektora, pozycję kota, aktualny kąt, maksymalną prędkość obrotową jako argumenty. Zwracana wartość jest końcową pozycją obrotu. Nie jest to ilość rotacji z bieżącej pozycji.

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

Jak wspomniano w komentarzach, trygonometryczne (odwrotne funkcje trygonometryczne) służą do obliczania kątów z pozycji. Jeśli wyjaśnisz treść matematyki, będzie to wskazówka, więc powinieneś wiedzieć, że powinieneś użyć tutaj arctangens. Zastosowana metoda to Math.Atan2 . Math.Atan zauważ, że tak nie jest.

Zobacz poniższy rysunek dla zwracanej wartości. Zwraca wartość -π do +π z kierunkiem +x jako 0. Zauważ, że kierunek +y zwraca wynik dodatni, ale we współrzędnych okna na dole jest kierunek +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);

Gdy masz kąt do wskazania celu, reszta jest określana kąt od bieżącej pozycji do żądanego kąta. Tutaj mamy maksymalną prędkość, którą można obracać w jednej ramce, MathHelper.Clamp(difference, -turnSpeed, turnSpeed) nie przekracza wartości maksymalnej.

Ponadto, jeśli zostanie obliczony po prostu przez plus lub minus, obraca się do tyłu poza zakres +π i -π, więc dostosowuję go za pomocą metody WrapAngle. Ta metoda jest opisana w następnej sekcji.

Wreszcie, gdy określi się kąt, pod którym jest obracany, wróć.

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

Jeśli kąt przekracza zakres od -π do +π, może obracać się w kierunku przeciwnym do kierunku, który powinien być obrócony, Jeśli ten zakres zostanie przekroczony, dodaj lub odejmij 2π, aby utrzymać go w powyższym zakresie.

Metoda rysowania

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

Po obliczeniu pozycji i obrotu wszystko, co musisz zrobić, to narysować duszka wzdłuż tej liczby. Aby wyrazić kota tak, jakby światło zostało uderzone, podczas rysowania reflektora wykonuje się syntezę addytywną.

Streszczenie

Myślę, że bardzo ważne jest, aby wiedzieć o tej technice, ponieważ w grze jest stosunkowo wiele sytuacji, w których decydujesz, w którą stronę znajduje się twój przeciwnik. Ta metoda jest powszechnie stosowana w grach 2D i może być obliczana za pomocą czwartorzędów w grach 3D. Ale jest wiele rzeczy, które wykorzystują obliczenia 2D w 3D, więc można to bezpiecznie wiedzieć.