Add Laser¶
Since Zork is hurling Asteroids at our defenceless space ship, we better give it something to fight back. In this lesson will will arm the space ship with a laser.
Shooting the Laser¶
Planning¶
We want to ship to spawn a laser whenever we press the space key.
This involves:
creating a new laser class
adding an event handler of the space key which create a laser object
make the laser object move across the room when spawned
deleting the laser object when it leaves the room
We already know how to do all this, so lets get to the coding.
Coding¶
Objects/Laser.py
¶
Create a new file in the Objects
folder called Laser.py
and then add the following code to it.
1from GameFrame import RoomObject, Globals
2
3class Laser(RoomObject):
4 """
5 Class for the lasers shot by the Ship
6 """
7
8 def __init__(self, room, x, y):
9 """
10 Inistialise the laser
11 """
12 # include attributes and methods from RoomObject
13 RoomObject.__init__(self, room, x, y)
14
15 # set image
16 image = self.load_image("Laser.png")
17 self.set_image(image, 33, 9)
18
19 # set movement
20 self.set_direction(0, 20)
21
22 def step(self):
23 """
24 Determine what happens to the laser on each tick of the game clock
25 """
26 self.outside_of_room()
27
28 def outside_of_room(self):
29 """
30 removes laser if it has exited the room
31 """
32 if self.x > Globals.SCREEN_WIDTH:
33 self.room.delete_object(self)
The only change worth noting is:
line 32: since the laser is moving right, we delete it after it’s
x
has moved past the screen width
Save Objects/Laser.py
Objects/__init__.py
¶
We need to tell GameFrame that we have added a new RoomObject to the Objects
folder.
Open Objects/__init__.py
and add the highlighted code below:
1from Objects.Title import Title
2from Objects.Ship import Ship
3from Objects.Zork import Zork
4from Objects.Asteroid import Asteroid
5from Objects.Laser import Laser
Save and close Objects/__init__.py
Objects/Ship.py
¶
Open Objects/Ship.py
and add the highlighted code below:
1from GameFrame import RoomObject, Globals
2from Objects.Laser import Laser
3import pygame
4
5class Ship(RoomObject):
6 """
7 A class for the player's avitar (the Ship)
8 """
9
10 def __init__(self, room, x, y):
11 """
12 Initialise the Ship object
13 """
14 RoomObject.__init__(self, room, x, y)
15
16 # set image
17 image = self.load_image("Ship.png")
18 self.set_image(image,100,100)
19
20 # register events
21 self.handle_key_events = True
22
23 def key_pressed(self, key):
24 """
25 Respond to keypress up and down
26 """
27
28 if key[pygame.K_w]:
29 self.y_speed = -10
30 elif key[pygame.K_s]:
31 self.y_speed = 10
32 if key[pygame.K_SPACE]:
33 self.shoot_laser()
34
35 def keep_in_room(self):
36 """
37 Keeps the ship inside the room
38 """
39 if self.y < 0:
40 self.y = 0
41 elif self.y + self.height> Globals.SCREEN_HEIGHT:
42 self.y = Globals.SCREEN_HEIGHT - self.height
43
44 def step(self):
45 """
46 Determine what happens to the Ship on each click of the game clock
47 """
48 self.keep_in_room()
49
50 def shoot_laser(self):
51 """
52 Shoots a laser from the ship
53 """
54 new_laser = Laser(self.room,
55 self.x + self.width,
56 self.y + self.height/2 - 4)
57 self.room.add_room_object(new_laser)
Lets break that down:
line 2: since we want the Ship to create a Laser object, we need to import the Laser class
lines 32-33: handles the event of the space key being pressed
lines 50-57:
creates a new laser object at the middle of the right side of the ship
adds the laser object to the Room the Ship is in
Spliting instructions over multiple lines
Lines 54 - 56 are actually one instruction split over 3 lines to make it easier to read.
The Python Style Guide recommends that lines of code shouldn’t be more than 79 characters long. Although this isn’t followed religiously, it is frown upon to write lines of code that are two long to read without scrolling sideway.
You can split arugements by placing a linebreak (pressing Enter) after any ,
(like in the code above). You can also place line breaks after ,
in list, dictionaries and tuples.
Placing long expressions inside parenthesis ( )
allows you to place a linebreak after any alrogithmic or Boolean operand.
This method can also be used to logically group operations in a exteremely long expression.
Save Ship.py
and then test the code by running MainController.py
.
Restricting the laser¶
When testing, did you notice what happens if you hold down the space key? There is a constant stream of lasers flowing across the screen. You might even notice that some of the other object freeze. What is that?
Since our event lister is tied to the frame rate, it is detecting the space key press 30 times a second, and, consequently, spawning 30 lasers a second. That’s a lot of lasers. We need to work out a way to restrict how many lasers can be shot a second.
Planning¶
To address this issue we will limit how frequently the Ship can spawn a Laser. To do this we are going to use a flag variable.
Flag variables
Flag variables, also known as Boolean flags, are variables used in computer programming to represent the state of a condition or a specific situation. Flag variables act as signals, indicating whether a particular condition is true or false, and they help control the flow of a program.
Specifically we are going to create a flag variable called can_shoot
initialised to True
. A laser can only spawn when can_shoot
is True
. Every time a laser is spawned, can_shoot
will be set to False
and a timer is started. When that timer ends, can_shoot
is set back to True
.
Lets look at that in an IPO table.
Coding¶
Objects/Ship.py
¶
Restricting the number of lasers is all about laser spawning, which happens in the Ship class so go back to Objects/Ship.py
and change the highlighted code.
1from GameFrame import RoomObject, Globals
2from Objects.Laser import Laser
3import pygame
4
5class Ship(RoomObject):
6 """
7 A class for the player's avitar (the Ship)
8 """
9
10 def __init__(self, room, x, y):
11 """
12 Initialise the Ship object
13 """
14 RoomObject.__init__(self, room, x, y)
15
16 # set image
17 image = self.load_image("Ship.png")
18 self.set_image(image,100,100)
19
20 # register events
21 self.handle_key_events = True
22
23 self.can_shoot = True
24
25 def key_pressed(self, key):
26 """
27 Respond to keypress up and down
28 """
29
30 if key[pygame.K_w]:
31 self.y_speed = -10
32 elif key[pygame.K_s]:
33 self.y_speed = 10
34 if key[pygame.K_SPACE]:
35 self.shoot_laser()
36
37 def keep_in_room(self):
38 """
39 Keeps the ship inside the room
40 """
41 if self.y < 0:
42 self.y = 0
43 elif self.y + self.height> Globals.SCREEN_HEIGHT:
44 self.y = Globals.SCREEN_HEIGHT - self.height
45
46 def step(self):
47 """
48 Determine what happens to the Ship on each click of the game clock
49 """
50 self.keep_in_room()
51
52 def shoot_laser(self):
53 """
54 Shoots a laser from the ship
55 """
56 if self.can_shoot:
57 new_laser = Laser(self.room,
58 self.x + self.width,
59 self.y + self.height/2 - 4)
60 self.room.add_room_object(new_laser)
61 self.can_shoot = False
62 self.set_timer(10,self.reset_shot)
63
64 def reset_shot(self):
65 """
66 Allows ship to shoot again
67 """
68 self.can_shoot = True
Investigating those changes:
line 23: creates our flag variable and initializes it to
True
line 56: checks whether the Ship is allows to spawn a laser
lines 57-60: these haven’t changed, but their indentation has increased one level to place them inside the if statement.
line 61: after spawning a laser, the
can_shoot
flag is set toFalse
to prevent other lasers being spawnedline 62: sets a timer which will call the
reset_shot
function when finished.lines 64-68: this function will be called when the timer has reached 0. It changed the
can_shoot
flag back toTrue
.
Save Ship.py
then run MainController.py
to test that everything is working as planned.
Commit and Push¶
We have finished and tested another section of code so we should make a Git commit.
To do this:
In GitHub Desktop go to the bottom left-hand box and write into the summary Added lasers.
Click on Commit to main
Click on Push origin
Now the work from this lesson is committed and synced with the online repo.
Completed File States¶
Below are all the files we used in this lesson in their finished state. Use this to check if your code is correct.
Objects/Laser.py
¶
1from GameFrame import RoomObject, Globals
2
3class Laser(RoomObject):
4 """
5 Class for the lasers shot by the Ship
6 """
7
8 def __init__(self, room, x, y):
9 """
10 Inistialise the laser
11 """
12 # include attributes and methods from RoomObject
13 RoomObject.__init__(self, room, x, y)
14
15 # set image
16 image = self.load_image("Laser.png")
17 self.set_image(image, 33, 9)
18
19 # set movement
20 self.set_direction(0, 20)
21
22 def step(self):
23 """
24 Determine what happens to the laser on each tick of the game clock
25 """
26 self.outside_of_room()
27
28 def outside_of_room(self):
29 """
30 removes laser if it has exited the room
31 """
32 if self.x > Globals.SCREEN_WIDTH:
33 self.room.delete_object(self)
Objects/__init__.py
¶
1from Objects.Title import Title
2from Objects.Ship import Ship
3from Objects.Zork import Zork
4from Objects.Asteroid import Asteroid
5from Objects.Laser import Laser
Objects/Ship.py
¶
1from GameFrame import RoomObject, Globals
2from Objects.Laser import Laser
3import pygame
4
5class Ship(RoomObject):
6 """
7 A class for the player's avitar (the Ship)
8 """
9
10 def __init__(self, room, x, y):
11 """
12 Initialise the Ship object
13 """
14 RoomObject.__init__(self, room, x, y)
15
16 # set image
17 image = self.load_image("Ship.png")
18 self.set_image(image,100,100)
19
20 # register events
21 self.handle_key_events = True
22
23 self.can_shoot = True
24
25 def key_pressed(self, key):
26 """
27 Respond to keypress up and down
28 """
29
30 if key[pygame.K_w]:
31 self.y_speed = -10
32 elif key[pygame.K_s]:
33 self.y_speed = 10
34 if key[pygame.K_SPACE]:
35 self.shoot_laser()
36
37 def keep_in_room(self):
38 """
39 Keeps the ship inside the room
40 """
41 if self.y < 0:
42 self.y = 0
43 elif self.y + self.height> Globals.SCREEN_HEIGHT:
44 self.y = Globals.SCREEN_HEIGHT - self.height
45
46 def step(self):
47 """
48 Determine what happens to the Ship on each click of the game clock
49 """
50 self.keep_in_room()
51
52 def shoot_laser(self):
53 """
54 Shoots a laser from the ship
55 """
56 if self.can_shoot:
57 new_laser = Laser(self.room,
58 self.x + self.width,
59 self.y + self.height/2 - 4)
60 self.room.add_room_object(new_laser)
61 self.can_shoot = False
62 self.set_timer(10,self.reset_shot)
63
64 def reset_shot(self):
65 """
66 Allows ship to shoot again
67 """
68 self.can_shoot = True