Live Coding, Continued
Erik on Aug 29th 2006
I have spent some time on further experiments with live coding and it seems to get tricky when you start using state. If you are loading and executing all of your code every frame, how do you handle tasks like animation? How do you load an image and cache it so you don’t have to reload every frame?
One approach I played with, just for fun, was animation without maintaining any state information. I have slightly expanded on my previous controller script. The major differences are that my new version will now print a stack trace if the current exception is different from the last exception. With the old script, if you eliminated one exception from your code, but a second exception prevented execution, no stack trace would be displayed. Also, this controller passes the livegame script the frame count when it calls play. You can view the updated script here: controller_stateless.py.
Here is my livegame_stateless.py:
fps = 30 screenWidth = 640 screenHeight = 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((screenWidth,screenHeight),0,32) display = pygame.display.get_surface() # initialize the pygame clock clk = pygame.time.Clock() def getMotion(framecount, shapeSize, screenSize, velocity): """ Calculates a linear position using a step number, a velocity, and a derived range of motion. Returns the position. """ # calculate the range of motion: workingSize = (screenSize - shapeSize) / velocity # use mod to find position in the range of motion. # *2 is used to bounce back and forth inside the range pos = framecount % (workingSize * 2) # if we are greater than the initial size, we are moving # in the opposite direction. if pos > workingSize: pos = 2*workingSize - pos return pos * velocity def play(framecount): """ 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,screenWidth,screenHeight)) shapeColor = (10,80,10,255) shapeWidth = 30 shapeHeight = 30 xVel = 10.0 # velocity in pixels per frame yVel = 10.0 x = getMotion(framecount, shapeWidth, screenWidth, xVel) y = getMotion(framecount, shapeHeight, screenHeight, yVel) # draw a shape. pygame.draw.ellipse(display, shapeColor,(x,y,shapeWidth,shapeHeight)) # 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
This code isn’t truly stateless. I use the Pygame module loading to do some Pygame initialization and window setup. Even this could be removed, but you would need a very fast machine to be able to recreate the pygame window every frame at a decent framerate. I’ve tried it though, and it is pretty cool to be able to change your window size mid run by tweaking your initialization parameters.
The animation produced by this code is stateless. And the code was surprisingly easy to write. It would be interesting to see how far you could push this approach. I think that it would rapidly become impossible to work with, as any sort of complex program would need to be passed all of the previous user input as a parameter to the play function. While it would be interesting to try to write a game that constantly derived it’s state from a time coded array of past user input, I doubt it would be fruitful. I suppose games like chess could read a board state and return a new board state every frame. Its great, from a mental exercise point of view, but I expect a dead end productivity wise.
Caching
So, onward to the next approach. Caching. This essentially boils down to having some code that isn’t run every frame. For example, if I wanted to move a box across the screen, I could use some special code to initialize it’s x position to 0, and then the live code that runs every frame increments the position by 1. You rapidly run into problems with this approach though. For some interesting reading on the topic of state and live coding, take a look at this reddit comment.
When I started working on this approach, I immediately ran into problems where my initialized state wasn’t in sync with my live code. To expand on the box moving example, lets suppose that I want to add a second moving box. So I add a line of code to the initialization section that sets x2 to 0, and I add my code to the live section to increment x2 by 1 every frame. Should work, right? Nope. If you do this while the code is live, you get an exception because x2 was never initialized, because the init code hasn’t been re-run since you added x2. I don’t have a solution for this that I am really happy with, but I have a couple of hacks that go a long way towards eliminating this problem. One is having ‘r’ keypresses call the init function. This helps a little. You could add x2 to the init, save, press ‘r’, add x2 to the live segment, and save again. But that sucks. The other hack I added was to the controller. I put the controller in charge of the initialization function at start up. Also, if the controller encounters an exception when calling play() on the livegame, it attempts to call the init function, to see if resetting the system will resolve the exception. I’m not really happy with my implementation, but you can take a look at it here: controller_hackish.py.
Here is the corresponding livegame_hackish.py. To run this, you will need an image. I use my pixel art alien:

