1

I want to spawn an enemy into the game at random intervals of 1-5 seconds. To prevent the gameplay part of the program (moving character etc.) from pausing whenever there is a sleep, I have split the whole code into 2 methods so that I can have 2 threads- one that sets enemyspawn True every 1-5 seconds and another one which controlls the character.

Sadly, either the 2nd method doesn't change enemyspawn in the first method or threading outputs an error. I have read about and tried retrieving the value from EnemySpawn() before the if statement but that didn't work (or I just did it wrong). I have also tried turning enemyspawn to a global variable. Didn't change anything.

def Gameplay():
    width=80
    height=80
    y = 720/2+height/2
    x = 720/2+width/2
    speed=2

    enemyspawn = False
    while True:
        #controll character here
        if enemyspawn:
            enemyspawn=False
            print(enemyspawn) #Spawn enemy here later

        window.blit(bg, [0,0])
        pygame.draw.rect(window,(100,100,100),(x,y,width,height))
        pygame.display.update()


def EnemySpawn():
    enemyspawn = EnemySpawn() #idrk about this line

    while True:
        sleep(randint(1,5))
        enemyspawn=True
        print(enemyspawn)
    return enemyspawn

Gameplay = threading.Thread(target=Gameplay)
Gameplay.start()
EnemySpawn = threading.Thread(target=EnemySpawn)
EnemySpawn.start()

The threading error message:

line 51, in EnemySpawn
    enemyspawn = Gameplay(enemyspawn)
UnboundLocalError: local variable 'enemyspawn' referenced before assignment

Another error message:

line 51, in EnemySpawn
    enemyspawn = EnemySpawn()
TypeError: 'Thread' object is not callable
Thomas3k24
  • 55
  • 5
  • I would use the pygame event system to do that. Have a look at [this answer](https://stackoverflow.com/a/56050157/10426037) of mine. – Valentino May 23 '19 at 16:16

1 Answers1

0

You wouldn't want to call a function from within the same function (a recursive function) in this situation. A better approach might be:

def spawn_enemy():
    # code that makes an enemy
    print("an enemy has been spawned")
    return True # would be "return enemy" after you create your enemy entity

def EnemySpawnThread():
    enemy_list = [] # to maintain records of all enemies made
    while True: # make enemies forever
        sleep(randint(1,5))
        enemy_list.append(spawn_enemy()) # call our function we made above which spawns enemies

Currently this would just make a list like [True, True, True, ...], but eventually you would probably define an enemy class and it would become [enemy_object, enemy_object, ...] which is preferable because the best way I've found to delete objects is to store them in lists and then del enemy_list[index] to delete them (for instance when the enemy dies so it doesn't keep using memory). Additionally it gives you the power to iterate over like:

for enemy in enemy_list:
    enemy.move()
    enemy.attack()
    enemy.die()

Which is why you would want enemy to be a class that has methods like shown above.

As a side note, you would likely want to have enemy_list as a global variable (or in your main function) so that all your threads can access it. Otherwise you will need to implement the queue.Queue() built in standard with python.

Reedinationer
  • 5,661
  • 1
  • 12
  • 33
  • Thanks for such a quick response! This works just perfectly for spawning the enemies. The only problem is that the enemy disappears really quickly when I use a background. Do you know any fix to this too? – Thomas3k24 May 23 '19 at 16:42
  • @Varvalian No, that sounds like it involves other parts of your code that are not posted. I suggest you diagnose it to a specific section of your code and [create a MCVE](https://stackoverflow.com/help/minimal-reproducible-example). Posting it here would be off the topic of "How to trigger action in method from a different method". Once you make your post you can make a comment here and "@" me with a comment here (like I have done with this comment) so I can get a notification to look at it. – Reedinationer May 23 '19 at 16:48
  • @Varvalian If this post has in fact answered your initial question, then it is common practice to click the check mark by my post to accept it as the answer (you can change this later if a better answer comes too). When you get 15 reputation you can up-vote helpful questions/answers too. Welcome to StackOverflow! – Reedinationer May 23 '19 at 16:49
  • Sadly I cannot post another question now (90 minute delay). I am thinking about just posting a comment with my code under a question which fits the topic and then commenting a link to it here... @Reedinationer – Thomas3k24 May 23 '19 at 17:18
  • @Varvalian It would be best to make your own question. You cannot properly format code in comment blocks as it would look like this `if my_test = True:`; `print("yes")`; `for item in my_list:`; `process_item(item)` and all sense of indentation and execution flow is thrown off (like in my code is the `for` loop nested within `if`?...Nobody knows!!) I'm sure you can handle a small delay! Plus questions give you opportunities to gain & build your reputation (which has perks like removing delays) while comments have no such feature – Reedinationer May 23 '19 at 17:38
  • Alright here you go @Reedinationer https://stackoverflow.com/questions/56280392/enemy-character-is-being-overdrawn-by-games-background – Thomas3k24 May 23 '19 at 18:00