Posts about Meetings


I don’t think you want to do that Dave…


In this session we are going to begin thinking about AI by adding a computer opponent to our number guessing game. Along the way we are going to introduce two new important concepts – logical combinations when making decisions and extending the available commands by adding a function.

The new version of the game is going to introduce a competitive element to the game by having the computer guess the code number each time the player does. Instead of the simple ‘higher’ ‘lower’ feedback we provided we will instead indicate who is closer to the answer each round. This is adds two elements to our game – a competitive tension and a clue that allows the player to improve there skill at the game with practice. We’ll discuss what makes a ‘good’ game many times over the coming sessions I am sure!

Functions

So far we have coded using commands, or functions, that already form part of the Python language and libraries. A function is a self-contained mini program that completes a calculation or task in its own right. They are very important for structuring more complicated programs and allowing you to re-use your code effectively. Functions often receive the data on which they will operate in the form of an ‘argument list’, which is passed to the function inside a pair of brackets () that follow the function name. A good example of a function is print() where you would normally pass the string of text you want to print as an argument (inside quotes and between the brackets) and the print function just does its thing without you having to worry about the ‘mini-program’ inside.

Our function is going to compare two values and return the absolute difference between them for us to use to give a clue back to the player as to their next guess. At the top of our program we start by defining the function we are going to use as follows:

def difference(x, y):
if x >= y:
result = x - y
else:
result = y - x
return result

As you can see we start with the Def keyword, our function’s name and the arguments it is going to use. The rest of the code is just a mini-program that does its stuff and returns the answer.

Once defined, we can use our function just like any pre-existing one – we’ve effectively *extended* the language, how cool is that?

I’m never going to not do the opposite…

The other new concept to introduce logical combinations in our decisions. In our new game design, we need to check if either the player *or* the computer has one.’Or’ is the first of several ways of combining questions to make a decision the other really important one is ‘and’. If you check to see if one thing is true ‘or’ another thing is true – then the decision will be triggered if either one is true. If we only want to trigger the decision if both things are true we can use ‘and’ instead.

Here is the code in its entirety, see if you can locate all the logical decisions being made and follow the flow of the program:

import random


def difference(x, y):
if x >= y:
result = x - y
else:
result = y - x
return result


answer = random.randint(1, 100)

print('I have selected a number between 1 and 100.')


# Main game loop, only exits when there is a winner using 'break'
while True:

# Lets get the player and computer guesses
player_guess = int(input('Your guess? '))
computer_guess = random.randint(1,100)
print('Computer guess is: ' + str(computer_guess))


# Has either one won?
if computer_guess == answer or player_guess == answer:
break


# See who is closest?
player_guess_difference = difference(answer, player_guess)
computer_guess_difference = difference(answer, computer_guess)


# Print a clue
if player_guess_difference > computer_guess_difference:
print('The Computer is closer...')
elif player_guess_difference == computer_guess_difference:
print('You are both neck and neck')
else:
print('You are closer. Nearly there...')


# We get here once we break from the loop, but who has won?
if computer_guess == answer:
print('You LOSE. Sorry, it seems your computer is smarter than you.')
else:
print('You WIN. Phew, you are still the boss!')

 

Don’t forget: half-term next week, so no computer club during the holiday!

That’s Numberwang!


GCC was *packed* last week and it was great! We covered a lot of ground with many key elements of coding that we will find ourselves using time and time again.

The key things we discusses were:

  • Repeating sections of code – the while loop
  • Decisions – if, elif, else & the while loop logic
  • Converting data types – str() & int()
  • The ‘main game loop’ structure

I will run through these again in our next session, but in the meantime here is  Nick’s code in its entirety for you to look at. Can you follow the flow of the code and pick out each element from the list above?

import random

answer = random.randint(1, 100)

print('I have selected a number between 1 and 100.')

lives = 3
while lives > 0:
      guess = int(input('Your guess? '))
      if answer == guess:
            print('Correct')
            break
      else:
            lives = lives - 1
            print('Incorrect, try again')
            if guess < answer:
                  print('Too low!')
            else:
                  print('Too high!')

if lives == 0:
      print('Sorry, you have no more guesses.')

For comparison, here is a near identical game created with slightly different underlying logic. Can you follow the flow of this code? Can you highlight the differences and the similarities between the two approaches to the same problem? It is interesting to see that there can be many different code solutions to the same problem.

import random

tries = 6
attempts = 0
secret = random.randint(1, 99)
guess = 0

print("Access DENIED: This system is locked with a code between 1-99")

while guess != secret and attempts < tries:
    remaining = tries - attempts
    print("You have " + str(remaining) + " remaining attempts") 
    guess = int(input("Enter Code Now..."))
    if guess < secret:
        print("Code FAIL: Too low. Don't you know any bigger numbers?")
    if guess > secret:
        print("Code: FAIL: Too high. Calm yourself & try a smaller number")
    attempts = attempts + 1

if guess==secret:
    print("Confirmed!")
    print("Access ENABLED: Please proceed to Nuclear Missile launch control. Have fun...")
else:
    print("Access LOCKED: Black helicopters have been dispatched to your location.")
    print("Before you are terminated, you may as well know that the code was " + str(secret))

Next session we will look at competing against our first computer opponent!

GCC Starts Tomorrow!


Logo v2 RGB smI hope everyone has had a fantastic break over the summer. We will be kicking off the first session of GCC tomorrow evening (Wednesday 14 September, 19:00-20:00) but *please note* we will be in L27 this year (where we had the last couple of session of last year).

We have some cool new stuff to get into this year. As well as extending you python skills, we should have access to Unity 3D game development using the new computer suite. I’ve also entered us into the PiWars competition early next year (1 April, 2017 in Cambridge). This will involves us designing, building and programming an autonomous robot to compete in various challenges. Plenty of interesting stuff for everyone! See you there…

News Flash: GCC moved to L27


imageLast minute change of location for tonight’s meeting because of work going on our usual computer lab.

GCC will be in L27 (just around the corner) from 19:00 – usual time.

Please bring your own laptop if you can because we won’t have access to our usual school PCs.

Crash, Bang, Wallop! – Collisions


imageThis session we are going to add collision detection between our particles and make them ‘bounce’ off one another. This will actually come down to 3 separate tasks.

Firstly we need to know when to particles have collided (collision testing, or detection).

Secondly we need to change the behaviour of the particles to respond to the collision - for now this will simply be to make them bounce apart.

Thirdly we need to catch a tricky problem common to all physics simulations – stickiness! Because we will be testing for collisions at discrete moments in time (whenever our collide function is called in the main game loop) there are going to be occasions where we have just missed the actual moment of collision. At this point our particles will be overlapping and begin to repeatedly trigger our collision test make the particles ‘bounce’ constantly in opposite directions each call – the visual effect of this will be to make the particles appear stuck together. To avoid this problem we will also add a bit of code to ensure that, in addition to causing the particles to bounce, we also make sure we remove any overlap.

Checking all the particles once, but only once!

Lets start with how we are going to call our collision code. Naively, for each particle we could just insert a second loop to check against the particle list particle by particle – but wait… A simple nested loop will actually check every *pair* of particles twice – A with B, and then later B with A. We want to avoid this duplication as well as avoid checking particles against themselves – A with A and B with B etc.

To achieve this we will use a modified internal loop that only checks the particle against the other particles *that are later in the list*. To do this we need to modify the way we loop through the list in the outer loop so that we can keep track of the position in the list of the particle we are currently checking. Find the existing update-draw loop for each particle and replace it with this code:

    for i, particle in enumerate(my_particles):

        particle.move()

        particle.bounce()

        for particle2 in my_particles[i+1:]:

            collide(particle, particle2)

        particle.display()

Can you see how this works? Enumerating the particle list means that the variable i will keep track of the index of each particle in the list as we iterate through it. We can use i to begin the nested for loop from 1 place further on in the list than the particle being tested. The function that we call – collide – will test to see if the pair of particles are in collision and act upon them if they are. Let’s look at that next.

Collision Testing & Handling

The collide function should be added under the existing findParticle function, before the Particle class definition. Here is the collision code in its entirety:

def collide(p1, p2):

    dx = p1.x - p2.x

    dy = p1.y - p2.y

 

    dist = math.hypot(dx, dy)

    if dist < p1.size + p2.size:

        tangent = math.atan2(dy, dx)

        angle = 0.5 * math.pi + tangent

 

        angle1 = 2*tangent - p1.angle

        angle2 = 2*tangent - p2.angle

        speed1 = p2.speed*elasticity

        speed2 = p1.speed*elasticity

 

        (p1.angle, p1.speed) = (angle1, speed1)

        (p2.angle, p2.speed) = (angle2, speed2)

 

        # Move overlapping particles apart

        separation = (p1.size + p2.size - dist) / 2.0

        x_separation = math.sin(angle) * separation

        y_separation = math.cos(angle) * separation

        p1.x += x_separation

        p1.y -= y_separation

        p2.x -= x_separation

        p2.y += y_separation

