Zielend
Hinweise zu diesen Tipps
Dieses Beispiel basiert auf den Programmen, die auf den folgenden Websites veröffentlicht wurden. Ich ändere den Code ein wenig, um es einfacher zu machen, ihn auf Japanisch zu verstehen und zu erklären. Grundsätzlich verwenden wir den ursprünglichen Code so, wie er ist, wenn Sie ihn also tatsächlich in Ihr Spielprogramm übernehmen, korrigieren Sie ihn rechtzeitig und verwenden Sie ihn.
- Referenzseite
Darüber hinaus wird es unter der Annahme erklärt, dass Sie einige Grundkenntnisse über MonoGame und XNA haben. Siehe MonoGame Tipps und XNA Tipps für das Ruder.
Insbesondere, da Mathematik, Vektoren, trigonometrische Funktionen, Matrizen usw. unerlässlich sind, wissen Sie bitte, was sie bis zu einem gewissen Grad sind.
Umwelt
- Bahnsteig
-
- Windows 10
- Code kann auf anderen MonoGame-fähigen Plattformen verwendet werden
- Visual Studio
-
- Visual Studio 2019
- .NET Core
-
- 3.1
- MonoGame
-
- 3.8
Über Beispiele
Das Licht dreht sich, um das Licht in der Richtung zu verfolgen, in der sich das Ziel befindet.
Wenn Sie die Katze mit einer Maus oder einer Taste bewegen,
Das Licht folgt der Katze.
Bedienung
Was ist zu tun Keyboard | Gamepad (XInput) | Mouse | Touch | |
---|---|---|---|---|
Katzenbewegung | ↑↓←→ |
|
Linke Schaltfläche | Berühren Sie überall |
Ende des Spiels | Esc | Zurück | - | - |
Was ist vorzubereiten?
Bilder, die das Ziel bewegen und die Lichter verfolgen.
Programm
Laden Sie das Programm für den gesamten Code herunter.
Konstante
<summary>猫が動くスピード。これはフレームあたりのピクセル数です。</summary>
const float CatSpeed = 10.0f;
<summary>スポットライトが回転する速度。これはフレームあたりのラジアンで表されます。</summary>
const float SpotlightTurnSpeed = 0.025f;
Katzenbewegung und Lichtverfolgung sind nicht augenblicklich, sondern bewegen und drehen sich Bild für Bild.
Übrigens, da in diesem Beispiel keine Spielzeit verwendet wird, kann der tatsächliche Zeitbetrieb und die Geschwindigkeit je nach Plattform unterschiedlich sein. Versuchen Sie, die Spielzeit in dem tatsächlichen Spiel zu verwenden, das Sie machen.
Feld
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();
Grundsätzlich haben Sie nur die Informationen, um das Sprite anzuzeigen.
Das Wichtigste ist _spotlightAngle
, dass automatisch berechnet wird, um auf das Ziel zu zeigen.
Konstruktor
public AimingGame()
{
graphics = new GraphicsDeviceManager(this);
ontent.RootDirectory = "Content";
sMouseVisible = true;
graphics.PreferredBackBufferWidth = 320;
graphics.PreferredBackBufferHeight = 480;
// フルスクリーンにしたい場合はコメントを外してください。
//graphics.IsFullScreen = true;
}
Es gibt nichts zu beachten, außer niedrigeren Auflösungen für Mobilgeräte.
Initialize-Methode
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;
}
Bestimmt die Anfangsposition jedes Bildes.
Beachten Sie, dass Sie Viewport
Code schreiben, nachdem base.Initialize()
Sie .
LoadContent-Methode
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
Ich erstelle und lade Texturen.
Zusätzlich werden hier die Mittelpunktsposition des Katzenbildes und die Mittelposition (Drehachse) des Scheinwerfers eingestellt. Weil sich die Center-Position je nach Bild ändert Es wird individuell eingestellt.
Update-Methode
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);
}
Die HandleInput-Methode behandelt die Vorgänge des Spielers, die später erläutert werden. Die Position der Katze wird durch Eingabe bestimmt.
Wir verwenden auchViewport
, um zu verhindern, dass die Katze auf dem Bildschirm erscheint.
TurnToFace
Die Methode dreht den Scheinwerfer. Die Position des Scheinwerfers und die Position der Katze, der aktuelle Winkel des Lichts und die maximale Drehzahl werden nun bestimmt, um die Ausrichtung des Lichts im Rahmen zu bestimmen.
Wir werden später darüber sprechen, worum es uns geht.
Übrigens verwende ich es nicht in diesen Tipps, aber Sie müssen die gameTime-Variable verwenden, um die Spielgeschwindigkeit anzupassen.
HandleInput-Methode
<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;
}
Der Eingabeprozess des Spielers. Was wir hier tun, ist der Katzenzug und das Ende des Spiels .
Eingabegeräte werden in einer Vielzahl von Bereichen unterstützt: Tastatur, Gamepad, Maus, Touch .
Im Grunde bewege ich mich nur mit einer Richtungstaste, berühre sie oder zeige die Katze auf die Position, auf die ich geklickt habe, damit ich nicht zu sehr ins Detail gehe. Als detaillierter Kontrollpunkt,
- Der Stick ist umgekehrt, da die Aufwärtsrichtung positiv ist, während die Bildschirmposition in abwärts gerichteter Richtung positiv ist.
- Bewegen Sie sich nicht über Punkte hinaus, nachdem die Katze ihre gewünschte Position mit der Maus erreicht oder berührt hat
- Sobald Sie die Fahrtrichtung bestimmt haben, normalisieren Sie sie zu einem Einheitsvektor (nur Richtung) und multiplizieren Sie schließlich die Bewegungsgeschwindigkeit.
Es kommt zu.
TurnToFace-Methode
<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)
{
// :
// :
}
Dies ist der Hauptprozess für Tipps. Der Prozess der Bestimmung des Winkels, so dass der Scheinwerfer der Katze zugewandt ist. Übergeben Sie die Scheinwerferposition, die Katzenposition, den aktuellen Winkel und die maximale Drehzahl als Argumente. Der Rückgabewert ist die endgültige Rotationsposition. Es ist nicht die Menge der Rotation von der aktuellen Position.
// この図を参照してください。
//
// 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);
Wie in den Kommentaren erwähnt, wird trigonometrisch (inverse trigonometrische Funktionen) verwendet, um Winkel von Positionen zu berechnen.
Wenn Sie die Inhalte der Mathematik erklären, wird es ein Tipp sein, also sollten Sie wissen, dass Sie hier arctangent verwenden sollten.
Die verwendete Methode ist Math.Atan2
. Math.Atan
Beachten Sie, dass dies nicht der Fall ist.
Den Rückgabewert finden Sie in der folgenden Abbildung. Gibt den Wert -π an +π mit der Richtung +x als 0 zurück. Beachten Sie, dass die +y-Richtung ein positives Ergebnis zurückgibt, aber bei Fensterkoordinaten der boden die +y-Richtung ist.
// これで猫を向くために必要な設定角度がわかりました。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);
Sobald Sie einen Winkel haben, um das Ziel zu richten, wird der Rest der Winkel von der aktuellen Position zum gewünschten Winkel bestimmt.
Hier haben wir die maximale Geschwindigkeit, die in einem Rahmen gedreht werden kann,
MathHelper.Clamp(difference, -turnSpeed, turnSpeed)
überschreitet den Maximalwert nicht.
Wenn es einfach durch Plus oder Minus berechnet wird, dreht es sich rückwärts über den Bereich von +π und -π hinaus, also passe ich es mit der WrapAngle-Methode an. Diese Methode wird im nächsten Abschnitt beschrieben.
Schließlich, wenn der Winkel bestimmt ist, in den es gedreht wird, kehren Sie zurück.
WrapAngle-Methode
<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;
}
Überschreitet der Winkel den Bereich von -π bis +π, kann er sich in die entgegengesetzte Richtung zu der Richtung drehen, die gedreht werden soll. Wenn dieser Bereich überschritten wird, addieren oder subtrahieren Sie 2π, um ihn innerhalb des obigen Bereichs zu halten.
Draw-Methode
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);
}
Sobald Sie die Position und Rotation berechnet haben, müssen Sie nur noch das Sprite entlang dieser Zahl zeichnen. Um die Katze so auszudrücken, als ob das Licht getroffen würde, wird beim Zeichnen des Scheinwerfers eine additive Synthese durchgeführt.
Zusammenfassung
Ich denke, es ist sehr wichtig, über diese Technik Bescheid zu wissen, denn es gibt relativ viele Situationen im Spiel, in denen man entscheidet, in welcher Richtung sich der Gegner befindet. Diese Methode wird häufig in 2D-Spielen verwendet und kann mit Hilfe von Quaternionen in 3D-Spielen berechnet werden. Aber es gibt viele Dinge, die 2D-Berechnungen in 3D verwenden, also ist es sicher zu wissen.