Live Coding Games in Python
Erik on Aug 2nd 2006
Reddit recently lead me to Logan Koester’s blog post about live coding with Python, and I found it fascinating. Live Coding is the practice of editing code for a running program in an environment that loads in the changes as they are made. Apparently people do this as a performance art, writing programs on the fly to play music. There is even a Wikipedia article on live coding.
I tried out the examples on Logan’s page, generated some weird drum loops, and had some fun with it all. The experience was a reminder that I want to learn how to make music. But it also opened my eyes to what seems like a new approach to programming.
After 30 seconds of looking I have failed to find a good reference on this, but I have read that a major factor in programmer productivity is the turnaround time between editing code and testing the results. If you are working on big project with a slow build process making changes takes a long time. Because changing too many things at once is a bad idea, you have to spend a lot of time waiting to test your code. If you are working on a small project in a scripting language, then it’s just save and run with little waiting to see the results of your edits.
What if we take this a step further? Are there productivity gains to be found through live coding, where you see the results of your changes when you save the code? I’ve decided it’s a neat idea, and I want to explore it. Since most of my programming focuses on game development, I tried out live coding with PyGame. What can I say? Recompiling your game’s codebase every frame strikes me as a wonderful way to trade cpu cycles for increased programmer productivity.
The first step is building the controlling script that does the live coding magic. I started with Logan’s example, but I wanted to do a couple of things differently. The first is I wanted to see a message and a backtrace for both syntax and runtime errors. The second is that I wanted the host and the client to be largely independent. Here is my controller.
import traceback import StringIO from time import sleep done = False imported = False loadBroken = False runBroken = False def genTraceback(ErrString): """ Takes an error message as an arguemnt. Generates a traceback of the most recent exception, and prints it with the error message. Sleeps for 2 seconds before returning. """ fp = StringIO.StringIO() traceback.print_exc(file=fp) message = fp.getvalue() print ErrString,"\n", message print "\nFix livegame.py to continue.\n" sleep(2) # loop until we sucessfully import livegame.py while not imported: try: import livegame except: if not loadBroken: genTraceback("Failed to import livegame.py.") # set a flag so that the traceback is only printed # once loadBroken = True else: imported = True # loop until livegame.play() returns True while not done: # attempt to reload the code try: reload (livegame) except: # if the reload fails, print the traceback once. if not loadBroken: genTraceback("livegame.py is broken.") loadBroken = True else: loadBroken = False # if the reload is succesful, try to run livegame.play() try: done = livegame.play() except: if not runBroken: genTraceback("play() in livegame.py is broken.") runBroken = True else: runBroken = False
Sorry for the \" silliness in the above code. It seems to be a wordpress bug. Anyway, what the above code does is load livegame.py and then reload it in a loop, calling livegame’s play function every time. Thanks to the traceback and StringIO modules, it outputs both syntax and runtime error tracebacks to the console seamlessly.
Here is a very basic livegame.py:
fps = 10 width = 640 height = 480 # check to see if the pygame module is in the namespace try: pygame except NameError: # pygame doesn't exsist, so import it. # this only happens once. import pygame from pygame.locals import * # initialize the pygame display pygame.init() pygame.display.set_mode((width,height),0,32) display = pygame.display.get_surface() # initialize the pygame clock clk = pygame.time.Clock() def play(): """ Main game logic, called once per frame.""" # fill the display with black. Clears the screen. pygame.draw.rect(display, (0,0,0,255),(0,0,width,height)) # draw a shape. pygame.draw.ellipse(display, (0,40,200,255),(200,200,100,50)) # update the display. pygame.display.flip() # check for events, and return true if we get an exit signal. events = pygame.event.get() for event in events: if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): return True # try to maintain a set frame rate. clk.tick(fps) # No exit signal was recived, so return false. return False
The first time this code is run, it initializes PyGame and creates a window. Every time play is called, it clears the window, draws a blue ellipse, checks for events, and then sleeps long enough to try to maintain a set framerate. If you press the escape key livegame signals the controller script, and the controller exits. To see where things get interesting, change this line:
pygame.draw.ellipse(display, (0,0,255,255),(200,200,100,50))
to something like this:
pygame.draw.rect(display, (255,0,0,255),(200,200,200,100))
Save the change with the PyGame window open beside your text editor, and watch the small blue ellipse instantly turn into a larger red rectangle. A basic example, I admit. But it hints at very deep potential.
The next step is taking this live environment, and building a full game using it. I intend to give it a try, hopefully soon. It will take some cleverness to keep things both dynamic and running fast enough. I might have to set up some sort of caching system so that expensive operations, such as loading an image or level from disk, don’t happen every frame. I could perhaps bind a key so that the expensive stuff is only done on request, but I think the experiment will be most interesting if I keep that sort of thing to a minimum.
There are obvious improvements to the above code that could be made, but I wanted to keep things as simple as possible. Though I do want to add more features before I make an attempt at building something significant using the live environment. I hope to modify the controller so that it takes the name of its client as a command line argument. I would like it if the controller gave some sort of audible notification if the client code caused an exception and produced a new traceback. I will see if I can use Ipython’s handy syntax highlighting for the controller’s tracebacks. It would also be neat to modify the client code to use the python if name == '__main__' trick. Then the client could optionally run as an independent program without the controller. This would also allow changing settings such as the target frames per second based on the environment the code is running in.
If I haven’t stated it enough already this post: this approach opens up lots of interesting possibilities. The only downside is that some day I’m going to start writing flash games that run directly in the browser. When I do that, I’m really going to miss all this dynamic language magic that I’m learning to use with Python.
Edit: I’ve posted an update, Live Coding, Continued
Filed in Games, Programing | 9 responses so far