Monday, June 27, 2011

Magnetic Glues

A Game Jam was organized during the 11th GulaschProgrammierNacht and the theme was magnetism. Here is the prototype I made in a few hours.

Windows users: quick play this game on Game Jolt (a .love version is also available for Linux and Mac users with Löve2D installed).

Download the sources in my cellar

I can't explain how the idea of making a Portal-like game appeared. Most probably it comes from the representation of magnetic dipoles, with red north pole and blue south pole, and I have quickly associated it with Portal.

I had two main goals with this prototype:
  • to test the physics library of Löve2D (actually a wrapper of Box2D)
  • to test a code architecture with entities, behaviors and components (explications below!)
Box2D is an efficient though simple physics engine. As the name implies, it is specialized for 2D physics: body's position is x, y and angle values.

An important feature for this gameplay is the ability to apply forces anywhere on the body, not only on the center (actually you may even choose an application point outside the body). But magnetic forces are omnipresent and Box2D was not designed to handle such forces. Worse, the magnetic force grows as the bodies come closer, so it is quite stressful when an object touches a magnetic glue. I limited the effect by restricting the forces to a maximal value, so they don't go to infinity.

There is a trick in the second level (the data's filename is 4 because I made it at the end). I didn't manage to handle well forces on each side. In every case, the platform got stuck inclined. So the trick is to set the body's width to the distance between the two wall's bodies minus one pixel. You may show this by enabling the debugging mode in main.lua. That way, it shouldn't rotate too much.

I said earlier that I had a discussion with Bruce about an architecture with entities, behaviors and components. I have given it a try with this prototype. Basically, an entity is composed of behaviors, and a behavior is composed of components. Theoretical examples of behaviors are "item", "movable" and "usable" while examples of components are "image", "(physical) body" and "input controller". Both entities and behaviors are game-specific, whereas components are given by the game engine. An entity may have at most one behavior of each type: having two identical behaviors makes no sense. However, a behavior may have several image or body components. For instance the player behavior has three images: the woman's body, her arm and the mouse cursor (because I thought a cursor would only appear when there is a player character).

Moreover, there are two communication mechanisms: entities may send events to other ones, and components may send signals which are connected to functions inside their parent behavior (they can't cross to other behaviors). These two mechanisms have different semantics, so they can't be merged (but I may give it a try in the future...). In theory, events are related to gameplay and may even be run asynchronously (although not in this implementation). Signals allow rather instantaneous, direct accesses to data.

But it was so complex. Entities ended to only have one behavior. In fact, entity and behavior could have been combined into a single class. And right now, just two events are sent to entities. They don't come from another entity, they are triggered by collision handlers, so I could have used alternatives not to have events. As I said, I wanted to assess the architecture, but maybe the gameplay doesn't fit well. Or maybe the way I coded it caused that, at the end, I was totally fed up with this architecture and I made some direct accesses to data... The signal system works (I'm a fan of Qt too, which heavily relies on signals-slots) but I am not sure it scales well when game engines become bigger.

A word about graphics for last. I confess I tried to copy the style of Portal: The Flash version. I made almost everything on Inkscape, using a grid to align lines. Therefore I made them quite quickly. A disadvantage of Inkscape is the y-axis which points up, and in Löve2D it points down, so I had to change the basis to set the collision boxes (I could also change it in their declaration, but it may confuse values' meaning). Each image has a "layer" to draw the background behind the objects, the cursor over them, etc.

Some things I'd change if I had to do it again:
  • I would really throw away behaviors, merging them with entities.
  • The asset format should be cleaned up.
  • There are some direct access to game and (physical) world (they are global). I would try to use no global variable. Maybe it's not a good practice, given that there is a single game and world.
  • There is a function in a level data. Perhaps it should better use a "script entity".
  • Some players reported a segfault. It may be related to physical bodies which seem not to be properly removed.

By the way, my game jam's favorite game is Princess by vrld (and I don't say that because I've kept in touched with him), made with Löve2D too. It was the only one to have a "story". Although very simple, the starting cut-scene and what the asteroids say took my breath away.

No comments:

Post a Comment