2

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.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
firat
  • 25
  • 6
  • 1
    I don't see where you use the class SpawnPoint. In your code it's unclear what `spawn_points` is, since you never initialize this variable. Please, instead of posting 3 code chunks, post a single minimal and reproducible example –  Aug 03 '22 at 14:22
  • Yes, I did that because I'm trying to spawn 2 cars every 1 second in 60. Is it wrong way to achieve this? Sorry by the way for posting this way but I thought that it would be more mess if I put the whole code. Simply I imported the spawnpoint class top of the code like this: from spawnpoint import SpawnPoint – firat Aug 03 '22 at 14:27
  • Could you add more context for how `spawn-points` and `is_game_on` are initialized? – Joe Carboni Aug 03 '22 at 14:28
  • We cannot reproduce - `NameError: name 'spawn_points' is not defined`. Please read [mre]. – wwii Aug 03 '22 at 14:28
  • Sorry for not clarify the whole code not very well (I'm very new to StackOverFlow and programming actually). I just thought that my problem would be my while loop so I only put that instead of the whole code. I'm going to put my whole code. – firat Aug 03 '22 at 14:30
  • your code doesn't control anywhere the speed of spawning. you are running an infinite loop that will spawn milions of turtles per second –  Aug 03 '22 at 14:35
  • I suspect that the spawn point are all the same due to how you instance turtle, you don't need that class, you can just save X and Y coordinates. And another thing, you can use a loop to generate the 6 spawn points `spawn_points = [SpawnPoint(x) for x in range(6)]` – Axeltherabbit Aug 03 '22 at 15:31
  • Thank you so much. I'm trying to do that with coordinates now. – firat Aug 03 '22 at 15:38

1 Answers1

2

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 gotos. 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. Cars 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()
ggorlen
  • 44,755
  • 7
  • 76
  • 106
  • Thank you so much! I'm very new at this, and your posts made my mind more clear about coding the things. Thanks again. – firat Aug 03 '22 at 17:18