fps = 30 width = 640 height = 480 import random class Alien: """ Effectivly a very simple sprite class, with it's properties defined in it's init code. """ def __init__(self): self.alienImage = pygame.image.load('eyealien.png') self.alienImage.convert_alpha() self.pos = [50,50] self.direction = [1,1] self.velocity = [2.5, 2.5] def draw(self, display): display.blit(self.alienImage, self.pos) def update(self): """ Update is called every frame. Adjust the alien's position based on it's velocity. """ if self.pos[0] > ( width - self.alienImage.get_width() ) \ or self.pos[0] < 0: self.direction[0] *= -1 if self.pos[1] > ( height - self.alienImage.get_width() ) \ or self.pos[1] < 0: self.direction[1] *= -1 self.pos[0] += self.velocity[0] * self.direction[0] self.pos[1] += self.velocity[1] * self.direction[1] # Ugh, has to be global because it is initialized in a function # and used in a different scope. global alien def initState(): """ Called by the controller, used to initialize variables and objects that maintain state. """ global alien alien = Alien() # 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() initState() def play(): """ Main game logic, called once per frame.""" global alien # 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, (180,40,200,255),(200,160,100,50)) # move the alien alien.update() # draw the alien alien.draw(display) # 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 == KEYDOWN and event.key == K_r: # reinitialize the program state. initState() 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 above code loads an image, and uses an object in a way that maintains state across frames. There are a few things that I don’t like about it though. One is the globals. I had to make my alien object global, because I initialize it in a function and then use it outside of that function’s scope. It’s ugly, and I don’t know if python offers a better way to do it. The other problem is related to the contents of the reddit comment I linked to above. When my alien object is initialized, it captures the code of it’s methods. Which means that the method code is not live. If you change the Alien.draw() method for example, the change doesn’t take effect when you save the file. You must reinitialize the system to get an alien object that uses the new code. It’s possible to work with this, but it doesn’t fit with the spirit of keeping everything ultra-dynamic for live coding.
It would be really nice if a Python guru could just hand me a framework that addressed all of the problems, and made live coding a completely painless process. I really think this sort of thing could make for an amazing game development environment. I just wish I had more free time to work through the quirks.
Not on the topic of live coding, but still along the lines of game development and python dynamism, I recently discovered a Python project called Bruce the Presentation Tool. I don’t know much about it, buy one feature caught my eye. It has an interactive Python shell running inside a Pygame window. I imagine that this could be very useful for introspection when working with Pygame. In the past I’ve had a lot of success with using a keystroke to spawn an iPython shell in the console window I’ve launched my game from. It’s extremely useful for debugging and experimentation. I imagine that a deeper embedding of the shell might be even better.
Filed in Games, Programing | 2 responses so far
Charlie Sep 1st 2006 at 06:25 pm 1
I don’t know if you know this, but the Jak & Daxter games were written in an in-house language (imperative but with Lisp-like syntax and function call semantics) that ran in a live-coding environment. Apparently it was done by maintaining a table of function pointers and doing a lookup for every function call. Word is it was a huge win, especially for gameplay tweaking and low-level optimization.
Richard Tew Sep 3rd 2006 at 12:12 am 2
This isn’t exactly rocket science :)
Step 1: Monitor for file changes.
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/215418
Step 2: Do the required wankery to get the instances using the new versions of the classes in the changed files. You can probably register with the operating system in some more optimal way for the same events.
Lunix = http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/160164
Windows = http://tgolden.sc.sabren.com/python/win32_how_do_i/watch_directory_for_changes.html
We do the equivalent in the framework we use in EVE Online, and have an embedded console. It is extremely convenient, and the number of times I need to restart the server in order make what I want to do work is few and far between.