Hi gamedev, I'm developing a Tetris clone and working on the input at the moment.
When I was prototyping, movement was triggered by releasing a directional key. However, in most Tetris games I've played the movement is a bit more complex.
When a directional key is pressed, the shape moves one space in that direction. After a short interval, if the key is still held down, the shape starts moving in the direction continuously until the key is released.
In the case of the down key being pressed, there is no pause between the initial movement and the subsequent continuous movement.
I've come up with a solution, and it works well, but it's totally over-engineered. Hey, at least I can recognize when things are getting silly, right? :)
public class TetrisMover
{
List registeredKeys;
Dictionary continuousPressedTime;
Dictionary totalPressedTime;
Dictionary initialIntervals;
Dictionary continousIntervals;
Dictionary keyActions;
Dictionary initialActionDone;
KeyboardState currentKeyboardState;
public TetrisMover()
{
*snip*
}
public void Update(GameTime gameTime)
{
currentKeyboardState = Keyboard.GetState();
foreach (Keys currentKey in registeredKeys)
{
if (currentKeyboardState.IsKeyUp(currentKey))
{
continuousPressedTime[currentKey] = TimeSpan.Zero;
totalPressedTime[currentKey] = TimeSpan.Zero;
initialActionDone[currentKey] = false;
}
else
{
if (initialActionDone[currentKey] == false)
{
keyActions[currentKey]();
initialActionDone[currentKey] = true;
}
totalPressedTime[currentKey] += gameTime.ElapsedGameTime;
if (totalPressedTime[currentKey] = initialIntervals[currentKey])
{
continuousPressedTime[currentKey] += gameTime.ElapsedGameTime;
if (continuousPressedTime[currentKey] = continousIntervals[currentKey])
{
keyActions[currentKey]();
continuousPressedTime[currentKey] = TimeSpan.Zero;
}
}
}
}
}
public void RegisterKey(Keys key, TimeSpan initialInterval, TimeSpan continuousInterval, Action keyAction)
{
if (registeredKeys.Contains(key))
throw new InvalidOperationException(
string.Format("The key %s is already registered.", key));
registeredKeys.Add(key);
continuousPressedTime.Add(key, TimeSpan.Zero);
totalPressedTime.Add(key, TimeSpan.Zero);
initialIntervals.Add(key, initialInterval);
continousIntervals.Add(key, continuousInterval);
keyActions.Add(key, keyAction);
initialActionDone.Add(key, false);
}
public void UnregisterKey(Keys key)
{
*snip*
}
}
I'm updating it every frame, and this is how I'm registering keys for movement:
tetrisMover.RegisterKey(
Keys.Left, keyHoldStartSpecialInterval, keyHoldMovementInterval,
() = { Move(Direction.Left); });
tetrisMover.RegisterKey(
Keys.Right, keyHoldStartSpecialInterval, keyHoldMovementInterval,
() = { Move(Direction.Right); });
tetrisMover.RegisterKey(
Keys.Down, TimeSpan.Zero, keyHoldMovementInterval,
() = { PerformGravity(); });
Issues that this doesn't address:
If both left and right are held down,
the shape moves back and forth really
quick.
If a directional key is held down and
the turn finishes and the shape is
replaced by a new one, the new one
will move quickly in that direction
instead of the little pause it is
supposed to have.
I could fix the issues, but I think it will make the solution even worse. How would you implement this?