Blogroll

Forums

python

Creating a game using python and Silverlight 1.1

This tutorial assumes that you have a passing understanding of Silverlight and Microsoft’s .NET technologies. If you do you should have no trouble understanding everything in this code, and chances are you will understand some of it more then I do!

Silverlight is a new Web presentation technology that is created to run on a variety of platforms. It enables the creation of rich, visually stunning and interactive experiences that can run everywhere: within browsers and on multiple devices and desktop operating systems (such as the Apple Macintosh). In consistency with WPF (Windows Presentation Foundation), the presentation technology in Microsoft .NET Framework 3.0 (the Windows programming infrastructure), XAML (eXtensible Application Markup Language) is the foundation of the Silverlight presentation capability.
This white paper will step you through the basics of Silverlight and how you can use the Microsoft stack of tools, including Microsoft Expression Blend, Microsoft Visual Studio 2005, and XAML to build rich graphical sites. First, let’s take a primer on the background leading up to Silverlight and where it stands on the development landscape.

What we are going to do is create a simple game with falling targets that they user has to click on in order to Ã¢Â€ÂœhitÃ¢Â€Â them. Each time they hit a target they will get a point and another target will be created.

Note: The DLR sample appears to have gone missing in recent SDK releases, but it can be found on the samples page.

Then copy all of the files from that folder into your SilverPy folder. Now rename the DLRConsole.xaml to SilverPy.xaml and you can get rid of the DLRConsole.py, in its place create a SilverPy.py file. This will be the file that will contain our python code.

So once you are done you should be left with the following:

Now we want to edit the index.html so that it doesn’t say that it’s the DLR sample. Edit the html code to look something like:

[code lang=”html”]

[/code]

Now since we changed the name of our xaml file we need to edit the CreateSilverlight.js file:

We simply changed the first parameter to the createObject function to be Ã¢Â€ÂœSilverPy.xamlÃ¢Â€Â so that the correct file gets loaded. CreateSilverlight.js calls Silverlight.createObject to instantiate Silverlight and tell it to load our “Silver.xaml” file, it also controls other a few other option. I don’t want to explain this in too much detail since Microsoft has already done this for me:

CreateSilverlight.js:

Defines the CreateSilverlight method, which invokes either the CreateObject or CreateObjectEx method, which are defined in the Silverlight.js file.

Silverlight.js:

Defines the CreateObject and CreateObjectEx methods, which provide version checking support and generate the tag settings in the HTML file for hosting the Silverlight control.

If you want more information on the nitty gritty of these these two files and the html file please read the Microsoft website.

The next step is to edit our XAML file, this is going to be pretty simple but I will explain it later:

This is our XAML file, it’s used to create our “GUI”. The first step is to create the Canvas element:

[code lang=”xml”]

[/code]

Defines an area within which you can explicitly position child elements by using coordinates relative to the Canvas area.

So this where we will create all of our visible objects, you can think of it like the canvas that a painter uses. You can see in the XML that we give our canvas object a name: “root_canvas”, a height, a width, and a background colour: “purple”.

The next element that you see is an x:Code element which we use to load the “Silver.py” python file. Here we could load as many code files as we wanted.

For some reason (perhaps it is explained somewhere or someone can tell me why) I could not get the “root_canvas” to send the Loaded event, so I needed to created the sub-canvas as a work-around.

The next element is a TextBlock element that is used to display (yes you guessed it) text. We use this TextBlock to display the user’s score as they play our little game.

Now it’s time to work on our python code.

If you load the index.html file into Internet Explorer (you have to use Internet Explorer as there currently are some issues with loading external python modules when running local files, I’ve read that it will be fixed soon) you will get an obscure error. This is because we have not created the on_canvas_loaded function that we described in our xaml.

So let’s add this to our Silver.py file:

[code lang=”python”]
def on_canvas_loaded(sender, e):
pass
[/code]

The first thing that you will notice about out Loaded event handler is that it has two parameters, which is common will all event handlers. Sender is the object that fired the event, and e is the event arguments associated with the event. What is contained in the event arguments changes depending on what event is being caught.

Now if you load index.html into Internet Explorer you should see the following:

So far so good! So now lets get into some serious python coding, now most of this code is simply Python/IronPython so if you are familiar with both you shouldn’t have any problem.

The fist step is to import all of the stuff that we are going to need:

