XNA中的RPG风格运动

时间:2013-01-02 03:20:24

标签: c# xna

我正在尝试在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;
            }

3 个答案:

答案 0 :(得分: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;

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

Tile Motion

这与“平滑”动作完全不同。现在您已经获得了基本速度,现在您只需要在移动一个完整的磁贴后停止。有很多方法可以解决这个问题,但我会在这里草拟一个简单的方法。

保持一个“距离”变量来表示角色到目前为止在他的动作中走了多远,与方向/轴无关:

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或更简洁的测试。

  3. 如果你想允许在中途停止动作并且仍然保持同步到网格,它会变得更先进,但这应该会给你一个良好的开端。

答案 1 :(得分:2)

不考虑适当性的问题:

您在每次按键时都会向该位置添加单位。在媒体上移动的正确数量将取决于主循环的速度:在这里,你在每次游戏更新时移动30个单位,这可能是很多或一点(没有更多上下文很难说)。 / p>

为了争论,让我们说你的瓷砖是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更改为较低的数字(最后一次)或较高的数字(朝向下一行)来向前或向后偏移。

答案 2 :(得分: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;
}