Initially you can see that we simply find the difference in x positions (dx) between the particles’ centres and the difference in y (dy). We use basic Pythagoras to find the distance between the centres of the particles (the hypotenuse of the dx dy triangle).

The collision test is now simply to see if this distance is less than the combined sizes of the particles – this should all be familiar to those of you who worked through the asteroids game.

If there is a collision, we change each particle’s movement angle and make them appear to bounce – I will go through the theory in the session.

Parting is such sweet sorrow

Finally, we need to deal with the ‘sticky’ problem of intersecting. If, at the moment we check them, the particles have already overlapped, then simply changing their speeds and angles is going to leave us in a tricky state. On the very next loop around our code, and despite the fact that they are now moving apart, we may well find them in collision again (because they were overlapped) and ‘bounce’ them again… and again… and again. This causes a very obvious glitch. To solve it we must ensure that we not only leave our particles moving apart, but also ensure that they are forcibly separated before the next time around the loop. We do this in the code by finding the separation required to ensure that the particles are *not* overlapped and then we simply force their positions apart by setting the particles’ x y values directly.

Once you have the code working, try commenting this section of code out and see what happens without it, Can you see and understand why the problem occurs? It’s a good one to think about as similar issues can often turn up in timing, or sampling, loops.

Projects for the Holiday

Hopefully you can see that you have an interesting ‘engine’ for a whole variety of games at the end of this session – see what you can do with it over the summer and I look forward to playing some of your games next term.

Welcome back!


imageThis term we are going to add a touch of class to our python coding. This year we have learned how to create, store and retrieve data of different types. We have also learned out to manipulate and transform this data using functions. Often in our projects we have had to use the global keyword to ensure that our functions are all operating on the same items of data (not just creating a copy of the data which is only used within the function). Wouldn’t it be useful if the data and functions for a particular entity (or object) in your game could be kept together, without having to pass the data, in a global form, explicitly between each function call? It would also be great if you need lots of these entities, the functions would ‘know’ which object instance they were acting on?

For example, we want lots of aliens in our game. Each alien will move using the same logic and be destroyed in a similar way by our missiles, but each will have its own position and perhaps its own sprite image? In other words, we want to reuse the same functions, but on different data for each alien object instance.

The final data structure we want to tackle this year capable of achieving this, and other very useful features and it is called a Class. To introduce classes we are going to work through the following exercise:

https://www.codecademy.com/courses/python-intermediate-en-WL8e4/0/1

Summer Term Sessions Start this Week!


I hope you’ve all had a great Easter break and will have plenty of coding projects to discuss! We’ll be beginning something completely new this term so don’t forget:

Wednesday 13 April @ 7pm

See you there!

Quick Skills Builder


After last week’s Python refresher, those of you who would like to consolidate the topics we covered can have a crack at this guided tutorial:

https://www.codecademy.com/courses/python-beginner-en-4XuFm/0/1

We’ll be on hand to discuss the interesting bits and at the end you should have your first fully functional version of a very simple Battleships game – ready for some enhancements and customisation if you finish it with time to spare! More importantly though, you should also have the set of coding concepts we will put to further use with some graphics and pygame next week.

GCC Restarts This Week (Feb 24 2016)


Lets get the second half of this term underway with a bit of game coding! I’ll be starting a Python Crash Course (or ‘Refresher’, depending on your perspective) for those who are new to the club, preparing for their GCSE project, or would just like to brush up on their skills.

For everyone else, there is still plenty of interesting stuff to do on building your game levels or extending the game engine for YumikosAdventure. I’ve noticed a couple of people have branched the github repository, so I’m interested to see what you have been up to over the half-term break. See you there. E

GCC 2016 is here!


The first session of the New Year will be this Wednesday:

Wednesday 13 January @ 7-8pm

We hope you all had a great Christmas break with plenty of coding and gadgets to tell us about when you get there. We are planning a very cool tiled-map based game this term using the excellent Tiled map editor. If you want to get a bit of a head start, you should take a look at http://www.mapeditor.org/ and there is an tutorial video here: https://www.youtube.com/watch?v=ZwaomOYGuYo

GCC is Back This Wednesday!


Everything is up an running with shiny new versions of Python, pygame and our other game dev tools. Make sure you join us for 7-8pm in L24. We’ll start the term off with a bit of retro-gaming on the RPi’s for a bit of fun and inspiration. Nick is working hard on a brand new game framework for us to work through and we’ll also be making extensive use of Codeacademy this term to hone our python and javascript game coding skills.

Don’t forget to bring along your own laptops and Pi’s if you want, and we’re looking forward to seeing what projects you’ve been up to over the break. See you there! E

