1

XNAでRPGゲームを作ろうとしていますが、動きに問題があります。RPGツクールのようにタイルからタイルへとスムーズに移動し、歩くのを止めるとタイルの真ん中にスナップするような動きを作りたいと思っています。どうすればこれを行うことができますか?私はこれを持っていますが、間に何もない正方形から正方形へと即座に移動します。

            if (aCurrentKeyboardState.IsKeyDown(Keys.Right) == true && mPreviousKeyboardState != aCurrentKeyboardState)
            {
                Position.X += 30;
            }
            else if (aCurrentKeyboardState.IsKeyDown(Keys.Left) == true && mPreviousKeyboardState != aCurrentKeyboardState)
            {
                Position.X -= 30;
            }
            else if (aCurrentKeyboardState.IsKeyDown(Keys.Up) == true && mPreviousKeyboardState != aCurrentKeyboardState)
            {
                Position.Y -= 30;
            }
            else if (aCurrentKeyboardState.IsKeyDown(Keys.Down) == true && mPreviousKeyboardState != aCurrentKeyboardState)
            {
                Position.Y += 30;
            }
4

3 に答える 3

10

今よりもかなり多くのものが必要です。

まず、すでにお気づきのように、位置を直接設定するのは簡単です。ソリューション?

速度

を直接設定する代わりに、次のようPositionに設定しますvelocity

Vector2 velocity = Vector2.Zero;

また、何らかの移動速度を定義します。

const float speed = 10.0f;

キープレスで代わりに変更velocityします。

if (aCurrentKeyboardState.IsKeyDown(Keys.Right) == true && mPreviousKeyboardState != aCurrentKeyboardState)
{
    velocity.X = new Vector2(speed, 0.0f);
}
else if (aCurrentKeyboardState.IsKeyDown(Keys.Left) == true && mPreviousKeyboardState != aCurrentKeyboardState)
{
    velocity.X = new Vector2(-speed, 0.0f);
}
else if (aCurrentKeyboardState.IsKeyDown(Keys.Up) == true && mPreviousKeyboardState != aCurrentKeyboardState)
{
    velocity.Y = new Vector2(0.0f, -speed);
}
else if (aCurrentKeyboardState.IsKeyDown(Keys.Down) == true && mPreviousKeyboardState != aCurrentKeyboardState)
{
    velocity.Y = new Vector2(0.0f, speed);
}

次に、すべてのフレームを更新Positionして、速度がアクティブに機能するようにします。

//assuming you're using GameTime gameTime for the timing values
Position += velocity * (float)gameTime.ElapsedGameTime.TotalSeconds;

(固定のゲーム時間を使用している場合、実際には経過時間を乗算する必要はありませんが、1 秒あたりの単位で測定することを好むので、通常はそこに入れます。)

タイルモーション

これは、「滑らかな」モーションとはまったく別の問題です。基本的な速度が得られたので、タイルを 1 つ動かしたら停止するだけです。これを解決するには多くの方法がありますが、ここでは簡単な方法をスケッチします。

方向/軸とは関係なく、キャラクターがモーションでどれだけ遠くまで移動したかを表す「距離」変数を保持します。

float distance = 0.0f;

変更に合わせてインクリメントしPosition、次の直後に「十分に移動した」テストを実行します。

//assuming you're using GameTime gameTime for the timing values
Position += velocity * (float)gameTime.ElapsedGameTime.TotalSeconds;

//increment total distance indepedent of direction/axis
distance += Math.Abs(velocity.X) + Math.Abs(velocity.Y);

//test if we've travelled far enough
if (distance >= tileSize)
{
    //reset distance
    distance = 0.0f;

    //stop
    velocity = Vector2.Zero;

    //TODO: SNAP TO TILE
}

これは良い出発点ですが、さらに 2 つのことを行う必要があります。

  1. 移動が完了したら、最も近いタイル (移動したばかりのタイル) にスナップします。そうしないと、キャラクターがタイル グリッドから非常にゆっくりと「外れ」始めます。これを行うには、キャラクターの中心座標(座標 + タイル サイズ / 2) を取得してタイル座標に変換し、それを「実際の」座標フロートに戻すことをお勧めします。これには他にも多くの方法があります。

  2. 動作中はキープレスが無効になっていることを確認してください。そうしないと、進行中のモーションを中断することで、キャラクターがタイル グリッドから自由に移動できます。ブール値を保持するinMotionか、より簡潔に言えば、そうvelocityでないかどうかをテストVector2.Zeroできます (そうすれば、動いていることがわかります)。

途中でモーションの中断を許可し、グリッドとの同期を維持したい場合は、もう少し高度になりますが、これで良いスタートが切れるはずです。

于 2013-01-02T10:05:01.473 に答える
2

妥当性に関する質問はさておき:

キーを押すたびに、位置に単位を追加しています。プレスで移動する適切な量は、メイン ループの速度によって異なります。ここでは、ゲームの更新ごとに 30 ユニットを移動していますが、これは多い場合も少ない場合もあります (詳細なコンテキストがないとわかりにくい)。

議論のために、タイルの幅が 100 単位であるとしましょう。移動速度は 1 ティックあたり 30 単位に維持します (完全に構成された数字 - 説明のため)。したがって、キーホールドは次のようになります。

turn  |0| 1| 2| 3|  4|  5|  6|  7|
units |0|30|60|90|120|150|180|210|
square|0| 0| 1| 1|  1|  2|  2|  2|

キーを押している間は、文字を絶対位置 (60、90 など) に描画します。キーが表示されると、次のようになります (明確にするために長い形式で):

float total_squares_moved = position / square_size;
int rounded_up = ((int) (total_square_size + .5f));
position = (rounded_up + .5f) * square_size;

これは、最後にいたタイルにスナップします。「rounded_up」行の .5f をより低い数値 (前回に向かって) またはより高い数値 (次回に向かって) に変更することで、前方または後方にバイアスをかけることができます。

于 2013-01-02T04:08:05.977 に答える
0

編集: Scott ソリューションは非常に似ていますが、個人的には、フレームごとに位置をインクリメントするよりも、ソースと宛先の間をアニメーション化する方が好きです。だからここに私のバージョンがあります:

キャラクターがタイル上 (中央) にあり、動いていない場合にのみ、キーをチェックする必要があります。次に、キーが検出されると、タイル 1 からタイル 2 への移動アニメーションを開始し、アニメーションが完了するのを待って、再び入力を受け入れます。

プライベート変数:

float m_animPercent = 1;
float m_animSpeed = 1.0f / .5f; // half a second to perform the movement. might want to try faster, like .25f (250 ms)
Vector2 m_from;
Vector2 m_to;

アップデート:

if (m_animPercent == 1)
{
    // Can check for inputs
    if (aCurrentKeyboardState.IsKeyDown(Keys.Right)) // Check for pressed only, so the player can hold the key down.
    {
        m_from = Position;
        m_to = Position + new Vector2(30, 0); // Set the destination tile position
        m_animPercent = 0;
    }
    [... do other movement checks... ]
}
else
{
    // Animate to the new position
    m_animPercent += m_animSpeed * ElapsedTime;
    if (m_animPercent >= 1) m_animPercent = 1;
    Position = m_from + (m_to - m_from) * m_animPercent;
}
于 2013-01-03T16:46:10.083 に答える