Pygame wrapper: Difference between revisions
(→Mesh: eliminate unsafe API description) |
m (Reverted edits by Rizwan1218 (Talk) to last revision by Quozl) |
||
(21 intermediate revisions by 14 users not shown) | |||
Line 1: | Line 1: | ||
{{Developers}} |
{{Developers}} |
||
<< [[API |
<< [[API reference]] |
||
{{Olpcboxtop|toptext=[[{{PAGENAME}}|OLPCGames]]}} |
{{Olpcboxtop|toptext=[[{{PAGENAME}}|OLPCGames]]}} |
||
Line 8: | Line 8: | ||
[http://dev.laptop.org/git?p=projects/games-misc;a=tree;f=olpcgames-src/dist;hb=HEAD Download] |
[http://dev.laptop.org/git?p=projects/games-misc;a=tree;f=olpcgames-src/dist;hb=HEAD Download] |
||
{{Olpcboxbottom}} |
{{Olpcboxbottom}} |
||
The [[Pygame]] wrapper for the OLPC [[Sugar]] platform is called OLPCGames. The [[Game development HOWTO]] describes how to use the wrapper to quickly create a skeletal Activity which has a basic Pygame event loop. This page explores the differences between standard Pygame programming and OLPCGames-mediated Pygame programming. |
|||
''(See also [http://wiki.sugarlabs.org/go/Development_Team/Sugargame Sugargame].)'' |
|||
The automatically generated [http://www.vrplumber.com/sugar-docs/olpcgames.html Pydoc documentation for OLPCGames] is also available and is likely to be more up-to-date than this document. |
|||
The [[Pygame]] wrapper for the OLPC [[Sugar]] platform is called OLPCGames. This page explores the differences between standard Pygame programming and OLPCGames-mediated Pygame programming. |
|||
The automatically generated [http://dev.laptop.org/~mcfletch/OLPCGames/pydoc/olpcgames.html Pydoc documentation for OLPCGames] is the canonical reference work for the documentation. |
|||
=Strategic Rationale= |
|||
The particular value of the OLPCGames wrapper is not, in fact, the ability to run Pygame games on the OLPC. The particular value is that by using Pygame for your game, and OLPCGames as your wrapper, you should not need to worry about the underlying details of the (rapidly changing, and rather breakage-prone) Sugar API. |
|||
Historically that changing API has forced each (PyGTK) developer to revise their activities time and again. OLPCGames, which is effectively the "Sugar" side of your Pygame activity, is shared among many projects, so that when it is fixed, all of the games using it are fixed along with it (generally only needing to replace the version of OLPCGames they are using and re-publishing the .xo file). |
|||
In addition, there are platforms where creating a working Sugar development environment is a non-trivial task. For those environments, it is often possible to set up a simple Pygame environment, write your game/Activity in that environment, and then port (or have someone port) the Activity to the XO in a matter of minutes. |
|||
=Tutorials= |
|||
* [http://www.vrplumber.com/olpc/pycon2008-handout.odt PyCon 2008 Tutorial] ([http://www.vrplumber.com/olpc/pycon2008.tar.gz Code]) -- Hello World, Participant Tracking in Games, Networked TicTacToe, Journal integration, SVG and Pango rendering |
|||
* [[Porting pygame games to the XO]] -- Phil Hassey's notes on porting two of his games to the XO. Much shorter and higher-level presentation, assumes you already have a working Pygame game, know your way around your game's code base, and just need to know what's different about OLPCGames under Sugar |
|||
* [[Game development HOWTO]] -- describes the process of building a new skeleton project |
|||
* [http://pygametutorials.wikidot.com OOP tutorial for PyGame] |
|||
=Getting the wrapper= |
=Getting the wrapper= |
||
Line 16: | Line 34: | ||
You can either [http://dev.laptop.org/~mcfletch/OLPCGames/ download the wrapper] as a .zip or .tar.gz |
You can either [http://dev.laptop.org/~mcfletch/OLPCGames/ download the wrapper] as a .zip or .tar.gz |
||
wget http://dev.laptop.org/~mcfletch/OLPCGames/OLPCGames-1. |
wget http://dev.laptop.org/~mcfletch/OLPCGames/OLPCGames-1.6.zip |
||
or you can check it out of the git repository on dev.laptop.org (note, do '''not''' attempt this on an XO or other space-constrained device, GIT downloads the entire history of the project, which is over 120MB in this case). |
or you can check it out of the git repository on dev.laptop.org (note, do '''not''' attempt this on an XO or other space-constrained device, GIT downloads the entire history of the project, which is over 120MB in this case). |
||
Line 30: | Line 48: | ||
* [[Story Builder]] -- Environment for creating story modules to be used in [[MaMaMedia]], uses the PGU GUI library extensively |
* [[Story Builder]] -- Environment for creating story modules to be used in [[MaMaMedia]], uses the PGU GUI library extensively |
||
* [[Games/Productive|Productive]] -- A Real-time Strategy game written explicitly for the OLPC platform. Includes networking via the mesh module (and raw Telepathy primitives). Graphics are via raw Pygame coding. |
* [[Games/Productive|Productive]] -- A Real-time Strategy game written explicitly for the OLPC platform. Includes networking via the mesh module (and raw Telepathy primitives). Graphics are via raw Pygame coding. |
||
* [[Games/FiftyTwo|FiftyTwo]] -- A set of card games |
|||
* [[Maze]] -- Maze navigation game |
* [[Maze]] -- Maze navigation game |
||
Line 38: | Line 57: | ||
* [http://dev.laptop.org/git?p=projects/games-misc;a=blob;f=videotest.activity/run.py;hb=HEAD Video Test] -- example showing use of the [[GStreamer]]-based <code>olpcgames.video</code> module [http://dev.laptop.org/~mcfletch/testactivities/videotest-2.xo XO] |
* [http://dev.laptop.org/git?p=projects/games-misc;a=blob;f=videotest.activity/run.py;hb=HEAD Video Test] -- example showing use of the [[GStreamer]]-based <code>olpcgames.video</code> module [http://dev.laptop.org/~mcfletch/testactivities/videotest-2.xo XO] |
||
* [http://drupal.ceibaljam.org/?q=node/235 Quinteti] -- Tic Tac Toe like game [http://drupal.ceibaljam.org/sites/default/files/Quinteti-1_0.xo XO] |
|||
=Differences from Pygame= |
=Differences from Pygame= |
||
Line 46: | Line 65: | ||
* You cannot set the display mode using pygame.display.set_mode. You must set it in the wrapper boilerplate instead (see [[Game development HOWTO]]). |
* You cannot set the display mode using pygame.display.set_mode. You must set it in the wrapper boilerplate instead (see [[Game development HOWTO]]). |
||
* It is not recommended that you use the regular Pygame.font text drawing. You can use the wrapper to draw text using the 'olpcgames.pangofont' module instead which supports proper internationalization. See [ |
* It is not recommended that you use the regular Pygame.font text drawing. You can use the wrapper to draw text using the 'olpcgames.pangofont' module instead which supports proper internationalization. See [http://dev.laptop.org/~mcfletch/OLPCGames/pydoc/olpcgames.pangofont.html Pangofont]. |
||
** ''NOTE'': If you are developing on an AMD64/EMT64 platform, there is a bug in Pygame 1.7.x which prevents it from working with the SVG or PangoFont modules. This bug has been fixed on the trunk of Pygame and should show up with the next release. |
|||
* The event module is shadowed by [[Pygame wrapper#Eventwrap]] and some methods may not work exactly the same. Certain methods in pygame.mouse and pygame.key are also shadowed. |
|||
* The event module is shadowed by [http://dev.laptop.org/~mcfletch/OLPCGames/pydoc/olpcgames.eventwrap.html Eventwrap] and some methods may not work exactly the same. Certain methods in pygame.mouse and pygame.key are also shadowed. |
|||
* There's no CD-ROM on OLPC-XOs, so the 'cdrom' module isn't generally useful. |
* There's no CD-ROM on OLPC-XOs, so the 'cdrom' module isn't generally useful. |
||
Line 86: | Line 107: | ||
The bit-depth chosen by the wrapper (16 bit on OLPC-XOs, normally) tends to make antialiased lines fail. The reason this is so hasn't been extensively investigated, so it's not known whether this is a hard-and-fast limitation, or just an optimization hint. |
The bit-depth chosen by the wrapper (16 bit on OLPC-XOs, normally) tends to make antialiased lines fail. The reason this is so hasn't been extensively investigated, so it's not known whether this is a hard-and-fast limitation, or just an optimization hint. |
||
= |
= Journal Integration = |
||
OLPCGames produces events to tell you about Journal save/restore requests: |
|||
Window resizing doesn't work yet (i.e., resizing windows via the screen rotate button), but we will try to get this working soon. |
|||
*pygame.USEREVENT |
|||
=Module Reference= |
|||
**code = olpcgames.FILE_READ_REQUEST (OLPCGames 1.6+) |
|||
*** filename -- the filename to be read |
|||
*** metadata -- metadata object (dictionary like) |
|||
**code = olpcgames.FILE_WRITE_REQUEST (OLPCGames 1.6+) |
|||
*** filename -- the filename to be written |
|||
*** metadata -- metadata object (dictionary like) |
|||
which are generated to allow you to save/restore to/from the [[Journal]]/Datastore on the OLPC. |
|||
Note that the [http://www.vrplumber.com/sugar-docs/olpcgames.html Pydoc documentation for OLPCGames] is often more up-to-date than the descriptions on this page. |
|||
= Window Resizing = |
|||
==Activity== |
|||
Window resizing doesn't work yet (i.e., resizing windows via the screen rotate button), but we will try to get this working soon. |
|||
The <code>olpcgames.activity</code> module encapsulates creation of a Pygame activity. Your Activity should inherit from this class. Simply setting some class attributes is all you need to do in a class inheriting from olpcgames.activity.PyGameActivity in order to get Pygame to work. (The skeleton builder script creates this file automatically for you). |
|||
=Module Reference= |
|||
You should not import pygame into your activity file, as the olpcgames wrapper needs to be initialized before pygame is imported the first time. |
|||
The [http://dev.laptop.org/~mcfletch/OLPCGames/pydoc/olpcgames.html pydoc-generated documentation] for the OLPCGames package serves as the canonical reference work at this point. |
|||
<pre> |
|||
class PyGameActivity(activity.Activity): |
|||
game_name = None |
|||
game_title = 'PyGame Game' |
|||
game_size = (units.grid_to_pixels(16), |
|||
units.grid_to_pixels(11)) |
|||
pygame_mode = 'SDL' |
|||
</pre> |
|||
= Customizing the Toolbar = |
|||
You can customise these required values: |
|||
If you want to add stuff to the standard toolbar you can override build_toolbar in your PyGameActivity subclass like this: |
|||
'''<code>game_name</code>''': This is a string containing the name of the module and, optionally a colon followed by the name of the main method (example: "tictactoe:main"). If there's no main method specified it defaults to "main". In this example, the wrapper code will import the module named "tictactoe" and call main() on it. That is expected to enter a Pygame main loop (which makes some call into pygame.event periodically, see [[Pygame wrapper#Eventwrap]]). |
|||
'''<code>game_title</code>''': This is the string containing the title of the game, as it appears in the Sugar toolbar at the top of activities. |
|||
'''<code>game_size</code>''': Pixel resolution of your game window. This is not changeable at runtime. This needs to match whatever you pass to pygame.display.set_mode(), and you cannot call set_mode() later with a different size. If game_size is None, a window will be created which tries to take up the whole activity window save for the activity toolbar at the top of the screen. |
|||
These attributes are optional: |
|||
<code>pygame_mode</code> can be set to 'Cairo' if you want experimental Cairo pygame support. In this case you need to include the 'pygamecairo' module accessible from your game. This is not recommended; the module is highly experimental and performance is not very good. |
|||
<code>game_handler</code> is a deprecated replacement for game_name, it holds a Python callable object reference. This approach doesn't work well due to the need to "hook" pygame before your code imports the various pygame modules. |
|||
==Canvas== |
|||
The canvas submodule handles wrapping events and initializing SDL inside the container. |
|||
<pre> |
<pre> |
||
import pygame |
|||
class PyGameCanvas(gtk.EventBox): |
|||
import olpcgames |
|||
pass |
|||
from sugar.graphics.toolbutton import ToolButton |
|||
</pre> |
|||
from gettext import gettext as _ |
|||
... |
|||
There's nothing you probably need to interact with in the canvas submodule. |
|||
def build_toolbar( self ): |
|||
==Eventwrap== |
|||
"""Build our Activity toolbar for the Sugar system.""" |
|||
toolbar = super( MyActivityClass, self ).build_toolbar() |
|||
The 'eventwrap' module is a replacement for pygame.event. It has much of the same interface (see [http://www.pygame.org/docs/ref/event.html]). See the doc-strings of these methods for full documentation; I'll point out the differences here: |
|||
# Add a button |
|||
There is an install() method which installs eventwrap in place of pygame.event, so that unaware Pygame applications will use this event queue rather than the native Pygame one. Performance is mostly unaffected, and this event queue is more versatile than Pygame's. Thus, we do this for all Pygame games as part of the Activity wrapper. |
|||
mybutton = ToolButton('activity-mybutton') # put the icon file here: MyGame.activity/icons/activity-mybutton.svg |
|||
mybutton.set_tooltip(_('Something')) |
|||
This event queue does not support getting events only of a certain type. You need to get all pending events at a time, or filter them yourself. You can, however, block and unblock events of certain types, so that may be useful to you. Set_grab doesn't do anything (you are not allowed to grab events). Sorry. |
|||
mybutton.connect('clicked', self._mybutton_cb) |
|||
toolbar.insert(mybutton, 2) |
|||
==Camera== |
|||
mybutton.show() |
|||
OLPCGames provides you with a simple wrapper class that will use GStreamer to snap an image with the built-in camera on the XO. The camera module works by creating a GStreamer pipeline that starts with the camera and terminates in a png or jpeg file. Alternately the module can allow you to take a "picture" of the test source, for those testing on non-XO hardware. |
|||
return toolbar |
|||
You will need to import the module: |
|||
from olpcgames import camera |
|||
when you want to take a picture with the camera, call snap_async, like so: |
|||
camera.snap_async( 'picture 32', source='test' ) |
|||
the gstreamer pipeline will be created and will iterate until complete. When the picture is available it will be read as a pygame image and returned via a camera.CAMERA_LOAD or camera.CAMERA_LOAD_FAIL event in your Pygame mainloop. These events have the following members: |
|||
* filename -- filename into which the file was saved |
|||
* success -- boolean indicating whether we succeeded/failed |
|||
* token -- the object passed in as the first argument to snap_async |
|||
* image -- the loaded image as a Pygame surface (image), or None on failure |
|||
* err -- if an error occurred, the Exception instance |
|||
There is a [http://dev.laptop.org/git?p=projects/games-misc;a=tree;f=cameratest.activity;hb=HEAD cameratest activity in GIT] which demonstrates basic use of the snap_async function. |
|||
'''Note''' with the current camera implementation taking a single photograph requires about 6 seconds! Obviously we'll need to figure out what's taking gstreamer so long to process the pipe and fix that. |
|||
Note that the camera module exposes an older synchronous API for backwards compatibility. You likely should *not* use that api, as it can hang your entire activity waiting for the GStreamer stream to complete. |
|||
==Mesh== |
|||
The 'mesh' module allows your Pygame game to be Shared across the OLPC networking infrastructure (D-bus and Tubes). |
|||
All Sugar activities have a 'Share' menu which is intended to allow other people to join the activity instance and collaborate with you. When you select Share, the activity's icon appears on the Neighborhood view of other laptops. If you do nothing with the 'mesh' module, this is all that will happen: if anyone selects your activity icon, they will just spawn a new instance of the activity, and they will get to play your game alone. |
|||
In order to send useful information across the mesh, you need to import this module (i.e., <code>import olpcgames.mesh as mesh</code>). |
|||
First, you need to be aware of the following event types that can now arrive on the Pygame event queue: |
|||
<pre> |
|||
'''The tube connection was started. (i.e., the user clicked Share or started |
|||
the activity from the Neighborhood screen). |
|||
Event properties: |
|||
id: a unique identifier for this connection. (shouldn't be needed for anything)''' |
|||
CONNECT = 9912 |
|||
'''A participant joined the activity. This will trigger for the local user |
|||
as well as any arriving remote users. |
|||
Event properties: |
|||
handle: the arriving user's handle.''' |
|||
PARTICIPANT_ADD = 9913 |
|||
'''A participant quit the activity. |
|||
Event properties: |
|||
handle: the departing user's handle.''' |
|||
PARTICIPANT_REMOVE = 9914 |
|||
'''A message was sent to you. |
|||
Event properties: |
|||
content: the content of the message (a string) |
|||
handle: the handle of the sending user.''' |
|||
MESSAGE_UNI = 9915 |
|||
'''A message was sent to everyone. |
|||
Event properties: |
|||
content: the content of the message (a string) |
|||
handle: the handle of the sending user.''' |
|||
MESSAGE_MULTI = 9916 |
|||
def _ mybutton_cb(self, button): |
|||
pygame.event.post(olpcgames.eventwrap.Event(pygame.USEREVENT, action='mybutton')) |
|||
</pre> |
</pre> |
||
Then in your event handler do this: |
|||
Once you have a handle from one of the above events, you can use it to send messages to them and get their buddy info. Alternatively, you can just broadcast messages to everyone: |
|||
<pre> |
<pre> |
||
if event.type == pygame.USEREVENT: |
|||
if event.action == 'mybutton': |
|||
def send_to(handle, content=""): |
|||
self.dosomething() |
|||
'''Sends the given message to the given buddy identified by handle.''' |
|||
else: |
|||
print "Unknown user event action:", event.action |
|||
def broadcast(content=""): |
|||
'''Sends the given message to all participants.''' |
|||
def my_handle(): |
|||
'''Returns the handle of this user.''' |
|||
def lookup_buddy(handle, callback): |
|||
"""Get a sugar.presence.Buddy from a handle.""" |
|||
def get_participants(): |
|||
'''Returns the list of active participants, in order of arrival. |
|||
List is maintained by the activity creator; if that person leaves |
|||
it may not stay in sync.''' |
|||
def dbus_get_object(handle, path): |
|||
'''Get a D-bus object from another participant. |
|||
This is how you can communicate with other participants using |
|||
arbitrary D-bus objects without having to manage the participants |
|||
yourself. Simply define a D-bus class with an interface and path |
|||
that you choose; when you want a reference to the corresponding |
|||
remote object on a participant, call this method.''' |
|||
</pre> |
</pre> |
||
[[Category:Developing games]] |
|||
In order to meaningfully communicate, you must |
|||
[[Category:Python]] |
|||
<pre> |
|||
from olpcgames import mesh |
|||
</pre> |
|||
Now, you can make use of events you receive that are mesh related. See [[Pygame_wrapper#Mesh]] for details. |
|||
Here is a simple example -- in your event loop, just add listening for certain mesh events: |
|||
<pre> |
|||
for evt in [ pygame.event.wait() ] + pygame.event.get( ): |
|||
if evt.type == pygame.KEYDOWN: |
|||
# ... |
|||
elif evt.type == mesh.PARTICIPANT_ADD: |
|||
# Somebody joined. Add them to our list of people, and send them a message: |
|||
self.participants.append(evt.handle) |
|||
mesh.send_to(evt.handle, str(self.game_state)) |
|||
elif evt.type == mesh.MESSAGE_UNI: |
|||
# Received a message! Display it (and/or decode it): |
|||
print "Got a message from %s: %s" % (evt.handle, evt.content) |
|||
# Figure out the nick of whoever sent it: |
|||
print "It was from buddy %s" % (mesh.get_buddy(evt.handle).props.nick) |
|||
</pre> |
|||
See Also: |
|||
* [http://blog.vrplumber.com/2016 Discussion] of how Productive uses the mesh module and raw Telepathy |
|||
==Pangofont== |
|||
The 'pangofont' module is a replacement for pygame.font. It has a similar interface (see [http://www.pygame.org/docs/ref/event.html]). By default you have to import PangoFont explicitly to use it: |
|||
from olpcgames import pangofont |
|||
myFont = pangofont.PangoFont( size=30, family='monospace' ) |
|||
image = myFont.render( u'some text', False, (0,0,0), (255,255,255)) |
|||
but if you would like to have PangoFont attempt to provide support on OLPC-unaware games, you can call the install method: |
|||
from olpcgames import pangofont |
|||
pangofont.install() |
|||
Keep in mind that most Pygame code will likely '''not''' work with PangoFont without some modifications. |
|||
See the [http://www.vrplumber.com/sugar-docs/olpcgames.pangofont.html pydoc for the module] for full documentation; I'll point out the major differences here: |
|||
* no ability to load TTF files, PangoFont uses the font files registered with GTK/X to render graphics, it cannot load an arbitrary TTF file. Most non-Sugar Pygame games use bundled TTF files, which means that you will likely need at least some changes to your font handling! |
|||
* limited mutation, only bold and italic properties can be mutated |
|||
* no underline operation |
|||
* no line-height, descender, etc operations |
|||
* far better support for "exotic" languages and scripts (which is why we use it) |
|||
The main problem with SDL_ttf is that it doesn't handle internationalization nearly as well as Pango (in fact, pretty much nothing does). However, it is fairly fast and it has a rich interface. You should avoid fonts where possible, prerender using Pango for internationalizable text, and use Pango or SDL_ttf for text that really needs to be rerendered each frame. (Use SDL_ttf if profiling demonstrates that performance is poor with Pango.) |
|||
<pre> |
|||
class PangoFont(object): |
|||
"""Base class for a pygame.font.Font-like object drawn by Pango.""" |
|||
def __init__(self, family=None, size=None, bold=False, italic=False, fd=None): |
|||
"""If you know what pango.FontDescription (fd) you want, pass it in as |
|||
'fd'. Otherwise, specify any number of family, size, bold, or italic, |
|||
and we will try to match something up for you.""" |
|||
def render(self, text, antialias, color, background=None): |
|||
"""Render the font onto a new Surface and return it. |
|||
We ignore 'antialias' and use system settings. |
|||
NOTE: Due to a retarded implementation problem you cannot use |
|||
transparent colors. Alpha is ignored (set to 255).""" |
|||
class SysFont(PangoFont): |
|||
"""Construct a PangoFont from a font description (name), size in pixels, |
|||
bold, and italic designation. Similar to SysFont from Pygame.""" |
|||
def __init__(self, name, size, bold=False, italic=False): |
|||
def fontByDesc(desc="",bold=False,italic=False): |
|||
"""Constructs a FontDescription from the given string representation.""" |
|||
</pre> |
|||
The format of the fontByDesc string representation is passed directly to the pango.FontDescription constructor and documented at [http://www.pygtk.org/docs/pygtk/class-pangofontdescription.html#constructor-pangofontdescription]. Bold and italic are provided as a convenience. Example descriptions: |
|||
"sans bold 12" |
|||
"serif,monospace bold italic condensed 16" |
|||
"normal 10" |
|||
Note that PangoFont objects only have a few of pygame.font.Font's methods to mutate and examine the font. If you really need these methods, you can consider sticking with SDL_ttf if it will support your language needs. |
Latest revision as of 20:33, 25 July 2013
(See also Sugargame.)
The Pygame wrapper for the OLPC Sugar platform is called OLPCGames. This page explores the differences between standard Pygame programming and OLPCGames-mediated Pygame programming.
The automatically generated Pydoc documentation for OLPCGames is the canonical reference work for the documentation.
Strategic Rationale
The particular value of the OLPCGames wrapper is not, in fact, the ability to run Pygame games on the OLPC. The particular value is that by using Pygame for your game, and OLPCGames as your wrapper, you should not need to worry about the underlying details of the (rapidly changing, and rather breakage-prone) Sugar API.
Historically that changing API has forced each (PyGTK) developer to revise their activities time and again. OLPCGames, which is effectively the "Sugar" side of your Pygame activity, is shared among many projects, so that when it is fixed, all of the games using it are fixed along with it (generally only needing to replace the version of OLPCGames they are using and re-publishing the .xo file).
In addition, there are platforms where creating a working Sugar development environment is a non-trivial task. For those environments, it is often possible to set up a simple Pygame environment, write your game/Activity in that environment, and then port (or have someone port) the Activity to the XO in a matter of minutes.
Tutorials
- PyCon 2008 Tutorial (Code) -- Hello World, Participant Tracking in Games, Networked TicTacToe, Journal integration, SVG and Pango rendering
- Porting pygame games to the XO -- Phil Hassey's notes on porting two of his games to the XO. Much shorter and higher-level presentation, assumes you already have a working Pygame game, know your way around your game's code base, and just need to know what's different about OLPCGames under Sugar
- Game development HOWTO -- describes the process of building a new skeleton project
- OOP tutorial for PyGame
Getting the wrapper
You can either download the wrapper as a .zip or .tar.gz
wget http://dev.laptop.org/~mcfletch/OLPCGames/OLPCGames-1.6.zip
or you can check it out of the git repository on dev.laptop.org (note, do not attempt this on an XO or other space-constrained device, GIT downloads the entire history of the project, which is over 120MB in this case).
git clone git://dev.laptop.org/projects/games-misc
The 'olpcgames' directory is the package in question. Submodules you can access are activity, canvas, camera, mesh, and pangofont. The wrapper also replaces certain Python modules (e.g. pygame.event with 'eventwrap' (which can also be imported separately)), so we document those here too.
Activities
The following Activities use OLPCGames and can serve as example code, (note that some of these projects may not be finished yet):
- Story Builder -- Environment for creating story modules to be used in MaMaMedia, uses the PGU GUI library extensively
- Productive -- A Real-time Strategy game written explicitly for the OLPC platform. Includes networking via the mesh module (and raw Telepathy primitives). Graphics are via raw Pygame coding.
- FiftyTwo -- A set of card games
- Maze -- Maze navigation game
- Camera Test -- example of using the
olpcgames.camera
module XO - Sound Test -- example showing simple multi-channel sound usage XO
- SVG Sprite Test -- example showing use of the
olpcgames.svgsprite
module XO
- Video Test -- example showing use of the GStreamer-based
olpcgames.video
module XO - Quinteti -- Tic Tac Toe like game XO
Differences from Pygame
The SDL Pygame wrapper allows for nested Pygame windows using a separate thread. It forwards GTK events and converts them to Pygame events. Games under the wrapper may not work exactly the same way and porting is not completely seamless -- you should be aware of a few OLPC-specific caveats:
- You cannot set the display mode using pygame.display.set_mode. You must set it in the wrapper boilerplate instead (see Game development HOWTO).
- It is not recommended that you use the regular Pygame.font text drawing. You can use the wrapper to draw text using the 'olpcgames.pangofont' module instead which supports proper internationalization. See Pangofont.
- NOTE: If you are developing on an AMD64/EMT64 platform, there is a bug in Pygame 1.7.x which prevents it from working with the SVG or PangoFont modules. This bug has been fixed on the trunk of Pygame and should show up with the next release.
- The event module is shadowed by Eventwrap and some methods may not work exactly the same. Certain methods in pygame.mouse and pygame.key are also shadowed.
- There's no CD-ROM on OLPC-XOs, so the 'cdrom' module isn't generally useful.
Keyboard and Mouse
Keyboard and mouse work approximately as they do under Pygame. We simulate repeated key-down events when a key is held down for a period in order to simulate Pygame's operation.
The "gamepad" buttons on the left and right of the screen come in as Numpad number keys (i.e., pygame.K_KP1
through pygame.K_KP9
):
D-Pad (left of screen) Gamepad (right of screen) 8 9 O 4 6 7 1 [] V 2 3 X
The D-pad (directional pad) mappings make sense as the traditional arrow keys on the numeric keypad of 101-key keyboards. The gamepad mappings make sense when you realize that 9/3 are page-up/page-down and 7/1 are home/end. The d-pad has 8 directions of articulation. You detect the diagonals by looking for two keys pressed at the same time.
When designing your interfaces keep in mind that an OLPC-XO in tablet mode only has the eight "keys" above available (as well as a resize-and-rotate key, but that's already mapped by the operating system).
Keep in mind that your activities will need to be localized into many languages, so binding, for instance "p" to "produce" is sub-optimal. If possible provide a run-time configuration, or at least a localization-time configuration mechanism (such as a configuration file) that lets more natural keys be chosen for each language's keyboard.
OLPC-XO (B4) Hardware Notes
The D-Pad control is usable for general control operations, but it is not a precise/fast control device as seen on gaming console controllers. It often slips from a cardinal direction to the adjacent intermediate direction (i.e. from left to left+up). You should not expect a traditional "platformer" game to be played with this control without some heuristics to clean up the input.
The checkmark button (K_KP1) is far easier to click than the X button, so "click" for "fire" should likely be the checkmark rather than the X. (Which makes sense to English users, at least). The Game pad buttons are small and close enough that asking a user to rapidly switch between them will likely result in a lot of multiple-button push events. Again, some heuristic code would be needed to clean up the input.
Cursors
See Sugar Standard Icons for instructions on how to create and use a standard Sugar cursor within Pygame-based games. The arrow and hand cursor there should likely be sufficient for most games.
Antialiased Lines
The bit-depth chosen by the wrapper (16 bit on OLPC-XOs, normally) tends to make antialiased lines fail. The reason this is so hasn't been extensively investigated, so it's not known whether this is a hard-and-fast limitation, or just an optimization hint.
Journal Integration
OLPCGames produces events to tell you about Journal save/restore requests:
- pygame.USEREVENT
- code = olpcgames.FILE_READ_REQUEST (OLPCGames 1.6+)
- filename -- the filename to be read
- metadata -- metadata object (dictionary like)
- code = olpcgames.FILE_WRITE_REQUEST (OLPCGames 1.6+)
- filename -- the filename to be written
- metadata -- metadata object (dictionary like)
- code = olpcgames.FILE_READ_REQUEST (OLPCGames 1.6+)
which are generated to allow you to save/restore to/from the Journal/Datastore on the OLPC.
Window Resizing
Window resizing doesn't work yet (i.e., resizing windows via the screen rotate button), but we will try to get this working soon.
Module Reference
The pydoc-generated documentation for the OLPCGames package serves as the canonical reference work at this point.
Customizing the Toolbar
If you want to add stuff to the standard toolbar you can override build_toolbar in your PyGameActivity subclass like this:
import pygame import olpcgames from sugar.graphics.toolbutton import ToolButton from gettext import gettext as _ ... def build_toolbar( self ): """Build our Activity toolbar for the Sugar system.""" toolbar = super( MyActivityClass, self ).build_toolbar() # Add a button mybutton = ToolButton('activity-mybutton') # put the icon file here: MyGame.activity/icons/activity-mybutton.svg mybutton.set_tooltip(_('Something')) mybutton.connect('clicked', self._mybutton_cb) toolbar.insert(mybutton, 2) mybutton.show() return toolbar def _ mybutton_cb(self, button): pygame.event.post(olpcgames.eventwrap.Event(pygame.USEREVENT, action='mybutton'))
Then in your event handler do this:
if event.type == pygame.USEREVENT: if event.action == 'mybutton': self.dosomething() else: print "Unknown user event action:", event.action