Skip to content

Commit 109a1d3

Browse files
authored
Merge pull request #504 from realpython/sg-turtle-game-space-invaders
Sg turtle game space invaders
2 parents ae17768 + 1927be4 commit 109a1d3

File tree

10 files changed

+1167
-0
lines changed

10 files changed

+1167
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import random
2+
import time
3+
import turtle
4+
5+
FRAME_RATE = 30 # Frames per second
6+
TIME_FOR_1_FRAME = 1 / FRAME_RATE # Seconds
7+
8+
CANNON_STEP = 10
9+
LASER_LENGTH = 20
10+
LASER_SPEED = 20
11+
ALIEN_SPAWN_INTERVAL = 1.2 # Seconds
12+
ALIEN_SPEED = 3.5
13+
14+
window = turtle.Screen()
15+
window.tracer(0)
16+
window.setup(0.5, 0.75)
17+
window.bgcolor(0.2, 0.2, 0.2)
18+
window.title("The Real Python Space Invaders")
19+
20+
LEFT = -window.window_width() / 2
21+
RIGHT = window.window_width() / 2
22+
TOP = window.window_height() / 2
23+
BOTTOM = -window.window_height() / 2
24+
FLOOR_LEVEL = 0.9 * BOTTOM
25+
GUTTER = 0.025 * window.window_width()
26+
27+
# Create laser cannon
28+
cannon = turtle.Turtle()
29+
cannon.penup()
30+
cannon.color(1, 1, 1)
31+
cannon.shape("square")
32+
cannon.setposition(0, FLOOR_LEVEL)
33+
cannon.cannon_movement = 0 # -1, 0 or 1 for left, stationary, right
34+
35+
# Create turtle for writing text
36+
text = turtle.Turtle()
37+
text.penup()
38+
text.hideturtle()
39+
text.setposition(LEFT * 0.8, TOP * 0.8)
40+
text.color(1, 1, 1)
41+
42+
lasers = []
43+
aliens = []
44+
45+
46+
def draw_cannon():
47+
cannon.clear()
48+
cannon.turtlesize(1, 4) # Base
49+
cannon.stamp()
50+
cannon.sety(FLOOR_LEVEL + 10)
51+
cannon.turtlesize(1, 1.5) # Next tier
52+
cannon.stamp()
53+
cannon.sety(FLOOR_LEVEL + 20)
54+
cannon.turtlesize(0.8, 0.3) # Tip of cannon
55+
cannon.stamp()
56+
cannon.sety(FLOOR_LEVEL)
57+
58+
59+
def move_left():
60+
cannon.cannon_movement = -1
61+
62+
63+
def move_right():
64+
cannon.cannon_movement = 1
65+
66+
67+
def stop_cannon_movement():
68+
cannon.cannon_movement = 0
69+
70+
71+
def create_laser():
72+
laser = turtle.Turtle()
73+
laser.penup()
74+
laser.color(1, 0, 0)
75+
laser.hideturtle()
76+
laser.setposition(cannon.xcor(), cannon.ycor())
77+
laser.setheading(90)
78+
# Move laser to just above cannon tip
79+
laser.forward(20)
80+
# Prepare to draw the laser
81+
laser.pendown()
82+
laser.pensize(5)
83+
84+
lasers.append(laser)
85+
86+
87+
def move_laser(laser):
88+
laser.clear()
89+
laser.forward(LASER_SPEED)
90+
# Draw the laser
91+
laser.forward(LASER_LENGTH)
92+
laser.forward(-LASER_LENGTH)
93+
94+
95+
def create_alien():
96+
alien = turtle.Turtle()
97+
alien.penup()
98+
alien.turtlesize(1.5)
99+
alien.setposition(
100+
random.randint(
101+
int(LEFT + GUTTER),
102+
int(RIGHT - GUTTER),
103+
),
104+
TOP,
105+
)
106+
alien.shape("turtle")
107+
alien.setheading(-90)
108+
alien.color(random.random(), random.random(), random.random())
109+
aliens.append(alien)
110+
111+
112+
def remove_sprite(sprite, sprite_list):
113+
sprite.clear()
114+
sprite.hideturtle()
115+
window.update()
116+
sprite_list.remove(sprite)
117+
turtle.turtles().remove(sprite)
118+
119+
120+
# Key bindings
121+
window.onkeypress(move_left, "Left")
122+
window.onkeypress(move_right, "Right")
123+
window.onkeyrelease(stop_cannon_movement, "Left")
124+
window.onkeyrelease(stop_cannon_movement, "Right")
125+
window.onkeypress(create_laser, "space")
126+
window.onkeypress(turtle.bye, "q")
127+
window.listen()
128+
129+
draw_cannon()
130+
131+
# Game loop
132+
alien_timer = 0
133+
game_timer = time.time()
134+
score = 0
135+
game_running = True
136+
while game_running:
137+
timer_this_frame = time.time()
138+
139+
time_elapsed = time.time() - game_timer
140+
text.clear()
141+
text.write(
142+
f"Time: {time_elapsed:5.1f}s\nScore: {score:5}",
143+
font=("Courier", 20, "bold"),
144+
)
145+
# Move cannon
146+
new_x = cannon.xcor() + CANNON_STEP * cannon.cannon_movement
147+
if LEFT + GUTTER <= new_x <= RIGHT - GUTTER:
148+
cannon.setx(new_x)
149+
draw_cannon()
150+
# Move all lasers
151+
for laser in lasers.copy():
152+
move_laser(laser)
153+
# Remove laser if it goes off screen
154+
if laser.ycor() > TOP:
155+
remove_sprite(laser, lasers)
156+
break
157+
# Check for collision with aliens
158+
for alien in aliens.copy():
159+
if laser.distance(alien) < 20:
160+
remove_sprite(laser, lasers)
161+
remove_sprite(alien, aliens)
162+
score += 1
163+
break
164+
# Spawn new aliens when time interval elapsed
165+
if time.time() - alien_timer > ALIEN_SPAWN_INTERVAL:
166+
create_alien()
167+
alien_timer = time.time()
168+
169+
# Move all aliens
170+
for alien in aliens:
171+
alien.forward(ALIEN_SPEED)
172+
# Check for game over
173+
if alien.ycor() < FLOOR_LEVEL:
174+
game_running = False
175+
break
176+
177+
time_for_this_frame = time.time() - timer_this_frame
178+
if time_for_this_frame < TIME_FOR_1_FRAME:
179+
time.sleep(TIME_FOR_1_FRAME - time_for_this_frame)
180+
window.update()
181+
182+
splash_text = turtle.Turtle()
183+
splash_text.hideturtle()
184+
splash_text.color(1, 1, 1)
185+
splash_text.write("GAME OVER", font=("Courier", 40, "bold"), align="center")
186+
turtle.done()
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import turtle
2+
3+
window = turtle.Screen()
4+
window.setup(0.5, 0.75)
5+
window.bgcolor(0.2, 0.2, 0.2)
6+
window.title("The Real Python Space Invaders")
7+
8+
LEFT = -window.window_width() / 2
9+
RIGHT = window.window_width() / 2
10+
TOP = window.window_height() / 2
11+
BOTTOM = -window.window_height() / 2
12+
FLOOR_LEVEL = 0.9 * BOTTOM
13+
14+
# Create laser cannon
15+
cannon = turtle.Turtle()
16+
cannon.penup()
17+
cannon.color(1, 1, 1)
18+
cannon.shape("square")
19+
cannon.setposition(0, FLOOR_LEVEL)
20+
21+
# Draw cannon
22+
cannon.turtlesize(1, 4) # Base
23+
cannon.stamp()
24+
cannon.sety(FLOOR_LEVEL + 10)
25+
cannon.turtlesize(1, 1.5) # Next tier
26+
cannon.stamp()
27+
cannon.sety(FLOOR_LEVEL + 20)
28+
cannon.turtlesize(0.8, 0.3) # Tip of cannon
29+
cannon.stamp()
30+
cannon.sety(FLOOR_LEVEL)
31+
32+
turtle.done()
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import turtle
2+
3+
CANNON_STEP = 10
4+
5+
window = turtle.Screen()
6+
window.tracer(0)
7+
window.setup(0.5, 0.75)
8+
window.bgcolor(0.2, 0.2, 0.2)
9+
window.title("The Real Python Space Invaders")
10+
11+
LEFT = -window.window_width() / 2
12+
RIGHT = window.window_width() / 2
13+
TOP = window.window_height() / 2
14+
BOTTOM = -window.window_height() / 2
15+
FLOOR_LEVEL = 0.9 * BOTTOM
16+
GUTTER = 0.025 * window.window_width()
17+
18+
# Create laser cannon
19+
cannon = turtle.Turtle()
20+
cannon.penup()
21+
cannon.color(1, 1, 1)
22+
cannon.shape("square")
23+
cannon.setposition(0, FLOOR_LEVEL)
24+
25+
26+
def draw_cannon():
27+
cannon.clear()
28+
cannon.turtlesize(1, 4) # Base
29+
cannon.stamp()
30+
cannon.sety(FLOOR_LEVEL + 10)
31+
cannon.turtlesize(1, 1.5) # Next tier
32+
cannon.stamp()
33+
cannon.sety(FLOOR_LEVEL + 20)
34+
cannon.turtlesize(0.8, 0.3) # Tip of cannon
35+
cannon.stamp()
36+
cannon.sety(FLOOR_LEVEL)
37+
window.update()
38+
39+
40+
def move_left():
41+
new_x = cannon.xcor() - CANNON_STEP
42+
if new_x >= LEFT + GUTTER:
43+
cannon.setx(new_x)
44+
draw_cannon()
45+
46+
47+
def move_right():
48+
new_x = cannon.xcor() + CANNON_STEP
49+
if new_x <= RIGHT - GUTTER:
50+
cannon.setx(new_x)
51+
draw_cannon()
52+
53+
54+
window.onkeypress(move_left, "Left")
55+
window.onkeypress(move_right, "Right")
56+
window.onkeypress(turtle.bye, "q")
57+
window.listen()
58+
59+
draw_cannon()
60+
61+
turtle.done()
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import turtle
2+
3+
CANNON_STEP = 10
4+
LASER_LENGTH = 20
5+
LASER_SPEED = 10
6+
7+
window = turtle.Screen()
8+
window.tracer(0)
9+
window.setup(0.5, 0.75)
10+
window.bgcolor(0.2, 0.2, 0.2)
11+
window.title("The Real Python Space Invaders")
12+
13+
LEFT = -window.window_width() / 2
14+
RIGHT = window.window_width() / 2
15+
TOP = window.window_height() / 2
16+
BOTTOM = -window.window_height() / 2
17+
FLOOR_LEVEL = 0.9 * BOTTOM
18+
GUTTER = 0.025 * window.window_width()
19+
20+
# Create laser cannon
21+
cannon = turtle.Turtle()
22+
cannon.penup()
23+
cannon.color(1, 1, 1)
24+
cannon.shape("square")
25+
cannon.setposition(0, FLOOR_LEVEL)
26+
27+
lasers = []
28+
29+
30+
def draw_cannon():
31+
cannon.clear()
32+
cannon.turtlesize(1, 4) # Base
33+
cannon.stamp()
34+
cannon.sety(FLOOR_LEVEL + 10)
35+
cannon.turtlesize(1, 1.5) # Next tier
36+
cannon.stamp()
37+
cannon.sety(FLOOR_LEVEL + 20)
38+
cannon.turtlesize(0.8, 0.3) # Tip of cannon
39+
cannon.stamp()
40+
cannon.sety(FLOOR_LEVEL)
41+
42+
43+
def move_left():
44+
new_x = cannon.xcor() - CANNON_STEP
45+
if new_x >= LEFT + GUTTER:
46+
cannon.setx(new_x)
47+
draw_cannon()
48+
49+
50+
def move_right():
51+
new_x = cannon.xcor() + CANNON_STEP
52+
if new_x <= RIGHT - GUTTER:
53+
cannon.setx(new_x)
54+
draw_cannon()
55+
56+
57+
def create_laser():
58+
laser = turtle.Turtle()
59+
laser.penup()
60+
laser.color(1, 0, 0)
61+
laser.hideturtle()
62+
laser.setposition(cannon.xcor(), cannon.ycor())
63+
laser.setheading(90)
64+
# Move laser to just above cannon tip
65+
laser.forward(20)
66+
# Prepare to draw the laser
67+
laser.pendown()
68+
laser.pensize(5)
69+
70+
lasers.append(laser)
71+
72+
73+
def move_laser(laser):
74+
laser.clear()
75+
laser.forward(LASER_SPEED)
76+
# Draw the laser
77+
laser.forward(LASER_LENGTH)
78+
laser.forward(-LASER_LENGTH)
79+
80+
81+
# Key bindings
82+
window.onkeypress(move_left, "Left")
83+
window.onkeypress(move_right, "Right")
84+
window.onkeypress(create_laser, "space")
85+
window.onkeypress(turtle.bye, "q")
86+
window.listen()
87+
88+
draw_cannon()
89+
90+
# Game loop
91+
while True:
92+
# Move all lasers
93+
for laser in lasers.copy():
94+
move_laser(laser)
95+
# Remove laser if it goes off screen
96+
if laser.ycor() > TOP:
97+
laser.clear()
98+
laser.hideturtle()
99+
lasers.remove(laser)
100+
turtle.turtles().remove(laser)
101+
window.update()
102+
103+
turtle.done()

0 commit comments

Comments
 (0)