🎯 Learning Objectives
After this lesson you should be able to:
- Be able to use graphics and sprites in games
- Understand what makes a good user experience
💬 Key Vocabulary
- Interactive
- Graphics
- 2D
- Sprite
- Bit Blit
- Masking
- User Experience
- User Interface
Last lesson, you…
- Wrote some more programs
- Explored the concepts of states and objects
- Learned about user experience
Today, you will…
- Learn about graphics and sprites in games
- Research what makes a good user experience
- Develop your first programs using graphics.
📝 Starter Activity
- Let’s see what you know already about graphics.
- What do we mean when we say a game is 2D or 3D?
- Where are 2D games still played?
- Why do you think 2D games are still popular even if we can create 3D graphics that look like real life?
Think about these questions, then have a look at the answer below.
Click here for the answer
- 2D or 3D refers to how the game is drawn. A 2D game uses images drawn onto a “surface” with a fixed camera that can only point in one direction. A 3D game uses a world made of models which are 3D objects, it calculates things like lighting and reflections, and the camera can move in any direction.
- 2D games are played in a lot of places, but primarily online and as mobile apps.
- There are many reasons 2D games are still popular – some games are limited by the hardware of the device they are played on, or their download speed – even as powerful as today’s smartphones are, they’re no gaming PC, and they’re often connected using mobile data. We want to play mobile games fast and so waiting four hours to download a beautiful 3D world isn’t an acceptable user experience. Also, some games focus on aspects other than graphics to make them successful. There are many games where it is the gameplay, or a particular game mechanic (feature) that make them popular and addictive more than the graphics – think of some examples of these!
📖 Learn it – Images
- Basic shapes are a good starting point, and many early games used them very successfully.
- Times have changed, we can draw better graphics, and images are much more fun. They allow us to have complex items with many different colours, and look far better than just simple shapes.
- In 2D games, like the one we’re making, image files are drawn onto the screen as movable objects on top of the background to make the finished frame of animation.
- Images drawn in this way are called sprites.
- Using images as sprites is very simple in Pygame.
The process for drawing sprites in Pygame looks like this:
Load the image into memory
Begin Game Loop
Set X and Y coordinates for the image
Display the image on the screen at the coordinates
The image we use only needs to be loaded into the computer’s memory once, before the game loop starts – why is this an advantage?
Click here for the answer.
If we loaded the file from the computer’s hard drive every time it was drawn on screen (e.g. 60 times a second), we would get very poor game performance.
📖 Learn it – Filmmakers’ Mattes
- We’re going to quickly look at an old film photography trick using a mask called a matte, which means you can combine two photographs into one.
- Film reacts to light reaching it, so if you can block the light from part of a piece of film as it’s shot, then swap which part is blocked out and shoot the same film again, you get a composite image with the two halves joined together, without having to do any editing afterwards.
- Let’s say we want to have a scene on a street in Paris. The problem is, we’re filming in a studio, and it’s in England.
- We could travel to Paris and spend a lot of money, or we could simply get our actors to look like they’re there – what if we printed a huge picture of a Parisian street and simply made our actors stand in front of it? Fine, but not great, the lighting would be all wrong and our actors would cast a shadow onto the picture, giving away that it’s fake.
- We could build a set to replicate Paris – still expensive and very time consuming.
- We can go one better – we can overlay our actors onto a film of Paris! Now we have something which looks convincing, but is fast and cheap.
- This works especially when the scene we want to appear isn’t even real.
- Before computers and computer generated art, artists called matte painters would create scenes with a blocked-out area and then these were used to fill in where the actors were. Famous sci-fi films such as the original Star Wars trilogy made heavy use of this idea.
- This is almost exactly the same way that green-screens are used now.
📖 Learn it – Bit Blit
- We want to combine at least two images for our games too – the background image and the sprite, to create one output image.
- Although it looks like it, the computer doesn’t actually just draw them one on top of the other – this is too time consuming! We’d have to do quite a lot of difficult calculations to know which parts of the original image to draw over, and what the resulting pixels should be. It also wastes a lot of memory and time as we’re drawing a lot more than we need to!
- Instead, a technique called bit blit was developed. Bit blit, originally written Bit BLT, means Bit BLock Transfer.
- The bit blit technique follows almost perfectly the same model we’ve just seen with film, but does it digitally using simple maths.
- We use a mask with the outline of the sprite to remove the background, then draw the sprite in its place.
- The mask has two colours, white (1) and black (0).
- The background and sprite have much a larger number of colours – many thousands of them. Each colour has a numeric value.
- We multiply the colours of the background and mask together – multiplying is really fast!
- When the mask is white, that’s 1 x the original colour, so the original colour.
- When the mask is black, that’s 0 x the original colour, so 0, which is black.
- Then we just add the sprite’s colours to the masked background’s colours. Where the mask area is on the background, the value of the colour is now 0, so adding the sprite to this gives just the sprite! Everwhere else, we’re adding 0, so this gives just the background!
- See what’s happening yet? Look at the following images to see how it works.
📝 Activity 1 – Drawing Sprites (? badge)
You’re going to modify the code below to draw a sprite, which will appear on the screen.
Start by adding this code above the gameState = running line:
player1Image = pygame.image.load("rocketship.gif")
Then add these below the “# gameloop code starts here” line and after screen.fill(black) to blit the image onto the screen:
player1XY = [100, 100]
player1 = screen.blit(player1Image, player1XY)
If it doesn’t work – think, what might you have done wrong?
Hint:
Check your indents, the code in the loop all needs to line up!
📝 Activity 2 – Mouse Following (? badge step 1)
Our sprite is static. That’s not very game like yet.
- Remember in lesson 1 we made the shape follow the mouse? We’re going to do that with the sprite instead.
- The mouse position is just a list of two coordinates:
[ x , y ]
- You get the mouse position from writing
pygame.mouse.get_pos()
- Work out how to modify your code so that instead of being drawn at (100, 100) the sprite is drawn at the current mouse coordinates.
Hint:
cursorX, cursorY = pygame.mouse.get_pos()
Then you’ll need to update your player1XY = [100,100] code to use cursorX and cursorY instead of 100, 100
📝 Activity 3 – Improvements (? badge step 2)
Our game is coming along, but a few improvements are needed to make it better:
- The image is its original size, which is probably wrong.
- The image isn’t centred on the mouse cursor.
- The mouse cursor is visible, which looks ugly.
Let’s deal with the resizing of the image first. We can handle this at the top of our program, before we start our game loop, just after we load the image. Pygame has a tool for this already, scale, which is part of the transform tools.
It looks like this: image = pygame.transform.scale(image, [x size, y size])
We’re going to modify this example to use the name of the image variable we want to resize. We could even store the size in a list as a variable too. We should probably do this, so a list called playerImageSize might be a good idea. Do this now.
What about centring the image on the mouse?
- We have the exact height and width of the image, so we can centre it on the mouse exactly like we did with the shapes in lesson 1.
- We have coordinates for where the image is being drawn, in the game loop.
- We need to tell Pygame to draw the image a bit further to the left, and a bit further up.
- How much further? Half the image size, ideally!
- Last time we tried this, we used fixed values, but since the image dimensions are in a variable, let’s calculate it using that instead. This means that we can resize the image and still keep it centred without changing any other code, it will recalculate automatically.
Try to work out how to centre the sprite on the mouse yourself. If you get stuck, there’s hints below.
Hints:
- The width of our image (in pixels) is stored in the variable called playerImageSize, which is a list.
- Remember, we can get items from a list using their index.
- For the X position, index 0, we use playerImageSize[0] and when we want the Y position, we use playerImageSize[1].
- We know where the mouse currently is. It’s stored in mousePosition, which is also a list.
- We need to subtract half the width of the image from the mousePosition to move it to the left. The same idea applies to the height to move it up
There are lots of ways to do this, here’s three:
Option 1 – plain and simple:
newXPosition = mousePosition[0] - (playerImageSize[0] // 2)
newYPosition = mousePosition[1] - (playerImageSize[1] // 2)
player1XY = newXPosition, newYPosition
OR…
newXPosition = cursorX - (playerImageSize[0] // 2)
newYPosition = cursorY - (playerImageSize[1] // 2)
player1XY = newXPosition, newYPosition
Option 2 – no extra variables:
player1XY = [mousePosition[0] - (playerImageSize[0] // 2), mousePosition[1] - (playerImageSize[1] // 2)]
OR…
player1XY = [cursorX - (playerImageSize[0] // 2), cursorY - (playerImageSize[1] // 2)]
Option 3 – list zipping (pretty insane but offers an advantage, see if you can spot it…):
player1XY = [ position - (size//2) for position, size in zip(mousePosition, playerImageSize)]
Oh, and that visible mouse cursor?
pygame.mouse.set_visible(False)
Done.
📝 Research Activity – User Experience
Players require some way of understanding the state of the game, so that playing is an interactive and enjoyable experience. Players cannot play if they don’t know what is happening or what they should do next! This is achieved most simply through the visuals provided as graphical output, but also as an impact of other components of the game – there’s a lot more to it than just pictures that move when we press buttons!
Your task is to research another element that goes into creating a complete game experience. Choose from:
- Assets / artwork
- Sound effects and music
- Menus and score systems
- Level / quest / task design
For your chosen area, write 200 words explaining what this part of game design involves, what it provides in terms of enjoyment, and why.
📖 Extra Research – Visual Art
If you want to see a short masterclass from a current generation visual artist, watch this video:
💬 Summary
In this lesson, you…
- Explored the history of bit blit
- Learned about sprites
- Made a simple program that draws sprites that follow the mouse
- Discussed user experience
In the next lesson, you will…
- Build your first proper game!
🏅 Badge it
🥈 Silver Badge
- Upload a screenshot of your sprite code to the Silver badge task.
🥇 Gold Badge
- Upload a screenshot of your finished and improved code to the Gold badge task.
🥉 Platinum Badge
- Upload your findings on user experience.