1

Hi I am new to python and new to stack exchange. I am trying to create a space invaders game using turtle, but I am having an issue in which my gun cannot shoot until all the aliens have finished invading. I think I'm having this problem because either the invasion function and shoot function are not running simultaneously, or because turtle does not allow more than one turtle to move at once.

I attempted to use threading to run both functions in parallel, but it did not solve the issue, the gun could not fire until the invasion was complete. Any help would be greatly appreciated! (I am very new to python, so I apoligize for the messiness of my code)

import turtle
import random
import threading
from threading import Thread

screen = turtle.Screen()
screen.setup(400, 500)
screen.bgpic("/Users/benmartinez/Desktop/Space_Invaders_BG.gif")

turtle.right(90)
turtle.pu()
turtle.forward(200)
turtle.pd()
turtle.right(90)
turtle.forward(200)
turtle.right(90)
turtle.forward(500)
turtle.right(90)
turtle.forward(400)
turtle.right(90)
turtle.forward(500)
turtle.right(90)
turtle.forward(200)

turtle.right(180)
turtle.forward(20)

turtle.left(90)

turtle.circle(20, 180)

turtle.left(90)
turtle.forward(20)
turtle.left(90)

homex = turtle.xcor()
homey = turtle.ycor()

def shoot():
    turtle.color('red')
    turtle.showturtle()
    turtle.pu()
    turtle.onscreenclick(turtle.goto)
    turtle.goto(homex, homey)
    turtle.onclick(shoot())

def invaders():
    invader_initial_position = random.randint(-200, 200)
    i = turtle.Turtle()
    i.shape('triangle')
    i.color('green')
    i.pu()
    i.hideturtle()
    i.goto(invader_initial_position, 300)
    i.showturtle()
    i.right(90)
    i.speed(1)
    i.forward(500)

def invasion():
    x = 0
    while x < 10:
        invaders()
        x += 1

#if __name__ == '__main__':
Thread(target = invasion()).start()
Thread(target = shoot()).start()
Billiam
  • 165
  • 1
  • 13
  • see [Create more than two turtles and moving them](https://stackoverflow.com/questions/47382389/create-more-than-two-turtles-and-moving-them) - it uses `ontimer()` and `"small steps"` to move many turtles. – furas Dec 03 '17 at 04:07
  • BTW: `onclick()` (similar like `onscreenclick` and `Thread(target=...)`) expects function name without `()` (also called `callback`) but you use `()` inside `onclick(shoot())` so you get `result = shoot() ; onclick(result)`. but `shoot()` returns `None` so you get `onclick(None)`. BTW: onclick/onscreenclick runs functions with two arguments `x,y` so you need `def shoot(x, y):` – furas Dec 03 '17 at 11:03
  • BTW: you forgot `turtle.mainloop()` - maybe it will run without `mainloo()` in `IDLE` or other tool which is built on `tkinter` which runs `mainloop()` (`turtle` is built on `tkinter` too) but outside `IDLE` it will not work. – furas Dec 03 '17 at 11:11
  • Thank you for you help @furas! I made the changes you suggested, but the problem in which the invasion cannot occur at the same time as the shoot function persists... and on top of this issue I have noticiced a new issue in which the shoot function is for some reason infinitely recursive. What am I doing wrong? I really appreciate your help and patience. – Billiam Dec 03 '17 at 20:42
  • @furas homex = turtle.xcor() homey = turtle.ycor() x = homex y = homey def shoot(x,y): turtle.color('red') turtle.showturtle() turtle.pu() turtle.onscreenclick(turtle.goto) turtle.goto(homex, homey) turtle.onclick(shoot(x,y)) def invasion(): x = 0 while x < 3: invaders() x += 1 if __name__ == '__main__': Thread(target = invasion()).start() Thread(target = shoot(x,y)).start() turtle.mainloop() – Billiam Dec 03 '17 at 20:43
  • in `onclick()` you have to put function name without `()` - see `onscreenclick`, you put function name `turtle.goto`, not `turtle.goto(x,y)`. I don't know what you try to do but I think you shouldn't use `shoot` in onclick because it make recursion. You should split function `shoot()` into `shoot_init()` and `shoot()`, , and thread should run `shoot_init()`and `shoot_init()` should use `onclick(shoot)` – furas Dec 03 '17 at 22:17
  • `turtle.tracer(0)` seems like a better approach than threading for this sort of real-time app. See [this answer](https://stackoverflow.com/questions/47879608/how-to-bind-several-key-presses-together-in-turtle-graphics/70979967#70979967) for a demo. Even if threading works (CPython has a GIL making it effectively single-threaded), you'd have to spawn a new thread for each entity which isn't scalable or easy to code. See [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem/233676#233676). – ggorlen Sep 22 '22 at 18:47

1 Answers1

1

Thread(target = invasion()) should be Thread(target = invasion)? Otherwise you are invoking the function straight away, rather than passing the function to be invoked by the Thread.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Thank you! I made that change, but now I am getting this error: RuntimeError: main thread is not in main loop and neither the invasion or shoot function are running – Billiam Dec 03 '17 at 04:16
  • @BEN `turtle` is created with`tkinter` which is not thread safe and it shouldn't change elements in thread. But your problem can be that you don't stop threads before you close program so main thread stops `mainloop` (which do everything in code) but other threads still run and try to use this `mainloop` – furas Dec 03 '17 at 10:58