1

我正在尝试在 XNA 中制作 RPG 游戏,但我遇到了移动问题。我想创建类似于 RPG maker 中的运动,您可以在其中顺利地从一个瓷砖走到另一个瓷砖,并在您停止行走时捕捉到一个瓷砖的中间。我该怎么做呢?我有这个,但它会立即从一个正方形移动到另一个正方形,中间没有任何东西。

            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;

(如果你使用固定的游戏时间,你并不需要乘以经过的时间,但我通常把它放在那里,因为我更喜欢以每秒为单位进行测量。)

瓷砖运动

这是与“平滑”运动完全不同的问题。现在你已经有了基本的速度,你现在需要的就是在移动一个完整的瓷砖后停止。有很多方法可以解决这个问题,但我将在这里勾勒出一个简单的方法。

保留一个“距离”变量来表示角色在他的运动中走了多远,与方向/轴无关:

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
}

这将是一个好的开始,但您仍然需要做两件事:

  1. 完成移动后捕捉到最近的图块(您刚刚前往的图块)。如果你不这样做,角色将开始缓慢地从瓷砖网格中“脱离”。为此,我建议获取角色的中心坐标(坐标 + 平铺大小 / 2)并将它们转换为平铺坐标,然后将其转换回“真实”坐标浮动。也有许多其他方法。

  2. 确保在运动时禁用按键。如果你不这样做,角色可以通过中断运动中途自由地“离开”平铺网格。你可以保留一个布尔值inMotion,或者更简洁地说,测试是否velocity不是Vector2.Zero(然后你知道你在运动)。

如果您希望在中途中断运动并仍保持与网格同步,它会变得更先进一些,但这应该会给您一个良好的开端。

于 2013-01-02T10:05:01.473 回答
2

撇开关于适当性的问题:

您在每次按键时将单位添加到位置。在印刷机上移动的正确数量将取决于您的主循环的速度:在这里,您在每次游戏更新时移动 30 个单位,这可能很多或很少(如果没有更多上下文很难判断)。

为了便于讨论,假设您的图块跨度为 100 个单位,我们将移动速度保持为每刻 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

编辑:斯科特解决方案非常相似,但我个人更喜欢在源和目标之间制作动画,而不是每帧增加位置。所以这是我的版本:

您只需要在角色位于图块(中间)而不是移动时检查键。然后,当检测到一个键时,您开始从 tile1 到 tile2 的移动动画,并等待动画完成以再次接受输入。

私人变量:

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 回答