I had to give my brain a break from all the Processing \ Android stuff and get back into Python. I’ve done a few small apps with PyGame and it’s been an enjoyable process. But PyGame is SDL based, and because of that never felt as ‘Pythonic’ to me as I’d like. Plus, and this is a minor gripe, but it really rubs me the wrong way: There seems to be no way to easily anti-alias PyGame programs. Without that, to me, they end up looking just a bit too indy for my taste.
I thought I’d go about learning a new Python game development framework, and the one I settled on is pyglet.
Reasons I like pyglet?
- Authored in Python with no external dependencies (other than OpenGL). It ‘feels moar Python’.
- It’s mainly a big fancy wrapper around OpenGL (which I want to learn).
- I like it’s event system.
- You can anti-alias it!
- It has an even higher-level wrapper API, cocos2d (which I’ll get into later), which adds even more game-framework related abilities.
Things that bum me out about it:
- Unless I’m missing it, has no built-in primitives library. If you want to draw a line, or a filled circle, you gotta roll your own. Yes, it has a ‘pyglet.graphics’ library (here, here), but it’s not exactly plug-n-play.
- The API, while great in some areas, doesn’t have all the convenience functions I’m used to with something like Processing, or even PyGame. But this means I get to learn how to do it, which isn’t a bad thing.
- There is no sprite\rect-based collision systems (unlike PyGame).
So, my pyglet beginnings are below. I tried to come up with a basic window displaying primitives, anti-aliased. I found a great primitives library that someone already authored so that filled in my primitives hole. And I wrote my own ‘utils’ module as I learned stuff, slowly filling in the missing pieces I think I’ll need in the future.
- Pyglet Programming Guide
- Pyglet API
- Google Code
- ‘2d Drawing Primitives Module‘ (used in the below code)
Here is my ‘utilities’ module:
# pyglet.utils.py # www.akeric.com - 2011-03-17 # utils to make pyglet easier to work with, help my learning of it. import pyglet from pyglet.gl import * def screenshot(name='screenshot'): """ Take a screenshot Parameters: name : string : Default 'screenshot'. Name of the saved image. Will always save as .png """ # Get the 'the back-left color buffer' pyglet.image.get_buffer_manager().get_color_buffer().save('%s.png'%name) def getPixelValue(x, y): """ Return the RGBA 0-255 color value of the pixel at the x,y position. """ # BufferManager, ColorBufferImage color_buffer = pyglet.image.get_buffer_manager().get_color_buffer() # AbstractImage, ImageData, sequece of bytes pix = color_buffer.get_region(x,y,1,1).get_image_data().get_data("RGBA", 4) return pix, pix, pix, pix def drawPoint(x, y, color): """ Based on the (r,g,b) color passed in, draw a point at the given x,y coord. """ pyglet.graphics.draw(1, GL_POINTS, ('v2i', (x, y)), ('c3B', (color, color, color) ) ) def getSmoothConfig(): """ Sets up a configuration that allows of smoothing\antialiasing. The return of this is passed to the config parameter of the created window. """ try: # Try and create a window config with multisampling (antialiasing) config = Config(sample_buffers=1, samples=4, depth_size=16, double_buffer=True) except pyglet.window.NoSuchConfigException: print "Smooth contex could not be aquiried." config = None return config def printEvents(window): """ Debug tool that will print the events to the console. window is an instance of a Window object receiving the events. """ window.push_handlers(pyglet.window.event.WindowEventLogger()) def playMusic(music): """ Simple wrapper to play a music (mp3) file. music : music file relative to application. """ music = pyglet.resource.media(music) music.play() def setBackgroundColor(color): """ Color is a list of four values, [r,g,b,a], each from 0 -> 1 """ pyglet.gl.glClearColor(*color)
And here is my ‘basic window’ which draws primitives. It is a dupe of the primitives drawing example from the primitives.py module, but modified into a new window:
# primitivesTest01.py # www.akeric.com - 2011-03-17 import sys import random import pyglet from pyglet.gl import * import primitives # module discussed above import utils # module from above FPS = 60 smoothConfig = utils.getSmoothConfig() class PrimWin(pyglet.window.Window): def __init__(self): super(PrimWin, self).__init__(fullscreen=False, caption='Primitives Test!', config=smoothConfig) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) self.p = primitives.Pixel(10,10) self.c = primitives.Circle(200,100,width=100,color=(0.,.9,0.,1.)) self.a = primitives.Arc(150,150,radius=100,color=(1.,0.,0.,1.),sweep=90,style=GLU_FILL) self.P = primitives.Polygon([(0, 0), (50, 200), (80, 200),(60,100),(100,5)],color=(.3,0.2,0.5,.7)) self.l = primitives.Line((10,299),(100,25),stroke=8,color=(0,0.,1.,1.)) # Setup debug framerate display: self.fps_display = pyglet.clock.ClockDisplay() # Schedule the update of this window, so it will advance in time at the # defined framerate. If we don't, the window will only update on events # like mouse motion. pyglet.clock.schedule_interval(self.update, 1.0/FPS) def on_draw(self): # Window event self.clear() self.c.render() self.p.render() self.a.render() self.P.render() self.l.render() self.fps_display.draw() def on_mouse_motion(self, x, y, dx, dy): # Window event self.c.x = x self.c.y = y def update(self, dt): # Scheduled event self.a.rotation+=1 self.c.color = [random.random() for i in xrange(3)]+ if __name__ == '__main__': PrimWin() sys.exit(pyglet.app.run())
And the result of this awesome code is the screenshot at the top of the screen.
Next steps will be to dig more into cocos2d and see if I can come up with something more interesting than the above screenshot to develop.