The first thing that came to mind when thinking of colours was RGB (red, green, blue). These are the primary colours, and you can create every colour out of these three by just varying the intensity of each one.
In Part 1 of this series, to pass a colour as an argument, I simply passed a tuple of three elements, like (255,0,0). The first element in the tuple corresponds to the intensity of the red colour, the others to green and blue. The intensity ranges from 0 to 255. Brightness increases as you move from 0 to 255. (0,0,0) is black, and (255,255,255) yields white.
Given below is a little script that accepts intensities for the three colours from the user, and displays the resultant colour.
#color.py import pygame from pygame.locals import * from sys import exit r=min(255, input("Enter the red colour intensity :")) g=min(255, input("enter the green colour intensity : ")) b=min(255, input("enter the blue colour intensity :")) screen=pygame.display.set_mode((640,480),0,24) pygame.display.set_caption("colour testing ") while True: for i in pygame.event.get(): if i.type==QUIT: exit() screen.fill((r, g, b)) pygame.display.update()
We have used the
min function to limit user-entered values to a maximum of 255. The
screen.fill() function fills the screen with the colour denoted by the (r, g, b) tuple passed as the argument. For example, values of (123, 212 and 255) will give a colour as shown in Figure 1.
Suppose you have a green character in your game and, suddenly, a yellow fireball hits him. The character’s colour must change as soon as the fireball hits him, to a mixture of green and yellow. This technique is known as blending in Python. You can find the mixture by simply subtracting the two colours. In this case, just subtract (0,255,0) and (255,255,0) and the result is (255,0,0) — red.
Next, the fireball will spread on the character’s body, over time — so the intensity of the resultant colour (mixture) should also become brighter over time. Here’s the role of the blending factor, the value of which lies between 0 and 1. You multiply the blended colour with a blending factor, the value of which increases over time, from 0 to 1. As a result, the intensity of the resultant colour will also increase from 0 to 255.
Getting user input is a necessity not only for game-play, but also to configure the game preferences. In the previous part, we handled various mouse and keyboard events; we will get user inputs via the same events, so you may want to re-visit them.
Every key on the keyboard has a key constant for identification; in Pygame, it’s
K_z for the alphabets. We don’t have anything like
K_A because uppercase results from combining the Shift key — and the
mod value (see the previous article) of the key event tells us about the combination state.
Let’s use these constants to get user inputs—try out the following script.
#keyinput.py import pygame from pygame.locals import * from sys import exit pygame.init() screen=pygame.display.set_mode((640,480),0,24) pygame.display.set_caption("Key Press Test") f1=pygame.font.SysFont("comicsansms",24) while True: for i in pygame.event.get(): if i.type==QUIT: exit() a=100 screen.fill((255,255,255)) if pygame.key.get_focused(): #1 press=pygame.key.get_pressed() #2 for i in xrange(0, len(press)): if press[i]==1: name=pygame.key.name(i) #3 text=f1.render(name, True,(0,0,0)) screen.blit(text,(100, a)) a=a+100 pygame.display.update()
After running the script, press some keys, and you will see the names of the pressed keys on a white screen. I pressed <Left-Ctrl><Left-Shift><a> and got Figure 2 as my result.
There are three important functions used in the above script:
pygame.key.get_focused(): The Pygame window will handle the key events only if the window is focused. It returns True if the window is focused, and False if not.
pygame.key.get_pressed(): Returns a list of Boolean values, one for every key. It returns 1 if the key is pressed, and 0 if not.
pygame.key.name(argument): It returns the key name from the key constant value; for example, it returns “space” given
K_spaceas the argument. You can get the values easily by looking for
pydoc pygame.localsin a terminal).
There are many more key functions like
set_mods(), etc, which you can research on the Web. I am pretty sure that after understanding the above script, you will have no trouble getting user input.
Rotation is the key to making any shooting game. You can rotate the image both with the keyboard as well as the mouse — but, generally, rotation is done with the mouse, because movements are done using the keyboard. Rotation in Pygame can be done by using its
transform module. The script below demonstrates rotation with both, keyboard and mouse.
#rotation.py import pygame from pygame.locals import * from sys import exit pygame.init() screen=pygame.display.set_mode((640,480),0,0) pygame.display.set_caption("Rotation using transform module") image=pygame.image.load("naughty.png").convert_alpha() image2=image a=0 clock=pygame.time.Clock() #1 while True: for i in pygame.event.get(): if i.type==QUIT: exit() screen.fill((0,0,0)) rotation=pygame.mouse.get_rel() #2 buttonpress=pygame.mouse.get_pressed() #3 press=pygame.key.get_pressed() screen.blit(image2,(100-(image2.get_width()/2),100-(image2.get_height()/2))) #4 if rotation and buttonpress: a=a+rotation image2=pygame.transform.rotate(image, a) #5 if press[K_LEFT]: a=a+10 image2=pygame.transform.rotate(image, a) if press[K_RIGHT]: a=a-10 image2=pygame.transform.rotate(image, a) pygame.display.update() clock.tick(15)
Run the script and use the Left/Right arrow keys, or left-click and drag, to rotate the naughty-face icon, as shown in Figure 3.
Let’s understand the code. We loaded the
naughty.pngimage into the video display, and then we performed the following steps:
Clock()function is used to determine the speed of the game through frames. When used with the
tick(value)function (see the last line of the code), it decides how many frames per second should appear on the video display. When the value is 1, the frame rate is 1 per second. As you increase the value, the speed or frame-rate of the game increases. Thirty frames per second is recommended as the best frame rate for games, as every hardware is capable of providing this rate continuously.
pygame.mouse.get_rel(): It returns a tuple of the change in coordinates with respect to the previous position of the mouse.
pygame.mouse.get_pressed(): It returns a Boolean value for every mouse button (1 if a button is clicked, otherwise 0), in a tuple of three elements.
- There is a problem with the inbuilt rotate function: the rotated surface doesn’t have the same dimensions as the original image; so we can not
blitthe returned surface into the original image because that will lead to flickering, as well as wrong imposition of the surface. So we cut out the centre of the returned surface, from the coordinates where we want to draw. In this way, the centre of every image coincides, and we get the proper rotation.
- We use the
transformmodule and its
rotatefunction, which takes the image to be rotated, and the angle, as arguments. It returns the rotated surface, which we stored in the variable
image2. If the angle is positive, it will rotate anticlockwise, and if negative, then clockwise.
transform module is of great use. It has functions like
flip(), etc., which are required in almost every game. Explore more about them on the Web.
This is one of my favourite topics, and I bet will become one of yours too — after reading about it. First of all, animation is nothing but a continuous, fast display of sprite images from sprite sheets. You can also define an animation as a collection of images in a serial order. For an example, look at Figure 4, consisting of 7 images.
When they are used in a continuous manner with a high frame-rate, it appears to be an explosion. That’s how animation is created. You can create the whole game using a single sprite sheet; it depends on how you use that sprite sheet.
You must have a good knowledge of surfaces to work with sprites — that comes with experience. Traversing the sprites from the sprite sheet is what we learn next; there is a special sprites module to do this, but we will start from scratch.
#sprite.py import pygame from pygame.locals import * from sys import exit counter=0 def Update(): global counter counter=(counter+1)%7 def sprite(w, h): a= clock=pygame.time.Clock() screen=pygame.display.set_mode((200,200),0,24) image = pygame.image.load("Image4.png").convert_alpha() width, height=image.get_size() for i in xrange(int(width/w)): a.append(image.subsurface((i*w,0, w, h))) while True: for i in pygame.event.get(): if i.type==QUIT: exit() screen.fill((0,0,0)) screen.blit(a[counter],(100,100)) Update() pygame.display.update() clock.tick(5) sprite(20,20)
If you are good with the GIMP, you will find traversing easy, because it is just a game of giving dimensions. In the above script, the sprite sheet used has a width of 140 and height of 20. We have 7 properly and equally spaced images, so let’s have a width of 20 pixels for each image. Let’s work with a rectangular (square, in this case) surface of 20*20 for each image, and then we will store them in a list.
Image4.png must be in the same directory as the script — otherwise, use
os.path.join to construct the path.
We have created a function called
sprite() to cut the image into sub-surfaces of 20*20, and then store them in the list ‘a ‘ in a continuous manner. Then we will display them by incrementing the counter value, which is done in the
Update() function. That’s it — the animation is done, and you will see the “blast” on the output when you run the script.
I believe that after reading both parts of this series, you can build a small game. For game development, control over loops is a must. I turned the famous snake game (of Nokia fame) into a 2D practice game, and released it as an open source project on Google, so that others could use it to learn, and modify it in their own way. I named it “Hungry-snakes”, and released it under GPL v3. Get it from here. As of writing this, 125+ downloads have already happened. Feel free to ping me for suggestions and queries.