Trying to create two objects at the same time but it creates one
Question:
I’m trying to develop a simple game while I’m learning python. Game is not complex, random cars (which are squares) are spawning at right of the screen and they are going to left. We are a turtle, trying to avoid them and make it to the top of the screen.
The problem is my code below doesn’t spawn two objects at the same time, it spawns just one.
from turtle import Screen
from turt import Turt
from spawnpoint import SpawnPoint
from cars import Car
import random
screen = Screen()
screen.setup(1000, 700)
screen.title("Cars and Turtle")
screen.bgcolor("gray")
turt = Turt()
is_game_on = True
screen.listen()
screen.onkey(turt.move_up, "Up")
spawn_points_ycords = [300, 200, 100, 0, -100, -200]
spawn_1 = SpawnPoint(spawn_points_ycords[0])
spawn_2 = SpawnPoint(spawn_points_ycords[1])
spawn_3 = SpawnPoint(spawn_points_ycords[2])
spawn_4 = SpawnPoint(spawn_points_ycords[3])
spawn_5 = SpawnPoint(spawn_points_ycords[4])
spawn_6 = SpawnPoint(spawn_points_ycords[5])
spawn_points = [spawn_1, spawn_2, spawn_3, spawn_4, spawn_5, spawn_6]
while is_game_on:
for n in range(60):
if n == 59:
random_spawn = spawn_points[random.randint(0, len(spawn_points)-1)]
random_spawn_2 = spawn_points[random.randint(0, len(spawn_points)-1)]
while random_spawn_2 == random_spawn:
random_spawn_2 = spawn_points[random.randint(0, len(spawn_points) - 1)]
random_spawn_car = Car(random_spawn.spawn.ycor())
random_spawn_2_car = Car(random_spawn_2.spawn.ycor())
screen.exitonclick()
My spawn points class code:
from turtle import Turtle
class SpawnPoint:
def __init__(self, ycor):
self.spawn = Turtle()
self.spawn.hideturtle()
self.spawn.speed(0)
self.spawn.penup()
self.spawn.goto(600, ycor)
self.spawn.showturtle()
self.new_car = None
and my car class code:
from turtle import Turtle
import random
class Car:
def __init__(self, ycor):
self.body = Turtle()
self.body.hideturtle()
self.body.penup()
self.body.shape("square")
self.colors = ["black", "red", "orange", "blue", "green", "yellow"]
self.body.color(self.colors[random.randint(0, len(self.colors)-1)])
self.body.shapesize(1, 5, 0)
self.body.speed(2)
self.body.goto(700, ycor)
self.body.showturtle()
self.body.goto(-700, ycor)
I can’t figure it out to solve this bug. I’m using Turtle module.
Answers:
The two car objects are created, but the problem is that you haven’t implemented real-time movement for multiple turtles, so the first turtle completes its 5-second journey across the screen before the second one is created or begins moving.
When you encounter problems like this, my suggestion is to strip the problem down to a minimal, reproducible example. This process makes the problem obvious by removing the noise. For example, if you move the spawn and destination points onto the visible screen, the problem becomes clear.
Here’s an even more minimal demonstration:
from turtle import Turtle
t = Turtle()
tt = Turtle()
t.speed(2)
tt.speed(2)
t.goto(100, 100)
tt.goto(200, 100)
t.Screen().exitonclick()
When you run this, you’ll see that t
moves from 0, 0 to 100, 100 in about a second. Only once t
arrives at the destination does tt
even begin moving. Adding print
statements on each line is another way to see that each goto
blocks the script completely until the slow movement completes. The simultaneous movement you expect is not the default behavior of turtle.
The typical solution is to use tracer(0)
to disable the internal update/rendering loop that turtle uses to smooth out goto
s. Once you’ve done this, you can reposition turtles at will and call turtle.update()
to render a frame. It’s up to you to reimplement smooth movement, which is not difficult to do.
Also worth noting, your exitonclick
call is never reached and the for
loop with if n == 59:
is pointless. Car
s are never garbage collected, and probably shouldn’t be doing so much work in the initializer. Kudos for using composition rather than inheritance for your classes, though.
I’d also caution against adding complexity like multiple classes and files before you’ve convinced yourself that the basics are operational. If something as simple as movement isn’t working as you expect, all of that other stuff only gets in the way of debugging. Run your code often, building up guarantees about its behavior as you work.
Here’s a quick sketch of how you might begin to redesign your project. It’s based on my recommended real-time setup for turtle.
import turtle
class Car:
def __init__(self, x, y, speed):
self.x = x
self.y = y
self.speed = speed
self.body = turtle.Turtle()
self.body.shape("square")
self.body.penup()
self.body.goto(x, y)
def move(self):
self.x -= self.speed
w = turtle.screensize()[0]
if self.x < -w:
self.x = w
self.body.goto(self.x, self.y)
def tick():
for action in keys_pressed:
actions[action]()
for car in cars:
car.move()
turtle.update()
win.ontimer(tick, frame_delay_ms)
if __name__ == "__main__":
w, h = turtle.screensize()
turtle.tracer(0)
t = turtle.Turtle()
t.penup()
t.goto(0, -h + 50)
t.left(90)
cars = [
Car(w, -200, 3),
Car(w, -100, 5),
Car(w, 0, 4.5),
Car(w, 100, 4),
Car(w, 200, 6),
]
frame_delay_ms = 1000 // 30
step_speed = 10
actions = dict(
u=lambda: t.forward(step_speed),
)
win = turtle.Screen()
keys_pressed = set()
win.onkeypress(lambda: keys_pressed.add("u"), "Up")
win.onkeyrelease(lambda: keys_pressed.remove("u"), "Up")
win.listen()
tick()
win.exitonclick()
I’m trying to develop a simple game while I’m learning python. Game is not complex, random cars (which are squares) are spawning at right of the screen and they are going to left. We are a turtle, trying to avoid them and make it to the top of the screen.
The problem is my code below doesn’t spawn two objects at the same time, it spawns just one.
from turtle import Screen
from turt import Turt
from spawnpoint import SpawnPoint
from cars import Car
import random
screen = Screen()
screen.setup(1000, 700)
screen.title("Cars and Turtle")
screen.bgcolor("gray")
turt = Turt()
is_game_on = True
screen.listen()
screen.onkey(turt.move_up, "Up")
spawn_points_ycords = [300, 200, 100, 0, -100, -200]
spawn_1 = SpawnPoint(spawn_points_ycords[0])
spawn_2 = SpawnPoint(spawn_points_ycords[1])
spawn_3 = SpawnPoint(spawn_points_ycords[2])
spawn_4 = SpawnPoint(spawn_points_ycords[3])
spawn_5 = SpawnPoint(spawn_points_ycords[4])
spawn_6 = SpawnPoint(spawn_points_ycords[5])
spawn_points = [spawn_1, spawn_2, spawn_3, spawn_4, spawn_5, spawn_6]
while is_game_on:
for n in range(60):
if n == 59:
random_spawn = spawn_points[random.randint(0, len(spawn_points)-1)]
random_spawn_2 = spawn_points[random.randint(0, len(spawn_points)-1)]
while random_spawn_2 == random_spawn:
random_spawn_2 = spawn_points[random.randint(0, len(spawn_points) - 1)]
random_spawn_car = Car(random_spawn.spawn.ycor())
random_spawn_2_car = Car(random_spawn_2.spawn.ycor())
screen.exitonclick()
My spawn points class code:
from turtle import Turtle
class SpawnPoint:
def __init__(self, ycor):
self.spawn = Turtle()
self.spawn.hideturtle()
self.spawn.speed(0)
self.spawn.penup()
self.spawn.goto(600, ycor)
self.spawn.showturtle()
self.new_car = None
and my car class code:
from turtle import Turtle
import random
class Car:
def __init__(self, ycor):
self.body = Turtle()
self.body.hideturtle()
self.body.penup()
self.body.shape("square")
self.colors = ["black", "red", "orange", "blue", "green", "yellow"]
self.body.color(self.colors[random.randint(0, len(self.colors)-1)])
self.body.shapesize(1, 5, 0)
self.body.speed(2)
self.body.goto(700, ycor)
self.body.showturtle()
self.body.goto(-700, ycor)
I can’t figure it out to solve this bug. I’m using Turtle module.
The two car objects are created, but the problem is that you haven’t implemented real-time movement for multiple turtles, so the first turtle completes its 5-second journey across the screen before the second one is created or begins moving.
When you encounter problems like this, my suggestion is to strip the problem down to a minimal, reproducible example. This process makes the problem obvious by removing the noise. For example, if you move the spawn and destination points onto the visible screen, the problem becomes clear.
Here’s an even more minimal demonstration:
from turtle import Turtle
t = Turtle()
tt = Turtle()
t.speed(2)
tt.speed(2)
t.goto(100, 100)
tt.goto(200, 100)
t.Screen().exitonclick()
When you run this, you’ll see that t
moves from 0, 0 to 100, 100 in about a second. Only once t
arrives at the destination does tt
even begin moving. Adding print
statements on each line is another way to see that each goto
blocks the script completely until the slow movement completes. The simultaneous movement you expect is not the default behavior of turtle.
The typical solution is to use tracer(0)
to disable the internal update/rendering loop that turtle uses to smooth out goto
s. Once you’ve done this, you can reposition turtles at will and call turtle.update()
to render a frame. It’s up to you to reimplement smooth movement, which is not difficult to do.
Also worth noting, your exitonclick
call is never reached and the for
loop with if n == 59:
is pointless. Car
s are never garbage collected, and probably shouldn’t be doing so much work in the initializer. Kudos for using composition rather than inheritance for your classes, though.
I’d also caution against adding complexity like multiple classes and files before you’ve convinced yourself that the basics are operational. If something as simple as movement isn’t working as you expect, all of that other stuff only gets in the way of debugging. Run your code often, building up guarantees about its behavior as you work.
Here’s a quick sketch of how you might begin to redesign your project. It’s based on my recommended real-time setup for turtle.
import turtle
class Car:
def __init__(self, x, y, speed):
self.x = x
self.y = y
self.speed = speed
self.body = turtle.Turtle()
self.body.shape("square")
self.body.penup()
self.body.goto(x, y)
def move(self):
self.x -= self.speed
w = turtle.screensize()[0]
if self.x < -w:
self.x = w
self.body.goto(self.x, self.y)
def tick():
for action in keys_pressed:
actions[action]()
for car in cars:
car.move()
turtle.update()
win.ontimer(tick, frame_delay_ms)
if __name__ == "__main__":
w, h = turtle.screensize()
turtle.tracer(0)
t = turtle.Turtle()
t.penup()
t.goto(0, -h + 50)
t.left(90)
cars = [
Car(w, -200, 3),
Car(w, -100, 5),
Car(w, 0, 4.5),
Car(w, 100, 4),
Car(w, 200, 6),
]
frame_delay_ms = 1000 // 30
step_speed = 10
actions = dict(
u=lambda: t.forward(step_speed),
)
win = turtle.Screen()
keys_pressed = set()
win.onkeypress(lambda: keys_pressed.add("u"), "Up")
win.onkeyrelease(lambda: keys_pressed.remove("u"), "Up")
win.listen()
tick()
win.exitonclick()