I've used the modulus operator method before while I was trying to emulate the movement in classic JRPG games, and also ran into the same limitation as you did. In order to be able to use any speed value, you need to approach the problem in a very different way.
The method I suggest is to treat movement as if you were following a path, moving your character between each node in the path at whatever speed you want, but making sure to keep the path nodes aligned to the grid. Because if all of the end points of your movement are aligned to the grid, then your character will also remain aligned, no matter what speed he's moving at.
For this answer I'll just give an example of how it can be implemented, although there are many other ways to do it. I've based the implementation for this answer on another answer I've given before, which I suggest you to check if you have any questions about the method below:
The core of that answer is the following method which moves the position
towards a goal
, snaps to the goal
if the movement happens to overshoot, and lets the user know when the movement has finished:
private bool MoveTowardsPoint(Vector2 goal, float elapsed)
{
// If we're already at the goal return immediatly
if (_position == goal) return true;
// Find direction from current position to goal
Vector2 direction = Vector2.Normalize(goal - _position);
// Move in that direction
_position += direction * Speed * elapsed;
// If we moved PAST the goal, move it back to the goal
if (Math.Abs(Vector2.Dot(direction, Vector2.Normalize(goal - _position)) + 1) < 0.1f)
_position = goal;
// Return whether we've reached the goal or not
return _position == goal;
}
Using this method, it's pretty simple to move your character around. But unlike the other answer, this time instead of using a list of positions as the path for the character to follow, I decided to only record what the next target is, and compute a new target whenever the old one is reached.
I created these variables for the pacman, that specify its position, speed, direction, and a nullable Vector2 to store the next target in its path:
private Vector2 _position = new Vector2(32, 32);
private const float Speed = 156;
private Vector2 _direction = new Vector2(0, 1);
private Vector2? _target;
And here's the bulk of the Update
method:
// Detect user input and set direction
if (Keyboard.GetState().IsKeyDown(Keys.Down)) _direction = new Vector2(0, 1);
if (Keyboard.GetState().IsKeyDown(Keys.Right)) _direction = new Vector2(1, 0);
if (Keyboard.GetState().IsKeyDown(Keys.Up)) _direction = new Vector2(0, -1);
if (Keyboard.GetState().IsKeyDown(Keys.Left)) _direction = new Vector2(-1, 0);
// If there's no target and we can move in the chosen direction, set new target to be the next tile
if (_target == null)
if (CanMoveInDirection(_position, _direction))
_target = _position + _direction * 32;
// If there's a target, then move pacman towards that location, and clear target when destination is reached
if (_target != null)
if (MoveTowardsPoint(_target.Value, (float) gameTime.ElapsedGameTime.TotalSeconds))
_target = null;
The comments should be self explanatory, but I suggest you to try the sample below and see for yourself how it works. Just copy the code into a new XNA project, it does not require any assets to run.
PS: I know some parts of the movement are not behaving exactly like pacman, such as stopping when changing direction towards a wall. The point of this answer is really just the grid aligned movement.