我正在尝试在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;
}
答案 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;
(如果你使用固定的游戏时间,你实际上不需要经过的时间相乘,但我通常把它放在那里,因为我更喜欢以每秒的单位来衡量。)
这与“平滑”动作完全不同。现在您已经获得了基本速度,现在您只需要在移动一个完整的磁贴后停止。有很多方法可以解决这个问题,但我会在这里草拟一个简单的方法。
保持一个“距离”变量来表示角色到目前为止在他的动作中走了多远,与方向/轴无关:
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)并将其转换为平铺坐标,然后将其转换回“真实”坐标彩车。还有许多其他方法。
确保在动作时禁用按键。如果不这样做,角色可以通过中断运动中途自由地“关闭”平铺网格。如果inMotion
不是velocity
(那么你知道你在行动中),你可以保持一个布尔Vector2.Zero
或更简洁的测试。
如果你想允许在中途停止动作并且仍然保持同步到网格,它会变得更先进,但这应该会给你一个良好的开端。
答案 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;
}