Now the first thing that we do is we create a random object that we will use in the future to position our Target sprites. The next step is to save the game_canvas that was passed in to the __init__ function, game_canvas corresponds to a canvas object. Then we initialize our score to zero, this will be the number of canvas sprites that our user hits. Then we create an empty list, which will store references to the targets that are currently on the screen.

Finally we hook up our member function on_key_down to the game canvas objects KeyDown event and set our done flag to False, since we aren’t done.

[code lang=”python”]
def on_key_down(self, sender, e):
“””Check to see if they hit escape”””

if (e.Key == 8):
self.game_over()
[/code]

This handler is pretty simple, it gets called when there is a key down event on the game_canvas, if the key is the Escape key (e.Key == 8) then we called self.game_over() to end the game. I added this so that someone could quit the game at any time.

The first think that we do is create a TextBlock object, set some of its properties, and add it to the game_canvas’s children making the object visible. Then we use a helper function in the wpf module to set the position of the TextBlock object. Finally we set done to True, done is simply a boolean flag that we use to know if the game is done or not.

The wpf modules SetPosition function is actually pretty simple and we could have done this ourselves:

Another function that we called way back when in the __inti__ function is:

[code lang=”python”]
def ensure_targets(self):
#Make sure that we have enough targets
while (len(self.targets) < NUM_TARGETS):
self.targets.append(self.create_target())
[/code]
Pretty simply stuff, while the number of targets stored in our list is less then NUM_TARGETS we create and add targets to our list. NUM_TARGETS is defined as follows at the beginning of the source code:
[code lang="python"]
NUM_TARGETS = 10
[/code]
The ensure_targets function uses the create_target function to create new targets:
[code lang="python"]
def create_target(self):
#Create a new target and return it
#Init to random size and position
x = int(self.random.random()*400)
width = int(self.random.random()*30) + 20
height = int(self.random.random()*30) + 20
y = 0 - height
"""Init the velocity to a random number based off of the
score so that more targets are hit, the game gets harder."""
velocity = int(self.random.random()*(int(self.score / 15)+1))
#The minimum velocity is 1
if (velocity<=0):
velocity = 1
return TargetSprite(self.on_target_event
, self.game_canvas, x, y,width, height
, velocity)
[/code]
This might seem slightly confusing, but basically what we are doing is using our random object to randomly position and size our new target sprite. We set the x position to be a random number between 0 and 400, the width and height a random number between 20 and 50. Then the Y position to be 0 Ã¢Â€Â“ height, this is simply done to start the target off of the screen.
Next we set the velocity of the Target sprite to be between 1 and the current score divided by 15. This makes it so that the game increases in difficulty as the more targets are hit.
Finally we return the TargetSprite that we create.
Now that we have looked at how we create TargetSprites in the TargetGame class lets look at our TargetSprite object, here is the __init__ function from the Target Sprite class, notice that in create_target we pass the function self.on_target_event as the first parameter:
[code lang="python"]
def __init__(self, notify_function, game_canvas, x, y, width, height, velocity=1):
"""Initialize the target sprite.
@param notify_function - function - The function to call when an event happens.
@param game_canvas - Canvas object - The canvas that the
TargetSprite will be added to.
@param x - int - X position
@param y - int - Y position
@param width - int - The width
@param height - int - The height
@param velocity = 1 - int - How fast we are going to move down.
"""
self.notify_function = notify_function
self.game_canvas = game_canvas
self.x = x
self.y = y
self.width = width
self.height = height
self.velocity = velocity
self.add_to_canvas()
[/code]
This function is pretty easy to understand, we save a reference to each parameter and then we add ourself to the canvas that was passed in. add_to_canvas is where we actually create the ellipse that will be displayed on the screen as our target:
[code lang="python"]
def add_to_canvas(self):
"""Add yourself to the canvas"""
#Create the Ellipse
self.ellipse = System.Windows.XamlReader.Load('‘)
#Height, Width, and position
self.ellipse.Height = self.height
self.ellipse.Width = self.width
#Add to canvase
self.game_canvas.Children.Add(self.ellipse)
#Set the position
wpf.SetPosition(self.ellipse, self.x, self.y)
“””Connect out click event handler with the Let button down
event.”””
self.ellipse.MouseLeftButtonDown += MouseEventHandler(self.on_ellipse_click)
[/code]

Now here we do something interesting, we create an ellipse object using some in-line XAML code instead of creating the object directly like we did with the Ã¢Â€Âœgame overÃ¢Â€Â text.

After the ellipse has been created, we set the Height and Width, and then we add it to the canvas objects children. Then we use the SetPosition helper function in the wpf module to set the position of our ellipse.

Then we add a handler to the ellipse’s MouseLeftButtonDown event. We do this so that we know that the user has Ã¢Â€ÂœhitÃ¢Â€Â a target.

Our MouseLeftButtonDown function is pretty simple as well, we simply call the notify function that we were initialized with and tell them what happened:

Target hit is defined at the start of the program along with GAME_OVER like so:

[code lang=”python”]
TARGET_HIT = 0
GAME_OVER = 1
[/code]

These are simply defines that we use to pass into the notify function in order to tell it what event has happened.

Now we switch back to the TargetGame clasas and the notify function that we passed to the TargetSprite():

[code lang=”python”]
def on_target_event(self, event, target_sprite):
“””Called when an event is sent from the target
sprite
@param event – number – What has happened?
@param target_sprite -TargetSprite – The sprite that
has sent this event”””

if (self.done==True):
#We are already done don’t bother
return

if (event == GAME_OVER):
#The game is over!
self.game_over()
elif (event == TARGET_HIT):
“””A target has been hit
Remove the hit target from the list and from
the canvass”””
self.targets.remove(target_sprite)
self.game_canvas.Children.Remove(target_sprite.ellipse)
#Update the score and the score text
self.score += 1
Score.Text = “Targets Hit: %d” % (self.score)
#Now make sure that we have enough targets
self.ensure_targets()
[/code]

Now I hope you can see what is happening, we don’t have any game play just yet, but we have started working on the interaction between our sprites and the main game object. This function is pretty straight forward, if we got the GAME_OVER event then we call the game_over function and everything finishes.

If the event is a TARGET_HIT event then we need to remove the target that was hit from the targets list and the game_canvas. Then we need to update the score and the score text. Now this is the first time that we have referenced our TextBlock object. If you remember the XAML you’ll remember that we created a TextBlock object in order to display the score:

[code lang=Ã¢Â€ÂxmlÃ¢Â€Â]
[/code]

So in order to set the text, we simply reference the object by it’s name and set its text property:

Now because of the way that Silverlight works we cannot simply use a normal game loop, instead we use a Storyboard object which is generally used for animations. What we do is tell the storyboard object to Begin its set of animations, but since there are no animations set up, it promptly quits and the Completed event fires, which we handle with the self.on_game_timer function.

[code lang=”python”]
def on_game_timer(self, sender, e):
“””This is the main game loop that we are going to be
using.”””

#Update the sprites
for sprite in self.targets:
sprite.update()
#If we are not done, start the timer again
if (not self.done):
self.gameLoop.Begin()
[/code]

What we do when our Storyboard ends is we update all of our targets, then if the game has not finished we restart the Storyboard, and the game loops.

The update function in the TargetSprite pretty simple, it updates the position of the Target and if the target has hit the bottom of the canvas then it fires the GAME_OVER event:

Now if you’ve made it this far in the code there shouldn’t be anything surprising about this, we basically initialise our TargetGame using the root_canvas, and then we tell that game to run, that’s it.

def ensure_targets(self):
#Make sure that we have enough targets
while (len(self.targets) < NUM_TARGETS):
self.targets.append(self.create_target())
def create_target(self):
#Create a new target and return it
#Init to random size and position
x = int(self.random.random()*400)
width = int(self.random.random()*30) + 20
height = int(self.random.random()*30) + 20
y = 0 – height
“””Init the velocity to a random number based off of the
score so that more targets are hit, the game gets harder.”””
velocity = int(self.random.random()*(int(self.score / 15)+1))
#The minimum velocity is 1
if (velocity<=0):
velocity = 1
return TargetSprite(self.on_target_event
, self.game_canvas, x, y,width, height
, velocity)
def on_target_event(self, event, target_sprite):
“””Called when an event is sent from the target
sprite
@param event – number – What has happened?
@param target_sprite -TargetSprite – The sprite that
has sent this event”””
if (self.done==True):
#We are already done don’t bother
return
if (event == GAME_OVER):
#The game is over!
self.game_over()
elif (event == TARGET_HIT):
“””A target has been hit
Remove the hit target from the list and from
the canvass”””
self.targets.remove(target_sprite)
self.game_canvas.Children.Remove(target_sprite.ellipse)
#Update the score and the score text
self.score += 1
Score.Text = “Targets Hit: %d” % (self.score)
#Now make sure that we have enough targets
self.ensure_targets()
def run(self):
“””Start the game.”””
#initialize the elements
self.done = False
self.score = 0
#Our storyboard timer
self.gameLoop = wpf.Storyboard()
#Set the name property for the 1.1 refresh
self.gameLoop.SetValue[System.String](self.gameLoop.NameProperty, “GameLoop”)
#Add to the canvas
self.game_canvas.Resources.Add(self.gameLoop)
#Add the completed event handler
self.gameLoop.Completed += EventHandler(self.on_game_timer)
#Start the game loop
self.gameLoop.Begin()
def on_game_timer(self, sender, e):
“””This is the main game loop that we are going to be
using.”””
#Update the sprites
for sprite in self.targets:
sprite.update()
#If we are not done, start the timer again
if (not self.done):
self.gameLoop.Begin()
def on_canvas_loaded(sender, e):
target_game = TargetGame(root_canvas)
target_game.run()
[/code]
All in all it’s pretty simple to create something like this using Silverlight and python. Hopefully this tutorial provides enough information so that all of you can create your own game or application, if you do drop me a line I’d love to check it out.
As always if you have any questions, comments, or spot a problem with the tutorial, add a comment below.

All right francois, sorry it took me so long to get back to you but I finally figured out what the problem is and I think that everything should be working now. Give it another chance and let me know what happens.

I really like your website. I’ve created something similar, http://my-python-blog.freehostia.com/, but it will take a more formal approach. So anyway, I have a couple tutorial requests. Can you post a tutorial explaining how to make exe, dmg, bin/deb/rpm files? And can you create a tutorial about porting your pyMan game to Jython?
Thanks,
Yuval

I enjoyed your article, and am excited about IronPython and Silverlight. Keep up the good work.

The real reason I am writing though is to save your visitors extreme frustration. I clicked on the Python Web Hosting link (at the top of your site’s page: Creating a game using python and Silverlight 1.1), and was taken to http://www.micfo.com. I’ve signed up with these people about 2 weeks ago, and have experienced a great deal of pain.

Their service doesn’t work. Their systems (hosting and accounting) are terrible. They advertise a 30-day money back guarantee, yet I find myself spending valuable time trying to get them to refund my money (they claim that the transaction is not going through because of PayPal and an invalid invoice number). Well, I never used my PayPal account (used MasterCard), and the invoice number I gave them was what they had given to me.

When I signed up, it took 3 days to get my account set up – which ended up not working.

They run a joke of an operation. NEVER, in my 20 years of business have I been so annoyed or played with.

To add insult to injury, I am being spammed by their virtual server (which was supposed to be cancelled because it is missing all kinds of modules) at the rate of about 150 emails per day!

I have all sorts of emails from me to them, and their replies to back all of this up.

Please consider removing this link. It’s hurting you – for recommending them as an ISP. They are a joke – and a HUGE waste of time.

well I stole your silver game to see if it could run on the page/server I use, and well it didn’t. Took me ages to find out that you link to the SilverLight.js file by a name silverlight.js. Not all systems ignores case in filenames it seems. :D,

I realize this is most lekily a request from the client but the IE7 only issue makes your company look incompetent.Also, as you guys appear to showcase Silverlight for your client, it doesn’t make their cross platform plugin look very friendly to the web development community.

First off I would like to say great blog! I had a
quick question in which I’d like to ask if you don’t mind.
I was interested to find out how you center yourself and clear your head before writing.
I have had a tough time clearing my thoughts in getting my ideas out there.
I truly do enjoy writing but it just seems like the first 10 to 15 minutes are usually lost simply just trying to figure out
how to begin. Any recommendations or tips? Kudos!

The exhibition titled To Hell and Back, will feature a
selection of lithographs (already in the museum’s permanent collection) that takes on the epic poem The Divine Comedy, written by Dante Alighieri, and mixes in a 21st century narrative of a
‘sneaker and hoodie-clad slacker’, set in circles of Hell to Purgatory
and Paradise, both presented as versions of modern American cities such as San Francisco and Los Angeles.
It is okay to serve corn dogs for dinner once in a while.
So, it was on to the thrift shops where I did find
a decent pair of new shorts just in my size only when I got home it turned out they were my size in youth, not adult,
even though they were in the adult section.