목표로
이 팁에 대한 참고 사항
이 샘플은 다음 사이트에 게시된 프로그램을 기반으로 합니다. 일본어로 쉽게 이해하고 설명할 수 있도록 코드를 조금 바꿉니다. 기본적으로 원래 코드를 있는 것처럼 사용하므로 실제로 게임 프로그램에서 채택하는 경우 적시에 수정하고 사용합니다.
- 참조 사이트
또한 모노게임과 XNA에 대한 기본적인 지식이 있다는 가정하에 설명되어 있습니다. 모노게임 팁 과 XNA 팁을 참조하십시오.
특히 수학, 벡터, 삼각함수, 행렬 등이 필수적이므로 어느 정도 는 무엇인지 알려주십시오.
환경
- 플랫폼
-
- 윈도우 10
- 코드는 다른 모노게임 지원 플랫폼에서 사용할 수 있습니다.
- 비주얼 스튜디오
-
- 비주얼 스튜디오 2019
- .NET 코어
-
- 3.1
- 모노게임
-
- 3.8
샘플 에 대해
빛이 회전하여 대상 방향으로 빛을 추적합니다.
마우스 나 키로 고양이를 이동하는 경우,
빛은 고양이를 따릅니다.
작동 방법
키보드 | 게임 패드 (XInput) | 마우스 | 터치 | 를 할 수있는 일 |
---|---|---|---|---|
고양이 운동 | ↑↓←→ |
|
왼쪽 버튼 | 어디서나 터치 |
게임의 끝 | 에크 (주) | 뒤로 | - | - |
준비해야 할 것
대상을 이동하는 이미지와 라이트를 추적합니다.
프로그램
모든 코드에 대한 프로그램을 다운로드합니다.
상수
<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()
.
로드콘텐츠 방법
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
메서드는 스포트라이트를 회전합니다. 이제 스포트라이트의 위치와 고양이의 위치, 빛의 현재 각도 및 최대 회전 속도는 이제 프레임의 빛의 방향을 결정하기 위해 결정됩니다.
우리는 나중에 우리가 무엇에 대해 얘기할 것입니다.
그건 그렇고, 나는이 팁에서 사용하지 않는, 하지만 당신은 게임 속도에 맞게 게임 시간 변수를 사용해야합니다.
핸들입력 방법
<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;
}
플레이어의 입력 프로세스입니다. 우리가 여기서 하고 있는 일은 고양이의 움직임 과 게임 작업의 끝입니다 .
입력 장치는 키보드, 게임 패드, 마우스, 터치 : 입력 장치의 다양한 분야에서 지원됩니다.
기본적으로, 난 그냥 방향 키로 이동, 그것을 만지거나 내가 클릭 한 위치에 고양이를 가리키는, 그래서 나는 너무 많은 세부 사항에 가지 않을 것입니다. 상세한 제어지점으로서,
- 화면 위치가 아래쪽 방향으로 긍정적이면 업 방향이 긍정적이기 때문에 스틱이 반전됩니다.
- 고양이가 마우스 또는 터치로 원하는 위치에 도달 한 후 포인트를 넘어 이동하지 마십시오.
- 이동 방향을 결정하면 이를 정규화하여 단위 벡터(방향만)로 만들고 마지막으로 이동 속도를 곱합니다.
그것은 온다.
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);
}
위치와 회전을 계산한 후에는 해당 숫자를 따라 스프라이트를 그리는 것만 있으면 됩니다. 빛을 치는 것처럼 고양이를 표현하기 위해 스포트라이트를 그릴 때 첨가제 합성이 수행됩니다.
요약
상대가 어느 방향으로 왔는지 결정하는 게임에 상대적으로 많은 상황이 있기 때문에 이 기술에 대해 아는 것이 매우 중요하다고 생각합니다. 이 방법은 일반적으로 2D 게임에서 사용되며 3D 게임에서 쿼터니온을 사용하여 계산할 수 있습니다. 그러나 3D에서 2D 계산을 사용하는 많은 것들이 있으므로 알 수 있습니다.