No Games Creators Club again this week :(


There will be no session this week on Wednesday 23 or 30 September due to on-going difficulties with the computer lab & the school open evening. We *hope* that normal service will resume soon, currently we're aiming for:

Wednesday 7 October– 7-8pm.

But watch this space please…

Sorry about the delay in getting back up an running.

Welcome back to Games Creators Computer Club 2015-16!


It’s been all change in the computer lab over the summer, so we can look forward to a big update to the computers we use at the club. We’re still waiting to hear when this work will be complete and our games tools will be ready to roll again, so unfortunately there won’t be a club session this week (no session on Wednesday 16 September). But watch this space and we’ll have things underway as soon as we can. Lots of new, fun  and interesting stuff ahead…

Clue’s in the name…


Right, this game is called ‘Asteroids’ so it is about time we added some ruddy asteroids! Hopefully you are already thinking about the 3 bits we are going to need – a list of data structures to hold the information about each asteroid, code in the Update function to change the position/rotation each frame and then some matching code in the Draw function to blit an image according to these positions and rotations. That is what you were thinking isn’t it…?

Data & Initialisation

First at the top we’re going to create a global list of asteroids: asteroid_list (globals can be easily accessed from within out functions). We will also need to add asteroid_list to the list of globals declared at the beginning of each function that we want to access it: GameReset(), UpdateGameScreen() and DrawGameScreen().

Next we need to load an asteroid image – this week we’ll only be creating the largest asteroid, but later on we’ll be ‘fragmenting’ our asteroids into smaller parts. For now we can add the following to GameInit() function:

asteroid_surface = pygame.image.load("Images/Asteroid.png")

Now the GameReset() function will need to empty out any previous asteroids in our list and refill it with shiny new ones in random positions. To access the python random number generator we need to import it right at the top of your code, so add random to the list of imported modules.

Now we can add the following list initialisation:

    # Delete and recreate the asteroid list

    # with big asteroids (size 1)

 

    asteroid_list = []

    for index in range(3):

 

        # velocity in the range -0.3 to 0.3 for x and y

        initial_velocity = [random.randint(-30, 30) / 10, random.randint(-30, 30) / 10]

        # position randomly along the top edge of the screen

        initial_position = [random.randint(0, screen_size[0]), 0]

 

        asteroid = {

            "size":1,

            "rotation":random.randint(0, 360),

            "velocity":initial_velocity,

            "position":initial_position,

            "active":True

            }

        asteroid_list.append(asteroid)

Updating Positons & Rotations

At the end of your UpdateGameScreen() function, you add the following to move, wrap-around and rotate each asteroid, each frame:

    for asteroid in asteroid_list:

        if asteroid["active"]:

            asteroid["position"][0] += asteroid["velocity"][0]

            asteroid["position"][1] += asteroid["velocity"][1]

 

            if asteroid["position"][0] < 0:

                asteroid["position"][0] = screen_size[0]

            if asteroid["position"][0] > screen_size[0]:

                asteroid["position"][0] = 0

            if asteroid["position"][1] < 0:

                asteroid["position"][1] = screen_size[1]

            if asteroid["position"][1] > screen_size[1]:

                asteroid["position"][1] = 0

 

            asteroid["rotation"] += 1

Drawing

Finally the following can be added to DrawGameScreen() to draw our asteroids. It is essentially the same process as the ship and uses the centre point of the image as our ‘origin’ so that the rotation (and resulting changing size of the image) doesn’t affect our blitting:

    for asteroid in asteroid_list:

        if asteroid["active"]:

            rotated_surface = pygame.transform.rotate(asteroid_surface, asteroid["rotation"])

            surface_centre = rotated_surface.get_rect().center

            draw_centre = [asteroid["position"][0] - surface_centre[0], asteroid["position"][1] - surface_centre[1]]

            screen.blit(rotated_surface, draw_centre)

OK, you should be good to run and see some asteroids ‘drifting’ across the screen. Go back to the update function now and play about with changing parameters to see their affects – in particular, have a go at making the rotation rate variable and random too.

Don’t forget that GCC is back on!


GCC restarts this week – Wednesday 25th February. Usual time, Usual place. I hope you had a good break but managed to cram in plenty of coding. This week we’ll be continuing to work on our asteroids game. Plus my Raspberry Pi 2 finally arrived, so we can give it a test drive!

See you all there.

E

Let’s Get Moving


In this last session before half-term we’re going to add movement to the player ship. We want this to have a physical feel to it so, unlike in previous games, we’re going to use a simple thrust and drag model to adjust the ship’s velocity instead of moving it directly. This should give us the feeling that the ship is a physical object and it will accelerate and decelerate rather than just instantly move – it should also mean you can slide and ‘skid’ a bit, which will make the asteroid avoidance a little more challenging Winking smile

You will probably already realise that this is in no way how a spaceship would really behave in space (although with a couple of tweaks to our code we could make it so) but instead this is a conscious bending of reality to create interesting gameplay. Nearly all the games that you know will ‘muck’ with reality to produce good gameplay – otherwise you are into the world of simulators instead.

New Globals & Update Code

At the top of your code, we need to add the globally accessible variables to hold the new ship data:

global ship_velocity, ship_thrust, ship_drag

 

And we’ll need to give these a starting value in the GameReset() function. To modify global variables from within a function you will need to add them to the list of global variables declared at the beginning of the function – this lets python know that you intend to modify the shared global variables with these names, not create local independent copies (which is the default):

def GameReset():

    global ship_position, ship_rotation, ship_thrust, \

    ship_drag, ship_velocity

 

    ship_thrust = 0

    ship_drag = 0.95

    ship_velocity = [0, 0]

Now all of the rest of our changes will occur in the UpdateGameScreen() function. Once we set the new ship position here, the rest of the game, including the ship drawing, can be left entirely as it already is. This is one of the key benefits of organising our game code into relatively self-contained functions. So in UpdateGameScreen we can read the up arrow key state immediately after our existing key reading code to modify the new thrust variable – up and down:

    # If the up-arrow key is held down, increment the thrust

    # If it is released, set the thrust back to zero

    if current_keys[pygame.K_UP]:

        ship_thrust -= 0.05

    else:

        ship_thrust = 0

So far, so good but the next bit is a bit trickier and will require some explanation during the session. The movement our ship needs to make is determined by an angle (the ship’s rotation) and a distance (set by the thrust value). But how do we turn this into the X and Y coordinate change that we know we need to position the ship in its new location? The answer is that we need a bit of trigonometry. The sin of the angle (which must be converted from degrees to radians) will give us the X component of our movement, and the cos of the angle will give us the Y component.  If you have not done this yet in maths, then don’t worry I will explain at the session but just go ahead and add this code for now:

    # Calculate the change in velocity - stored in the form [X,Y]

    # For the X direction:

    ship_velocity[0] += ship_thrust * math.sin(math.radians(ship_rotation))

    # And the Y direction:

    ship_velocity[1] += ship_thrust * math.cos(math.radians(ship_rotation))

Now we need to apply a multiplier to represent drag (even though we know that there would not be any in the vacuum of space!). This factor reduces the velocity proportionately each frame. Using a proportionate reduction is a useful technique because it slows the ship more at high speed and as the speed gets down towards zero it slows it less and less – this ‘feels’ like drag, which behaves similarly in the real world. When the velocity reduces to near zero, then the multiplier has virtually no effect and we don’t have to worry about preventing the velocity turning negative – which we would if we simply subtracted a fixed velocity each frame.

    # Reduce the total velocities proportionally by a drag factor

    ship_velocity[0] *= ship_drag

    ship_velocity[1] *= ship_drag

Finally we can update the ship’s position using our calculated velocity:

    # Update the ship's position using the [X,Y] velocities

    ship_position[0] += ship_velocity[0]

    ship_position[1] += ship_velocity[1]

You should be able to F5 and try out the new thrust at this point, but don’t fly off the edge of the screen!

Wrap Around

To sort out the ‘flying off into the abyss’ problem we need to add some wrap around code. You’ve probably seen this sort of thing in one of our previous games, but essentially we need to check the position of the ship each loop and ‘fix’ it if it has moved outside of our screen bounds. In Pong we simply prevented movement beyond the screen bounds, but here we are going to use a technique called ‘wrap around’. When we detect the ship has moved off one edge of the screen we’ll automatically move the ship back to the opposite side. The code looks like this:

    # Wrap around the ship position if necessary

    if ship_position[0] < 0:

        ship_position[0] = screen_size[0]

    if ship_position[0] > screen_size[0]:

        ship_position[0] = 0

    if ship_position[1] < 0:

        ship_position[1] = screen_size[1]

    if ship_position[1] > screen_size[1]:

        ship_position[1] = 0

Give it a go now and see what you think of our ‘physical’ movement model. Try changing the drag value from 0.95 to 0.99 or 0.9 and see how that affects the movement. Can you work out what 3D surface we are simulating with this sort of wrap around – clue: it’s not a sphere…

Ship Rotation Using Transforms


image

Previously we have ‘turned’ our sprites by swapping the sprite for completely different images for each facing direction. For this project we are going to use an alternative method. We can actually rotate just one sprite image within our code - using a ‘transform’. The result of this operation will be to create a new image surface containing our sprite orientated at new angle.

A Bit of Background

But lets start by adding a background image and the ship image as global objects (so that we can access them from within our functions). We need to declare these at the top of code using the global keyword.

global background_surface, ship_surface

We will need to load the images just once when our program first starts, so we can use the GameInit() function we created last week to add the following:

def GameInit():

    global screen, background_surface, ship_surface

    background_surface = pygame.image.load("Images/Background.png")

    ship_surface = pygame.image.load("Images/PlayerShip.png")

 

    pygame.init()

    screen = pygame.display.set_mode((640, 480))

Now we are going to need two more global ‘data’ variables – ship position and rotation – so add these at the top of your code.

global ship_position, ship_rotation

ship_position = [320, 240]

ship_rotation = 0

Next we are going to create 2 crucial new functions UpdateGameScreen() and DrawGameScreen(). These will be called once every time we cycle around our game loop. The Update function is where we will handle player input (such as reacting to key presses or mouse movements), we’ll update the game data associated with our game objects (player position, for example), check for collisions and carry out any other game logic (like checking if a level is complete). Then the Draw function will simply deal with constructing the screen display from our drawable game objects – background, sprites, score text etc.

By keeping these parts of our code nicely separated we should find it much easier to navigate around and maintain our code as it grows.

def UpdateGameScreen():

    global current_keys, ship_rotation

    if current_keys[pygame.K_LEFT]:

        ship_rotation += 2

    if current_keys[pygame.K_RIGHT]:

        ship_rotation -= 2

Now transforms are a great method for rotating, scaling and mirroring an image on the fly but they do have a ‘gotcha’. The orientated sprite will require a bigger image rectangle to contain it, so we will need to focus on a single centre point rather than relying on the bottom left corner. Our draw function needs to offset the position coordinate of the player ship using the centre of the rotated surface each time we draw. We use the centre point as our origin because, unlike a corner, it will remain consistent relative the image of the ship – try drawing the sprite using the rotated surface rectangle directly instead of the draw_centre calculation to see the problem for yourself.

def DrawGameScreen():

    global screen

    screen.blit(background_surface, (0,0))

 

    rotated_surface = pygame.transform.rotate(ship_surface, ship_rotation)

    surface_centre = rotated_surface.get_rect().center

    draw_centre = [ship_position[0] - surface_centre[0], ship_position[1] - surface_centre[1]]

    screen.blit(rotated_surface, draw_centre)

Finally we need to call our new update and draw functions (remember, at this stage we have only defined them, we haven’t actually called them), which we do inside of the GameLoop function:

def GameLoop():

    global current_keys

    pygame.time.Clock().tick(60)

    for event in pygame.event.get():

        if event.type == pygame.QUIT:

            pygame.quit()

            sys.exit()

 

    current_keys = pygame.key.get_pressed()

 

    UpdateGameScreen()

    DrawGameScreen()

   

    # flip the screen to show our drawing

    pygame.display.flip()

You should now be able to rotate your ship using the left and right arrow keys. Next time we’ll look at how to implement some movement thrust.

Functions – The Building Blocks of Code


GameLoop() walks into a bar to order a beer, but barman says “Sorry, you’ll have to use the function room.”

This term we’re going to start work on a new game. Much of the individual snippets of code will be the familiar from Pong. But this time we are going to structure the game quite differently by breaking it down into component parts, each of which perform a specific set of tasks. These sets of tasks are called functions.

Functions are a vital way of re-using bits of your code at different points in your program without having to write the code again and again. They also allow you to ‘see’ your program’s structure more clearly. Most of you will have already produced code that is getting too long for you to easily find where the bit you need to edit actually is. By dividing your code into functions you can find the bits you need more easily, and each bit of your code can be isolated and tested independently of other areas to try and eliminate errors more easily.

We’re going to start by defining two functions – one to initialise the game (GameInit), and a second to do one iteration of our game loop (GameLoop). Then we will call these functions to actually run the game.

Functions must be defined first – this does not actually run the function, it just lays out the code that will run when the function is call. In Python we do this using the def keyword then indent the code that forms the function definition in the same way that you would for a loop or conditional.

defFunctionName():

  # code goes here in an indented block

The brackets () after the function name allow us to pass some variables to the function to work with. For now we will not need to pass any values because we will use globally accessible variables, but when you define and call the function you will need to include the brackets – despite the fact they are empty – as this allows Python to know that it is a function, not a variable, that you are referring to.

So here is our standard game layout (with all the same code snippets as we needed for Pong), but structured using functions:

import pygame, sys

 

# == (1) Create 'global' variables ==

# These are variables that every part of your

# code can 'see' and change

global screen, current_keys

 

# == (2) Define the functions for each task first ==

 

# == GameInit ==

# Put initialisation stuff here

# it is called just once

# at the beginning of our game

defGameInit():

    global screen

    pygame.init()

    screen = pygame.display.set_mode((640,480))

 

# == GameLoop ==

# Put things that have to occur repeatedly

# here. It is called every frame

defGameLoop():

    global current_keys

   

    # Lock the timing to 60 frames per second

    pygame.time.Clock().tick(60)

   

    # Check for exit

    for event in pygame.event.get():

        if event.type == pygame.QUIT:

            pygame.quit()

            sys.exit()

           

    # update our key states

    current_keys = pygame.key.get_pressed()

   

    # GameUpdate functions will go here

    # GameDraw functions will go here

   

    # flip the screen

    pygame.display.flip()

 

# == (3) Call the functions to run the game ==

# We have only *defined* our functions above.

# Here we actually call them to make them happen

GameInit()

whileTrue:

    GameLoop()

 

Hopefully you can already see that this makes it easier to understand, find & edit the bits of code do each task. As our game progressively becomes more complicated this will be very important!

Have a Happy Christmas and New Year!


imageNext GCC session will be Wednesday 14 January 2015! I hope you all have a great holiday and get lots of gadgety-gizmos to show us all in the New Year. And don’t forget – as far as we’re concerned if you can’t program it, you don’t own it.

E

Adding a Title Screen – Introducing Game States


In this session we are going to add a simple start screen. This will show a title image and text and the game will only begin when the spacebar is pressed.

To do this we are going to think about the concept of ‘Game States’ that is, making your game loop do different things depending on what state your game is currently in – waiting to start a game, game in progress, game just ended, game paused etc. Pretty much any game you can think of must manage states like these – from the simplest mobile app to the top end XBox AAA title.

The basic management of game state is simple. We need to create a variable that holds a number which represents each state we want: 0 = game start, 1 = game running etc. Inside our game loop we can now check what game state we are in from this number and run different update + draw code for each case. To swap game states, we simply change the value of this number, Simples!

Adding the game state variable

At the beginning of our game (before the game loop starts) add the following variable:

# Game State

# 0 = Game Start Screen

# 1 = Game Running

game_state = 0

Now we need to go into our game loop and make our game-play specific update and draw code only run if the game_state variable is equal to 1. If it is 0, then we will add some new update (check for spacebar press) and drawing (blit some instructions) code for the game title screen instead.

We can do this check neatly with an if..elif conditional. The elif is short for (else if). While you can only pair an if check with only one else, you can have as many elif’s as you like. This is what we need because we can use this system to check for as many game state values as we need. Just after the code where we fill the pressed_keys list (everything in the game loop before then will be needed regardless of the game state we are in) add your new conditional check:

    # === Update Section ====

    # According to game state

    # Game start state

    if game_state == 0:

        if pressed_keys[pygame.K_SPACE]:

            game_state = 1

    # Game running state

    elif game_state == 1:

        ...

        ...

The dots indicate where your previous code should go; be very careful with your indenting – remember, this is what determines which code is ‘inside’ each conditional block!

This code now runs two different updates, depending on the game state. In state 0 (game start screen) we just check for the spacebar press and set the state to 1 (game running) if we detect it. In game state 1, we just run our normal game update code as previously. We can repeat this later in the game loop for the drawing code too:

    # According to game state:

    # game start screen

    if game_state == 0:

        title_text = score_font.render("Press Spacebar to Start", 1, (255,255,255))

        screen.blit(title_text, (width/2 - title_text.get_width()/2, height/2 - title_text.get_height()/2))

    elif game_state == 1:

That’s it! Now you have conditional update & draw sections to your main game loop.

Next steps

First, have a go at improving your title screen:

  • Blit a background image
  • Create some new font objects with different sizes
    • Add your game’s name as a title in a bigger font
    • Adding some instructions in a smaller font

Now have a go at adding a Game Over game state and screen if either player manages to score 3 points more than their opposition.

Text &amp; Fonts


In this session we’re going to add two scores to the game – one for each player. To do this, we are going to need to understand how to print text on the screen within our pygame app. We have used the print command previously in non-graphics (console) apps, but unfortunately this will not work inside our constantly looping pygame graphics window. Instead we need to render the text like a sprite and this will require several steps.

First we need to choose a font – the character style – that we want. We can used any installed system font that we like. You will probably already be familiar with the concept of the system font list from the down list in your word processor – such as Microsoft’s Word. Using pygame we can load in pretty much any font that you see in this list, but we do need to know the exact name of the one that we want.

Listing the available system fonts

In Idle you can quickly create a list of the available fonts on your system using the following code snippet:

import pygame

pygame.init()

for font in pygame.font.get_fonts():

    print(font)

 

This will create a long list of font names for you to check against. For the purposes of this tutorial, I am going to choose ‘jokerman’ but you can choose whichever font name takes your fancy!

Creating a font object

Just like images that we use as the basis of our game sprites, we now need to create a font object in our game project that we can use for rendering. At the beginning of you game (before the main game loop) add the following:

# Create a font to render the score

score_font = pygame.font.SysFont("jokerman", 40)

This creates a pygame font object called ‘score_font’ based on the jokerman font at a size of 40 points (just as it would be in Word, this is fairly big text).

Rendering some text with the font object

In order to render some text on the screen we need to use our new font object to create a renderable surface object (like the ones we create from our sprite images). It is during this process that we can also set what the text is that we actually want to render, as well as its colour. We are going to need to do this with the current score each time we go around the game loop in the same area of your code that you are blitting the sprites - make sure you do it *after* your screen.fill(..) command or you will just erase it each time before you see it!

score_text = score_font.render("Score text here!", 1, (255,255,255))

The score_text object now contains a surface with out text rendered into it. The first argument is the actual text we want to write, the second argument enables anti-aliasing (smotth edges to the text letters) and the third argument is a colour in the usual (Red, Green, Blue) format. In the example above RGB are all at their maximum values, so this is the colour white. We can now blit this score_text surface object to the screen in the usual way.

screen.blit(score_text, (0,0))

The blit call is just the same as with the sprite objects so the second argument is a pair of X,Y coordinates indicating where on the screen we are going to position the text.

Onwards and Upwards

You now should have the basic tools you need to render text using any font, in any size or colour, wherever you want on the screen! Next you need to create two score integer (whole number) variables that you can add to as the game progresses and use them as the source of your text when creating the score text for blitting. Don’t forget you will need to convert your score integers into strings (word form) at this point:

score_text = score_font.render(str(score1), 1, (255,255,255))

Have a go at:

  • Listing the system fonts using Idle
  • Creating two score integers
  • Rendering two scores at the top of the screen on the left and right for each player using a font object

Lets make a Pong!


Now we have something bouncing, this can form the basis of a classic game – Pong – in which two players control a ‘paddle’ to bat the ball backwards and forwards. The ball will continue to bounce on the top and bottom of our screen, but we will need to make it only bounce at either end if the player manages to get their paddle to it. If they miss, then we need to chalk up a score.

Obviously we need to break this down in some bite-size chunks, so in this session we are going to start by simply:

  • adding a paddle image
  • moving it under arrow key control
  • checking it for collisions with ball

We’ll modify the bouncy game you started last week, so begin by loading that into IDLE and re-saving it under a new name – “pongy.py”

Adding a Paddle Image

Just as we did for the ball sprite last week, we need to create our paddle.png file using paint.net. Make the image 64x196 pixels big, and save it in the same location as pongy.py. Now we can load the image into a surface object (which we will call ‘paddle1’) and store the rectangle bounds of this image in another recatangle object called ‘paddle1rect’. Add this code *before* the game loop:

# Same again for our player1 paddle

paddle1 = pygame.image.load("paddle.png")

paddle1rect = paddle1.get_rect()

Moving the paddle under keyboard control

There are two stages to this. First, each time we go through our game loop, we need to store a list of the currently pressed keys. Then we need to check if the keys we are interested in (arrow up & down) are in the pressed key list and, if so, move the paddle rectangle accordingly. This code goes at the beginning of the game loop:

    # Get the current state of all the keyboard keys

    pressed_keys = pygame.key.get_pressed()

 

    # If the up or down arrows are pressed, move the paddle rect

    if pressed_keys[pygame.K_UP]:

        paddle1rect = paddle1rect.move(0, -1)

    if pressed_keys[pygame.K_DOWN]:

        paddle1rect = paddle1rect.move(0, 1)

Checking if our paddle has moved off the screen

If the paddle’s top is at the top of the screen, or its bottom has reached the bottom of the screen, we need to reset it back to a position within the screen edges:

    # Reset the paddle position if it is beyond the top

    # or bottom of the screen

    if paddle1rect.top < 0:

        paddle1rect.top = 0    

    if paddle1rect.bottom > height:

        paddle1rect.bottom = height

Draw the paddle image using the paddle rectangle

Now the paddle rectangle is in the right place, we need to use it to blit our paddle image to that position. We do this a the end of the game loop before the display is flipped:

    # Blit the paddles

    screen.blit(paddle1, paddle1rect)

What next?

Well, now see if you can add paddle 2 for the other player and we have the beginnings of a proper game!

Graphics time!


Over the last few weeks we’ve hopefully go the hang of variables (of different types), control structures (if, then etc.) and loops (while, do). Now it is time to put all these elements together to produce our first animating graphical display. In this example we are going to initialise pygame, create a window and bounce a sprite around inside it. The sprite can be any png image that you can create using Paint.net.

You will need to create a small-ish sprite image (say 64 x 64 pixels) and save it to the same location as the python file you are working on. Call the image file ‘sprite.png’, or modify the pygame.image.load png’, or modify the

The source code below is heavily # commented to explain each line. Do read these comments to help you understand what each bit is doing, but you don’t need to type the comment lines in yourself!

import sys, pygame

pygame.init()

 

# Chain together some variables

# to store the screen size

size = width, height = 1024, 1024

 

# Create a velocity List variable to store

# movement values for [X, Y] in one place

velocity = [1, -3]

 

# Create a Tuple to hold the red, green and blue

# values to pruduce black (i.e. 0 for each)

black = (0, 0, 0)

 

# Create the main game window and store it

# in a variable called 'screen'

screen = pygame.display.set_mode(size)

 

# Load the image we want to bounce

# and store it in a variable called 'sprite'

sprite = pygame.image.load("sprite.png")

 

# Get a bounding rectangle from the sprite

spriterect = sprite.get_rect()

 

# Loop forever

while True:

    # Check the event list for a 'quit' event

    for event in pygame.event.get():

        if event.type == pygame.QUIT: sys.exit()

 

    # Move the bounding rectangle according to the

    # current velocity

    spriterect = spriterect.move(velocity)

 

    # Check of the bounding rectangle has touched the

    # edges of the window.

    if spriterect.left < 0 or spriterect.right > width:

        # Yes - touching sides of screen so reverse X

        # velocity value

        velocity[0] = -velocity[0]

    if spriterect.top < 0 or spriterect.bottom > height:

        # Yes - touching top/bottom of screen so

        # reverse the Y velocity value

        velocity[1] = -velocity[1]

 

    # Draw everything

    # First clear the screen

    screen.fill(black)

    # Blit the sprite to the current rectangle position

    screen.blit(sprite, spriterect)

    # Flip the screen we've just drawn to the front

    pygame.display.flip()

# end of while loop - go round the loop again!

*No* GCC This Week (1 Oct)


Because of the school open day there will be no GCC session this week. But fear not, for we will be back to game coding as normal next week (Wednesday 8 Oct @ 7pm sharp)! See you then.

Minecraft modding… take 2


creeperThis week is the last session of the year! We will be doing more minecraft modding using Javascript. We will have a common server running for you all to connect to, but please remember to bring your own laptop with minecraft on it if you can, but before you do please:

  • Download minecraft 1.6.4
  • Create a new profile that runs in this version
  • Disconnect your laptop from the internet and make sure you can still run under this profile

I will be setting up our own GCC wireless network and we will also talk a bit more about how computers connect and communicate over local area networks (LANs) and the internet.

Instructions are links are all here in The Young Person’s Guide to Programming in Minecraft. Feel free to get ahead of the game (literally!) before Wednesday…

Mouse Input &amp; Buttons


This week, by popular demand, we are going to add an options screen to our games. To achieve this we are going to need to do two new things – read mouse clicks and positions, and know if we have clicked inside a specific region of the screen.

The first part is easy. Just like the close button on the top right of your game window, your game gets to hear about mouse input via events. In the same place where we check for the quit event (triggered by the close button being clicked) we can also check for mouse movements and mouse clicks.

while game_running:

    for event in pygame.event.get():

        if event.type == pygame.QUIT:

            game_running = False

        if event.type == pygame.MOUSEMOTION:

            mouse_position = event.pos

        if event.type == pygame.MOUSEBUTTONDOWN:

            click_position = event.pos

When the MOUSEBUTTONDOWN event occurs, we can do whatever it is we want to do in response to mouse clicks. In our game options screen, for example, we want to see if one our buttons has been clicked – how can we do this?

Well, we can go back to basics and use very similar code to that we use for checking if two rectangles have collided. It turns out as well as Coliderect, there is a Collidepoint method that will return true if a coordinate point (as opposed to another rectangle) is inside the target rectangle. As you can see from the code above, we are already storing the coordinate point of the mouse in the click_position variable, so when we detect a mouse click we can simply test this point against a list of button rectangles to know which one was clicked!

if event.type == pygame.MOUSEBUTTONDOWN:

    click_position = event.pos

    if option_button1_rect.collidepoint(click_position):

        # Do Option Button 1 stuff...

    elif option_button2_rect.collidepoint(click_position):

        # Do Option Button 2 stuff..

        ...

        ...

As well as adding a new screen to our existing game, it is possible to build an entire game around this simple game mechanic. Take a look at ClickChase.py and see if you can follow how it works and modify it.

Pac-man pop-gun


Untitled-1

By popular request we are going to add GUNS this week – I can’t help thinking that you’re not really entering into the spirit of Pac-man by arming him, but here goes…

The Bullet Data

The key to allowing our player to fire is really management of a list of bullet objects, so lets start by defining the data of a bullet and creating a list of them. We are going to make this data available to all functions as a global variable so we will create it with the other global game data just after your pygame.init() call.

bullets = []

for index in range(10):

    bullet = {

        "image":pygame.image.load("exit_32.png"),

        "rect":pygame.Rect(0, 0, 16, 16),

        "speed":8,

        "velocity":[0,0],

        "active":False

        }

    bullets.append(bullet)

You can see that the bullet is very similar to the other game objects we have created, like the the player and enemy. The for loop adds 10 of them to the global list called bullets.

Currently our bullets are being created with no velocity, at position 0,0, and they are set as inactive (their “active” flag is set as False). The plan is that that we will re-use these same bullets each time the player fires. This approach avoids constantly creating and destroying bullet data, but it also means that we can only ever have 10 bullets �?active’ at any time. We can tweak this number, but it is useful to have a cap on the maximum number of bullets in any case, to avoid us slowing down the game. So the process of firing will be like this:

  • If bullet is fired:
    • Find the first available inactive bullet
    • Move it to the player
    • Set its velocity according to the direction the player is facing
    • Activate it

Then the update function will need to do the following:

  • For each bullet that is active:
    • Update its position according to its current velocity
    • Check for collisions with relevant objects (currently this means Walls or Enemies)
    • If collided with a wall:
      • Deactivate the bullet
    • If collided with an enemy:
      • Kill the enemy
      • Deactivate the bullet

Finally the drawing function will be easy:

  • For each bullet that is active:
    • Blit the bullet’s image to its current position rectangle

That’s it!

Bullet Drawing

So lets do the easiest function first:

def DrawBullets():

    global bullets, screen

    # For each active bullet

    for bullet in bullets:

        if bullet["active"] == True:

            screen.blit(bullet["image"], bullet["rect"])

Simples!

Bullet Update

The next function will do the basic update and deactivate the bullet when it hits a wall (we’ll kill enemies with it next week!):

def UpdateBullets():

    global bullets, walls, enemies

    # For each active bullet

    for bullet in bullets:

        if bullet["active"] == True:

            bullet["rect"] = bullet["rect"].move(bullet["velocity"][0], // bullet["velocity"][1])

            if bullet["rect"].collidelist(walls) > -1:

                bullet["active"] = False

NOTE: the “//” in the code above means I’ve had to split the line to paste it into the website, but you should not – keep the two bits together on one line.

Bullet Firing

Now this is the trickiest one because of a subtle issue that we haven’t really encountered before. Our previous checks have simply been to see if a button is down. If we do this for firing then we will fire all of our bullets immediately – it will be a machine gun by default! Instead, to make it a single shot, we need to fire one each time the key is pressed. To do this we need to only activate a bullet if the fire button is pressed, but was not pressed last time we checked. This requires an additional global variable, last_fire_button_state, that we can check and update in the following function:

def CheckFireBullet():

    global bullets, player, last_fire_button_state, keys

    current_fire_button_state = keys[pygame.K_SPACE]

    if current_fire_button_state and not last_fire_button_state:

            # Find the next available bullet

            for bullet in bullets:

                if bullet["active"] == False:

                    # Move it to the player's position

                    bullet["rect"] = player["rect"]

                    # Reset the bullet velocity

                    bullet["velocity"] = [0,0]              

                    # Set its velocity according to the player's direction

                    if player["direction"] == 0: # left

                        bullet["velocity"][0] = -bullet["speed"]

                    if player["direction"] == 1: # right

                        bullet["velocity"][0] = bullet["speed"]

                    if player["direction"] == 2: # up

                        bullet["velocity"][1] = -bullet["speed"]

                    if player["direction"] == 3: # down

                        bullet["velocity"][1] = bullet //["speed"]               

                    # Activate it

                    bullet["active"] = True                 

                    # break out of the for loop

                    last_fire_button_state = current_fire_button_state

                    break

    # Store the last button state in the global for the next time...

    last_fire_button_state = current_fire_button_state

Adding the function calls

All that remains now is to call our new functions in the main game loop – we can call

CheckFireBullet()
UpdateBullets()

after the existing UpdateEnemyPosition(enemies, walls).

then DrawBullets() can be added after DrawPlayer(player)

You should now have the ability to fire some bullets in the direction your player is facing.

Next week we’ll blast enemies with �?em!

Jet Pack


JetPac–16KB of genius for the ZX Spectrum.
In this session we’re going to add an alternative update method for the player to provide a completely different kind of movement – Jet pack power! This new game dynamic is inspired by a favourite game of mine – JetPac. This was the flappy bird of its day and was written by a company called Ultimate – which is now Rare, a computer games company that is still based very close to us in Twycross.

To begin with, our player data is going to need to include a new �?velocity’ field. Velocity is different from simple speed, because it encodes direction as well as magnitude. In our case the velocity is going to be represented with two values – one for the x direction (left and right) and the other for the y direction (up and down). We need to add this field to our existing data:

player = {
    "images":[
        pygame.image.load("player_left_0.png"),
        pygame.image.load("player_right_0.png"),
        pygame.image.load("player_up_0.png"),
        pygame.image.load("player_down_0.png"),
        pygame.image.load("player_left_1.png"),
        pygame.image.load("player_right_1.png"), 
        pygame.image.load("player_up_1.png"),
        pygame.image.load("player_down_1.png")
        ],
    "rect":pygame.Rect(0, 0, 32, 32),
    "start_rect":pygame.Rect(0, 0, 32, 32),
    "speed":4,
  
"velocity":[0,0],
    "direction":0
    }

This new velocity field will now be used to decide how far and how fast the player is moving, but we will continue to use the old speed field as a *maximum* speed to cap the velocity values.

Next we need to create our new movement function. Hopefully the power of functions will now become apparent because we can leave all of our other code almost entirely untouched and just focus on this new functionality. The fundamental principles of our new function are pretty similar to the previous one and can be divided into the following stages:

  • Copy the current player position rectangle
  • Read the keyboard input
  • Use the input to update the position of the copy position rectangle
  • Check if the copy rectangle collides with any level walls
  • If it doesn’t collide, use the copy to update the actual position of the player rectangle

Our new function is a little more sophisticated in how it does these things, but essentially it is the same as our previous one. I will go through it in detail during the session.

def UpdateJetPackPlayerPosition(player, walls):
    player_move_rect = player["rect"]

    # reset the x movement velocity to zero
    player["velocity"][0] = 0

    # Create gravity force
    force = -0.5

    # Read the keyboard input and move the marker
    key = pygame.key.get_pressed()
    if key[pygame.K_LEFT]:
        player["direction"] = 0
       # Add left movement velocity
        player["velocity"][0] = -2

    if key[pygame.K_RIGHT]:
        player["direction"] = 1
        # Add right movement velocity
        player["velocity"][0] = 2

    if key[pygame.K_UP]:
        # Add Jetpack thrust
        force = 1
       
    player["velocity"][1] = player["velocity"][1] - force

    if player["velocity"][1] > player["speed"]:
        player["velocity"][1] = player["speed"]

    if player["velocity"][1] < -player["speed"]:
        player["velocity"][1] = -player["speed"]
   
    player_move_rect = player_move_rect.move(player["velocity"][0], 0)
    if player_move_rect.collidelist(walls) == -1:
        player["rect"] = player_move_rect
    else:
        player_move_rect = player['rect']
        player["velocity"][0] = 0

    player_move_rect = player_move_rect.move(0, player["velocity"][1])
    if player_move_rect.collidelist(walls) == -1:
        player["rect"] = player_move_rect
    else:
        player["velocity"][1] = 0

Finally we need to update our main game loop to replace the call to UpdatePlayerPosition, with the new UpdateJetPackPosition.You’ll probably want to redesign your levels to be more like platforms than mazes, but apart from that you’re done. We now have a radically different game by modifying just one function – how cool is that!

Animation time!


imageNow we have got all of our player-related data into a single dictionary container, we can add more images to it and create some cleverer update and drawing functions to take our new multi-image player and work out which frame to draw.

Firstly we are going to add a python list *inside* the player dictionary. You will remember that the list has the syntax:

python_list = [item1, item2, item3]

and we can access the item using an index into the list (don’t forget the indices start from zero):

item1 == python_list[0]

Our list is going to hold all the images for our player. There are two frames of animation for four different directions and are named with the following convention: player_direction_frame.png

so when we add our list to the player dictionary and associate with the “images” key, it looks like this:

player = {
    "images":[
        pygame.image.load("player_left_0.png"),
        pygame.image.load("player_right_0.png"),
        pygame.image.load("player_up_0.png"),
        pygame.image.load("player_down_0.png"),
        pygame.image.load("player_left_1.png"),
        pygame.image.load("player_right_1.png"), 
        pygame.image.load("player_up_1.png"),
        pygame.image.load("player_down_1.png")
        ],
    "rect":pygame.Rect(0, 0, 32, 32),
    "start_rect":pygame.Rect(0, 0, 32, 32),
    "speed":4,
    "direction":0
    }

Hopefully you can see that the images are added in the order

frame 1: left, right, up, down

frame 2: left, right, up, down

Which will be important to remember later.

You can also see an final extra data item at the end of the player dictionary, “direction”, that we will use to keep track of which way our player is facing.

The update player position function now needs to set the direction, which will form part of the index we need to choose the right image from our new list:

def UpdatePlayerPosition(player, walls):
    player_move_rect = player['rect']
    speed = player['speed']
    # Read the keyboard input and move the marker
    key = pygame.key.get_pressed()
    if key[pygame.K_LEFT]:
        player["direction"] = 0
        player_move_rect = player_move_rect.move(-speed, 0)
    if key[pygame.K_RIGHT]:
        player["direction"] = 1
        player_move_rect = player_move_rect.move(speed, 0)
    if key[pygame.K_UP]:
        player["direction"] = 2
        player_move_rect = player_move_rect.move(0, -speed)
    if key[pygame.K_DOWN]:
        player["direction"] = 3
        player_move_rect = player_move_rect.move(0, speed)

  

# Only if the marker has not hit a wall do we update the player
    if player_move_rect.collidelist(walls) == -1:
        player['rect'] = player_move_rect

Now we can add a draw function to pull together everything we have put into place.

def DrawPlayer(player):
    animation_step = player["speed"] * 3
    image_index = player["direction"]
    if image_index < 2: # Moving left/right?     
        if (player["rect"].left // animation_step) % 2:
            image_index = image_index + 4
    else: # Moving Up/Down
        if (player["rect
"].bottom // animation_step) % 2:
            image_index = image_index + 4
   
    screen.blit(player["images"][image_index], player["rect"])

This is a fairly complicated function. animation_step is the variable that will determine how often we will change the image frame – it must be a multiple of player["speed"] to ensure that we will actually hit these steps. Initially we get the image_index directly from the direction. we know that we are moving left/right if the index is less than 2 and we use the left-hand position of the player rectangle to pick the image frame we’re going to use. We divide it by the animation_step to find give us our horizontal position in ‘steps’ (the // is a special divide that will round the result to a whole number) then we see if it is an odd or even step using modulus (%) 2. If it is an odd numbered step then we bump the image_index up by 4, which if you remember how the image list was ordered from above, will now make it one of the frame 1 images.

Essentially we do the same thing, but based on the position of the bottom of the player rectangle if we are moving up/down.

In our main loop we can now just add calls to these functions:

UpdatePlayerPosition(player, walls)
DrawPlayer(player)

the game loop is *a lot* neater, but more importantly we can now improve or replace these functions very easily without affecting any other parts of our game.

Gathering our data together


imageOur game is starting to get quite sophisticated, but the code is also starting to get complicated too Sad smile. Now is a good time to step-back and make our lives a bit easier with some restructuring before we go onto to add more functionality. We learned about functions in the last meeting, which allow us to reuse important operations and logic, but now we need to look at our data.

Replacing Individual Data Elements

Our current player requires four separate data elements in order to manage movement and drawing

player_start_rect = pygame.Rect(0,0, block_size, block_size)
player_sprite = pygame.image.load("sprite_32.png")
player_rect = player_start_rect
speed = 4

We are going to strip these out and replace them with a single data element with each sub-element neatly grouped under it. To do this we are going to use a Dictionary container that will allow us to use a label to access each item from a new, single, player element.

player = {
    "image":pygame.image.load("sprite_32.png"),
    "rect":pygame.Rect(0, 0, 32, 32),
    "start_rect":pygame.Rect(0, 0, 32, 32),
    "speed":4   
    }

Now, everywhere in our code where we were having to remember and use each separate data item we can use our neater container:

player_rect 
player_sprite

player_start_rect

speed

player['rect']
player['image']
player['start_rect']
player['speed']

But even more usefully we can now create some cool functions that take our new player dictionary – or indeed other dictionaries provided they have the same the items in them. This is a *very* useful way to simplify our main loop code with function calls like:

UpdatePosition(player)

Draw(player)

instead of the swathes of code currently required.

C++ Coding Ninjas


For the Code Ninjas in the club, we took a look at C++ for the first time. This is a *massive* new challenge, so don’t feel bad if you found it a bit daunting!

imageUnlike python, C++ is a compiled language, which means we can’t avoid looking at how the code that we write is actually converted into the code that the computer truly understands. In python this process goes on too, but we just don’t see it. In addition, we are expected to understand the computer’s underlying structure and resources (in particular its memory) and manage these directly for ourselves as we write our program. However, in return for this additional complexity C++ is one of the few remaining ‘System Languages’ still in common use – this means that you could even write an operating system (such as Windows itself) using a language like C++. Indeed Android, Linux and the like have mostly been written using C (a closely related pre-cursor of C++).

Because C++ requires quite a few steps to get from our source code (the text we write) to byte code (the code the computer actually runs) it makes life a lot easier if use an Integrated Development Environment (IDE) to work in – provided we have a compiler and linker though, we could (and probably will at some point, just to prove it) do everything we need from simple text editor – even notepad!

The IDE we’ve settled on for now is CodeBlocks and this includes GCC compiler and linker see the tools and technology section for download info.

We will have a lot of conceptual ground work to cover over the next few weeks, but I want to have something visual for you to tinker with so I skipped over *a lot* of topics and we looked at single page of code that made use of two existing libraries – GLFW & OpenGL. GLFW allowed us to make a Window and run our simple game loop, then OpenGL provides the 3D drawing commands we need to put stuff on the screen. The process of incorporating these libraries will be covered again. But the important thing I highlighted was that the key concepts of the code itself were almost identical to pygame:

  • Initialise the window,
  • Start a while loop that runs until the window is closed (the game loop)
  • Within the loop:
    • Collect the messages from Windows (otherwise it thinks we are dead, or at least ‘Not responding’
    • Draw our graphics.

Simples! Winking smile

Pygame Graphics


Thanks to the efforts of the IT staff at the school (cheers Manjit & Josh, we really appreciate it!) this week’s meeting was the first one we’ve run without having to use Raspberry Pi’s for our python programming. Not only was this easier to organise, but it was also more productive and you all now have access to your own files and projects via the school network.

imageSo in this session we had a little refresher on variables, then we cracked straight on with creating our own sprite (using Paint.net) and then rendered it on the screen using the pygame library.This process uses the command ‘Blit’, which is short for a block image transfer. As we will see in later sessions, this is the mainstay for rendering nearly all your graphical game elements. Early computers that were aimed at running games had a dedicated chip whose sole role was to perform these fast copies of image data blocks to the video memory area from which the screen display is drawn. On our modern computers using pygame the process is still being performed by an entirely independent computer-within-the-computer called the Graphics Processing Unit (GPU) in order to make it lightning fast.

Don’t forget that if you have access to a computer at home, you can install python, pygame & Paint.net for free – go to the tools and technology section for links. You should also be able to log in to the school VLE and be able to access and update your files from home.

Meeting 11- Summer-term Studio Start-ups!


Last term we learned the basic skills we needed to write a game using Python & Pygame. This term we are going to put those skills into practice, from first principles, by designing and creating our own games.

To start with, we divided into teams to form our own ‘game studios’. We chose names, designed logos and began planning our first games. We are going to advertise our games using our own websites.

The studio sites created so far are:

Epicorp

Epicorp

Alex, James & Noah

 

Raspberry Crush

Emily & Cecilia

SectorNineStudios

SNS™
(Sector Nine Studios)

Lewis & Santi

Click on the logos to follow their progress on their own webpages – we will be begin writing these using HTML and javascript next week.

Meeting 6–Saving &amp; Loading Files


In this session we looked at file loading and saving. In the last meeting before half-term we had the bare-bones of a game, but we were creating the background by ‘hardwiring’ the positions of our bush and tree images in code. In this meeting we want to see how we can load in the layout of our background map using a simple text file. Then we will be able to edit or add completely new levels to our game without modify any code.

Defining our map

Our starting point is do think of our map level as a grid. At each point in the grid we can have a number that will indicate a type of image to use for that location – 1 is grass, 2 is concrete, 3 is a wall, etc.

1 1 1 1
1 2 2 1
1 2 2 1
1 3 2 1

We’ll call the images ‘tiles’ because building our map will be a bit like tiling a floor – we’ll look at map for each location, pick the appropriate tile image and then render it in that location. The images we used were from the python-games directory:

Wall_Block_TallGrass_BlockPlain_BlockWood_Block_Tall

These images are slightly more complicated than the most basic form of tile, because they will give the impression of a 3D world when overlaid with one another. Because of this they are not actually square. If you want to create your own map tiles, I would recommend using simple 64 pixel square images instead – but we needed to use existing images in the club.

We can load our images into a List of images like this:

tile_image_list = [
    pygame.image.load('Wall_Block_Tall.png'),
    pygame.image.load('Grass_Block.png'),
    pygame.image.load('Plain_Block.png'),  
    pygame.image.load('Wood_Block_Tall.png')
    ]

Files with miles of tiles…

So we know that our map will be a grid, but how can we store a grid in a file? Well, we used a simple text file where each line of the file is a row in our grid. On each line we write the number of the image tile and separate each one with a ‘,’. So the file will look like this:

1,1,1,1

1,2,2,1

1,2,2,1

1,3,2,1

Four lines, and each line has four (comma separated) numbers.To read a text file like this we need to import the os module (which includes all the Operating System-related commands). Then use the Open function to create a file object. Then we can read each line from the file object. Split the line on commas to get the list of individual tile numbers:

def LoadLevelMap(filename):
    file = open(filename)
    for line in file:
        tiles = line.split(",")
        level_map.append(tiles)
    file.close()

Don’t forget to close the file when you have finished with it, or you will leave it locked and inaccessible to your own and other programs.

Maps in memory

When we load this file we need to store the map in the computer’s memory so we can read it during the draw function and blit the tile with the matching index number. To do this we use a python List of rows, and each row will also be List of tiles – a List of Lists! But if you think about this for a moment it is not as complicated as it first appears and it is a common construction in programming that is often called ‘nesting’. To read back this data, we’ll also use a For Loop that is nested inside another For Loop too. The outer For Loop will go through the rows and then run the inner For Loop to read each tile number from the row.

def DrawLevelMap():
    x_pos = 0
    y_pos = 0
    for line in level_map:
        for tile in line:
            screen.blit(tile_image_list[int(tile)], (x_pos, y_pos))
            x_pos += 50     
        x_pos = 0
        y_pos += 40

Because we’ve wrapped up the key bits of code into functions we can now just call them at the appropriate stages in our game set-up and game loop to do their funky stuff. Also we can easily re-use this code again in different projects because it is (quite) self-contained.

All the code for a quick demo is here, assuming you have saved your level file as “GCC-Level1.txt” inside the python-games directory along with the python file.

import pygame, os

pygame.init()
screen = pygame.display.set_mode((400, 400))

tile_image_list = [
    pygame.image.load('Wall_Block_Tall.png'),
    pygame.image.load('Grass_Block.png'),
    pygame.image.load('Plain_Block.png'),  
    pygame.image.load('Wood_Block_Tall.png')
    ]

level_map = []

def LoadLevelMap(filename):
    file = open(filename)
    for line in file:
        tiles = line.split(",")
        level_map.append(tiles)
    file.close()

def DrawLevelMap():
    x_pos = 0
    y_pos = 0
    for line in level_map:
        for tile in line:
            screen.blit(tile_image_list[int(tile)], (x_pos, y_pos))
            x_pos += 50     
        x_pos = 0
        y_pos += 40
        print(x_pos, y_pos)

LoadLevelMap("GCC-Level1.txt")
DrawLevelMap()
pygame.display.flip()

Meeting 5+ Minecrafting


For the last 10 minutes of the session I found a great target for our new-found python skills in a proper commercial game – Minecraft.

The version recently released for the RPi can be found at the PiStore and it includes a python scripting interface. I moved the mcpi directory onto the desktop of the club RPi’s for easy access.

Initialising our connection to minecraft

First we need start minecraft running in X Windows on the Pi, then open a terminal window in the mcpi directory and change our current directory (using the cd command) to the one containing the python module for minecraft:

cd mcpi/api/python/acpi

then start python

python

and import the minecraft module into a namespace called ‘minecraft’.

import minecraft as minecraft

and get hold of the main minecraft object:

mc = minecraft.Minecraft.create()

Now we should have a minecraft object called ‘mc’ and we can use it to access lots of different commands in minecraft. We can find out where we are with:

mc.Player.getTilePos()

then create some blocks around us using:

mc.setBlock(x, y, z, block_id)

where the block_id is one of the many types of block. It turns out that Scott has an encyclopaedic knowledge of these and told me that id ‘10’ is lava. This was impressive, but caused me to die from my own lava flow almost immediately in my own demo Sad smile

Minecraft XBLA

You can use all the usual python constructs, like for loops, to programmatically creating lots of shapes. You can even create python module with some functions defined to create whole structures or buildings automatically. Enjoy!

Meeting 5–Putting it all together…


This was the last session before half-term so I was keen to finish with a basically functional game! For this we needed to add two key game principles to our growing collection – showing a score, and a bit of randomness.

Rendering Text

Unlike the early sessions, printing text is decidedly trickier using pygame. It involves creating a font object, using this to render to a surface (which is an image) and then blitting this surface to our screen backbuffer and flipping it to the front. Phew! Lets eat this elephant a bite at a time…

Creating a font object

We need to initialise the font system in pygame and then create a font object from the available system fonts installed on your computer.

pygame.font.init()
font_object = pygame.font.SysFont("Arial", 30)

How did we know to use the name ‘Arial’? Well strictly speaking we need to know what fonts are installed on the computer on which we are going to run this game. There are certainly likely to very different ones installed on a typical Windows PC compared to the RPi. To find out we can use the IDLE shell to type in directly:

>>> import pygame
>>> pygame.init()

>>> for font in pygame.font.get_fonts():
    print(font)

This will print the names of all the fonts installed on your machine – pick one of these and use this instead of ‘Arial’.

Rendering our text to a surface

Now we have our font object we can use it to render some text to a surface in our main game loop:

score_text_surface = font.render("Score: " + str(score), 1, (255, 255,255))

This line assumes that our score is being kept in a simple integer variable called ‘score’, simageo before we can add it onto the the end of our ‘Score: ‘ text it needs to be turned into a string using the str() function. The other values determine if the text should be antialiased (smoothed around the edges) and the colour in Red, Green, Blue (RGB) format – in this case maximum for all three, which will make it white.

Blit our surface to the screen

Finally we can blit our surface to the screen in exactly the same way we blitted our images in the last session:

screen.blit(score_text_surface, (10, 10))

If you recall, the pair of numbers is the screen coordinate to blit to. In this case 10 pixels across and 10 pixels down from the top left corner of our screen.

Random Girlfriends

So, our Valentine’s Day meeting game needs a backstory. Our player character, lets call him Jack (because we know Jack has a thing about Pink Girl), has arranged to meet *all* of his girlfriends at the same place by accident. If they meet each other he’s going to be totally busted. So our game will be that Jack needs to meet all his girlfriends as they walk across the park and send them on their way with a quick kiss (or mouse click). For each girlfriend he succeeds in kissing we’ll award 10 points, but for each one that he misses he’ll lose 5 points.

The main action we need to implement here is that PinkGirl needs to start at a random location on the left of the screen and she needs to move across the screen each time through the game loop. If she is clicked on, we’ll score 10 and move her back to the left. If she makes it to the right-hand of the screen then we’ll subtract 5 points and then move her back.

Data-wise we need two things here: the bounding rectangle of the girl, so we can test if the mouse has been clicked on her, and a pair of position coordinates that we can adjust programmatically to move her around. These are created thus:

girl = pygame.image.load("pinkgirl.png")
girl_rect = girl.get_rect()

girl_position = [0,0]

To be able to create random numbers we also need to import the random module at the beginning of our game code:

import pygame, random

We can now use the random object to create random integers (random.randint) between two values – in our case 0, an the height of the screen to give a random starting position for the girl.

Now to move the girl across the screen until she gets the right-hand side, then reset her to a random position on the left we use this code in the main loop:

girl_position[0] += 1
girl_rect.center = girl_position
if girl_position[0] > screen.get_width():
    score -= 5
    girl_position[1] = random.randint(0, screen.get_height())
    girl_position[0] = 0

Finally we need to add an extra bit of code the the event handlers for the mouse click. This tests if we have clicked on  the girl and it resets her position and adjusts the score accordingly:

if event.type == pygame.MOUSEBUTTONDOWN:
    if girl_rect.collidepoint(event.pos):
        score += 10
        girl_position[1] = random.randint(0, screen.get_height())
        girl_position[0] = 0

And that is basically it. There is *a lot* to take in I know – this was probably too ambitious for one session in hindsight, but never mind. For the last 10 minutes we directed our new-found python skills at minecraft to cheer up those with aching brains, but before we go onto that, here is the full code for the game, just for the record:

import pygame, random

pygame.init()
pygame.font.init()
screen = pygame.display.set_mode((400, 400))

font = pygame.font.SysFont("Arial", 30)

boy = pygame.image.load("boy.png")
boy_rect = boy.get_rect()

girl = pygame.image.load("pinkgirl.png")
girl_rect = girl.get_rect()

background = pygame.image.load("flippyboard.png")
tree_image = pygame.image.load("Tree_Ugly.png")

position = [0,0]
girl_position = [0,0]
tree_positions = [(167, 56), (78, 127), (55, 55), (350, 270)]
score = 0

game_running = True

while game_running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            game_running = False
        if event.type == pygame.MOUSEMOTION:
            position = event.pos
        if event.type == pygame.MOUSEBUTTONDOWN:
            if girl_rect.collidepoint(event.pos):
                score += 10
                girl_position[1] = random.randint(0, screen.get_height())
                girl_position[0] = 0

    boy_rect.center = position 
   
   # Move the girl
    girl_position[0] += 1
    girl_rect.center = girl_position
    if girl_position[0] > screen.get_width():
        score -= 5
        girl_position[1] = random.randint(0, screen.get_height())
        girl_position[0] = 0
          
    screen.blit(background, (0,0))
    screen.blit(girl, girl_rect)
    screen.blit(boy, boy_rect)
    score_text = font.render("Score: " + str(score), 1, (255, 255,255))
    screen.blit(score_text, (10, 10))
       
    for tree in tree_positions:
        screen.blit(tree_image, tree)

        
    pygame.display.flip()

pygame.quit()

Meeting 4–Graphics!!


image

This week we put together everything we’ve been working on over the last few meetings to start rendering some graphics in the game loop and then move them around using the mouse.

The graphics we used are all part of the pygame games provided as part of the raspian image.  We discussed how to define ‘paths’ to different files on the computer, but to simplify the process of finding the images we need, we started the IDLE shell, created an new window then immediately saved our new file in the pygames folder – in the same location as the images.

Everyone then created the basic pygame initialisation and game loop in the new blank file (it’s all good practice!) and then we got onto the meat and potatoes – loading an image.

We used pygame.image.load to import an image from disk and then store it in a variable. Then, in the game loop, we used the blit method (short for ‘block image transfer’, if I remember my Amiga days correctly) to render the image using the variable name. We also had to remember to flip our freshly drawn screen from the back buffer to the visible screen.

Once we’d nailed the basic principles of rendering images, we added a background and some bushes, being careful to draw them over the top of each other in the correct order: background, manga boy (or girl in Jack’s case), then the bushes.

The final bit to wire-up was some interaction. Initially I demonstrated using a mouse click (the pygame.MOUSEBUTTONDOWN event) to move the character around the screen. But Jeremy, et al. tried the pygame.MOUSEMOTION event instead and this was actually way better!

So there we had it – loops, rendering and events coming together to make something that is really starting to look like a game!

Here’s the final code:

import pygame

pygame.init()

screen = pygame.display.set_mode((400, 400))
boy = pygame.image.load("boy.png")
background = pygame.image.load("flippyboard.png")
tree_image = pygame.image.load("Tree_Ugly.png")
position = (0,0)

tree_positions = [(167, 56), (78, 127), (55, 55), (350, 270)]

game_running = True

while game_running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            game_running = False


        if event.type == pygame.MOUSEMOTION:
            position = event.pos

    screen.blit(background, (0,0))
    screen.blit(boy, position)
   
    for tree in tree_positions:
        screen.blit(tree_image, tree)
       
    pygame.display.flip()

pygame.quit()

Meeting 3 – Functions, While-loops &amp; Events


In this meeting we got into extending and organising some of our code with functions defined using the def keyword. We all created a new Me() function that printed our name and (apparently extensive!) skills.

With functions under our belts we went back to the main game loop in pygame. We learned about the while loop and set a game loop calling our own Update() and Draw() functions until the game_running flag was set false.

Then we learned how the game window needs to interact with the the operating system (either Windows or LDXE on the RPi’s) using messages and events. We added a check for OS messages on the pygame event queue and printed the different events being sent to our game.

image

We rigged a test for the pygame.QUIT event, which was received when our game’s frame window close button was clicked. We used this to exit our game loop cleanly. The final version of this bit of code was:

import pygame, sys

pygame.init()
screen = pygame.display.set_mode((640, 480))

game_running = True

while game_running:   
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            game_running =
False
           
        print(pygame.event.event_name(event.type))

pygame.quit()
sys.exit()

Jeremy and Oliver went on to use this code to test if the mouse was inside or outside of a circle they had drawn on the screen. We just got this working before the end of the session using Pythagoras’ Theorem to test if the mouse X and Y position was within the radius distance of the centre of the circle. These are the rudiments of collision detection – nice work lads, well done!