After implementing the pause menu I realized that my current approach to keyboard and gamepad input was insufficient. If you hit the A button to dismiss the menu, your character would immediately jump after the menu exited – not so great.

My old input code was pretty simple: I had a custom type called InputState, containing booleans for each ‘input’ I care about – move left, move right, jump, pause, etc. Every frame, I cleared out the state of all those booleans, and then walked through all the keyboard keys and gamepad buttons I cared about and used them to update the input state. It was fairly straightforward – about two dozen lines of code in total – and it worked well for the basics of jumping around and moving. However, fixing the problem revealed by my menu requires a more complex input model.

Essentially, jumping and selecting a menu item are both responses to input events – a key press – instead of responses to key states. So I built a layer atop the existing input state to give me a better model for handling input.

My new model has a set of inputs, as before, but they have a different form and structure now. Each input is now an InputControl, with a name, a state, and a set of event listeners. The state behaves as before, being updated every frame. The main addition is the event listeners – every frame, after I determine the new state of all the input controls, I compare it against the old state of that control. If the state has changed, I construct an InputEvent and dispatch it to every event listener attached to that specific control, one at a time. Each event listener has the option to ‘suppress’ the event, indicating that the event was processed.

What this allows me to do is easily have a component like a menu eat a button press, but still use the straightforward ‘is the key held? then do something’ approach for things like character movement. The event listener model also allows me to keep things simple, since I can easily attach and detach event handlers at will without having to set up lots of scaffolding to keep things straight.

As a result, the event handling looks like this:

            _Accept = (c, e) => {
                if (e.Type != InputEventType.Press)
                    return false;

                var item = Items[SelectedIndex];
                item.Handler(item.Text);

                return true;
            };

            Game.InputControls.Accept.AddListener(_Accept);

            Game.InputControls.Accept.RemoveListener(_Accept);

A bit more verbose than I’d like, but so far it’s working quite well.

After I got the details of input worked out, I started fiddling with switches and doors. After a little hacking, I came up with a basic solution for both that works quite well:

Pressure plates are fairly easy – I take the existing code I wrote to determine what surface the player is standing on, and use that to determine every frame whether the player is standing on the pressure plate. Every frame, a pressure plate either moves up or down, depending on whether the player is standing on it. Based on that, I can determine whether the plate is ‘depressed’ just by checking its position. There are some other side details like making sure the plate doesn’t move too far, but all said it’s about a dozen lines of code.

Doors are actually not that different from pressure plates either, as a result of the model I’m using for collision detection and movement. I treat a door similarly to a pressure plate, in that every frame it either moves up or down. I determine which direction to move the door by calling a delegate every frame, which allows me to easily attach a door’s open/closed state to other objects in the game world:

            var plate1 = new PressurePlate(this,
                new Vector2(8 * Map.TileWidth, 24.85f * Map.TileHeight),
                new Vector2(9 * Map.TileWidth, 25 * Map.TileHeight)
            );
            _LevelGeometry.Add(plate1);
            Components.Add(plate1);

            var plate2 = new PressurePlate(this,
                new Vector2(4 * Map.TileWidth, 24.85f * Map.TileHeight),
                new Vector2(5 * Map.TileWidth, 25 * Map.TileHeight)
            );
            _LevelGeometry.Add(plate2);
            Components.Add(plate2);

            var door = new VerticalDoor(this,
                new Vector2(6 * Map.TileWidth, 21 * Map.TileHeight),
                new Vector2(7 * Map.TileWidth, 25 * Map.TileHeight),
                () => plate1.Depressed || plate2.Depressed
            );
            _LevelGeometry.Add(door);
            Components.Add(door);

As it turns out, I had to make some corrections to my sloped surface code for this to all work correctly. The moving door revealed a rather serious bug: If an object (like a door) moved into the player while the player was moving into the object, the existing sloped surface code wouldn’t run, which would result in the player passing through the top of the surface by a few pixels, and getting stuck. At that point, the door would be free to continue moving and the player would be completely unable to get free.

Luckily, the solution wasn’t actually as hard as I feared at first. I extended my existing ComputeStandingY algorithm to do an additional check: If the player is currently obstructed by an object, check to see if he’s within a few pixels of where he *would* be if he was standing on it. If he is, treat that position as his new Y position. Making this change provided a solution for the bug, and ensures that the player can jump onto a door just as it’s opening or closing without any collision glitches.