Saturday, September 21, 2013

The Input System

Two weeks went by without a post, and i've been busy.
I've been busy playing Borderlands 2, and brainstorming and implementing my Input System.

I've been thinking on how to implement this system in a way to allow more than two types of devices (mouse and keyboard). There was a good question somewhere on the web that made me think about this: "What if i decided to support a gamepad? How would that change my code?"

So i set to work, and after some google-fu sessions decided on the following approach.

I changed the code a bit to allow the Window to accept listeners which want to listen to all OS messages. Then i registered my InputEngine to the Window, so it gets the messages.

When a message is received, this invokes handle() on the InputEngine, which in turn calls handle() for every device that has been created.

The devices are a way to translate the OS specific messages to my own, device specific, events. MouseDevice, KeyboardDevice, GamepadDevice, TouchscreenDevice, etc, are very platform specific in how they work. Each device has a chance to translate the OS message to a specific event, and in theory, only one device should be able to do that. If the event is successfully mapped, the event is stored in an event queue for later use.

So now that i have a sequence of device specific events, i need to translate them to game specific events. Think of this as "When the user presses <some key>, his character should run forward". So basically, i need to translate the device specific event into an intent (which in this case would be a RUN intent). When it's time to handle input in the game loop, the InputEngine goes through all of the device specific events, and gives them to a context dependent intent generator (name taken from this SO thread).

The intent generator is a sequence of sets of functions that try to map the events to intents, with a specific priority. For instance, in Assassins Creed games, when the player presses and holds the right mouse button, the context changes so all of the buttons map to High Profile actions. When RMB is let go, the context is popped off, and the buttons map back to low profile actions.

So when all device events are processed, the result is a list/sequence of intents (if there was anything mapped to those device specific events) which is returned to the Input Action (my whole engine is based on these actions). The Input Action then traverses all entities that are marked as input controllable, and gives them a chance to execute every intent. If any entity is not capable of executing some intent, it simply doesn't, and the loop goes on.

So this is my input system. I still haven't fully implemented it (i'm currently working on the intent generator), and it took me a while to reach a consensus with myself on how to do it. I tried a couple of different approaches, but they weren't as flexible and/or layered as this one.

Monday, September 2, 2013

Polishing my Pong

It's been a while since my last post. I took a week off to spend time with a friend who came to visit me for a week here in Stockholm, which should explain the absence of my standard monday post. But it felt good to take a week off from work after work. :)

But now i'm here, and i can show the progress i've made 2 weeks ago.
The first piece of progress has been my finalization of the Entity Component System. It probably has places which could be made better (and i have an idea on how), but that will have to wait for a later time. I need to see how much i can do with the current implementation before i start pushing changes. Again. :)

In my previous post, i talked about the small-ish problem of having no control over the destruction of more complex data the Entity holds. I solved it by registering a callback to my Entity Pool class which is invoked on destruction of an Entity instance, and which then seeks the complex data and does it's magic. Using this concept, i made the Physics logic class register the appropriate lambda to the pool, so everything is cleaned up properly.

Then i started doing some work on actually getting Pong to a playable state. I haven't finished with that, but i have managed to get the foundation for it set up.

Right now, the paddles are there, the ball is set with a starting impulse in a specific direction, and the walls and goals are there. There's more work to be done here, and thankfully, i'm able to make a TODO list of what's left:
1. get input working so the paddles can be moved up and down
2. set up a random direction for the ball when it starts moving from the center of the field
3. get collision working between the paddles and the walls
4. make the goals sensors, which reset the ball back to the center of the screen, and increase the score
5. get the ball to bounce with a bigger angle when it hits the sides of the paddle
6. add awesome smashing next-gen graphics which will make this sell a ton of copies
7. add GUI element for score
8. make the ball increase it's speed with one of two possibilities:
8.a. as a function of time
8.b. as a function of the number of times it collided with anything

Most of these should be self-explanatory, but number 3. bears (rawr) a bit more explanation.
The way Box2D is setup, there are 3 types of objects: static (cannot move), dynamic (move and collide with everything) and kinematic (exactly like static, but can move). In order to detect a collision, at least one of the bodies needs to be a dynamic body. The above screenshot has the paddles as kinematic bodies, however, in my first setup, the paddles were also dynamic bodies, and when the ball hit a paddle, the paddle would suddenly fly away because of the collision, which is funny to see.

You can see the left paddle down in the corner, and the right paddle being hit and having collision way off.
One way i could solve this is by having the ball density really low, and the paddle density really high. That way the paddles will be almost immune to any forces being applied as a result of the collision, and the ball will behave as expected. And now, after putting this on paper, it seems as a very obvious solution, which i'll implement. :)

And that's it for this week. Tune in next monday to (hopefully) see a finished Pong game in my framework. :)

Thanks for reading.