Sunday, March 25, 2012

Python Explorations

Most of my In Profundis work this week has been in research.  Here's what I'm looking into.

There are benefits and drawbacks to using Python as a development language.  The biggest drawbacks are performance (although that's not as bad as you'd think) and something called the GIL, or Global Interpreter Lock.

A feature of many dynamic languages (Ruby has one too), the GIL is something that ensures that, even in multithreaded code, only one thread can execute at a time.  On a single-core machine this is not so bad since the code is stuck doing this, more or less, anyway, but it means we won't get all the expected performance out of multicore machines that we might expect.  (Note: I'm not currently sure if Pygame's rendering respects the GIL.)  This is done to simplify the behind-the-scenes details of Python.  There are exceptions (especially regarding I/O), but for the most part threading seems like of a convenience feature than something one would turn to to improve speed.

But all is not lost, there are other solutions.  One is to use the module multiprocessing, which offers a way around the GIL.  Another is to use a version of Python that solves the GIL problem; both Jython and IronPython allow this, although they don't interface to Pygame to my knowledge.  And then there's to optimize the cellular loop using something like Numpy and/or Cython.

Other notes:

Discarding Psyco means some hard choices I had to go with in earlier versions, especially concerning Python version, aren't so hard anymore.  This opens things up a bit concerning other modules, although I still can't go to Python 3.X because Pygame requires 2.X.

In the last message someone expressed concern that calculating the whole world would make the game unresponsive.  This is actually not necessarily the case, as the calculation loop is written in such a way that on a given "frame" it can just calculate part of the world, remembering where it left off to continue later.  So, I can calculate one-fifth of the world this frame, get player input, then another fifth the next frame, and so on.  It queues cells up in a spiral pattern from around the player's location, and the visible screen is the beginning of each pass of the world, so we don't even have to worry about visible calculation artifacts.  Neat, huh?  In the future it would be nice to calculate different, non-adjacent areas of the world in different threads to make use of multicore systems -- maybe the multiprocessing module can help with that.

Tuesday, March 20, 2012

State of the project: 3/20/11

This is rambly and questionably edited, but I wanted to get something out there about the project and what's up with it.

There hasn't been a lot of comment here recently because I've been thinking long and hard about the design, and about what I'm happy with about the direction In Profundis is going, and what I'm not so happy about.

Because of this, a semi-radical redirection of the project is underway.  Many computers are not fast enough to run an automation of the size of the game's world as an action game unless I only simulate a zone around the player, and I've been unhappy with my efforts to make a platforming engine. As Miyamoto has been known to say, an idea is something that solves two problems at once, and I think I have the solution: to make In Profundis a quasi-real-time, turn-based game.

So how will this work?  Well, first off, character movement won't be on a pixel level.  Instead, like in roguelikes, the character will move one cell at a time.  There will probably be an animation between cells and some concessions to playability, but you won't be able to stop "between" spaces.

The game will attempt to simulate the entire field each frame.  This will greatly reduce framerates, but will mean that the engine can support one of the features I consider to be essential: continuously flowing water between points.  If I don't simulate the whole field then liquids will tend to accumulate at the edges of the execution frame, meaning flows like waterfalls and rivers will eventually cease if one end of the flow is outside the frame.  Obsessive thinking about the problem has identified no solutions that themselves won't cause bigger problems, so I've come to the conclusion that cheating this isn't the answer.

This means to a degree actually abandoning the platforming physics engine, but I'm okay with it as it has taken a disproportionately large amount of development time to implement, and it's still pretty bad.  By simplifying the physics here, I can avoid some of the annoying edge cases that result in frequent character wall embeds, or at least handle them more elegantly.

However if you sit and do nothing, I'm thinking that game time will not freeze, although it may slow down.  Not only does this still provide some time pressure, but it also makes it more obvious what direction flows are going without having to add new visual elements to identify them.

What will probably happen is I'll run the world simulation and character turns on different threads.  One problem with this, however, is that Python threading is a odd.  To my understanding, due to the existence of a thing called the Global Interpreter Lock, threads aren't like real operating system threads, but cannot execute more than one process at a time, even if Python is running on a multiprocessor system.  (There is a module to get around this, multiprocessing, and there are versions of Python that either remove the GIL or make it less onerous, but I'm not sure how compatible those are with Pygame.)

But one good thing about this is, since the game won't be so dependent on processor speed, we'll be able to abandon Psyco.  Honestly it has been the source of many technical problems.  It works great as a drop-in solution that just "works" to make Python faster... so long as all your modules work with it (at least one has mysteriously broken in the past when Psyco's been enabled, which took some time to discover)...  and so long as you avoid esoteric language features like generators... and don't use Python versions after 2.6.  I've at last reached the point where I'm ready to declare it's just more trouble than it's worth.  Fortunately, it's just as easy to remove as it was to add.

As an aside, I am thinking hard about the nature of fluids.  The game can currently simulate up to eight kinds of fluids with random properties, but that might be a case of too much randomness.  It might be best to stick with water, sand, and a couple of others in each world.