How do you create a new thread in python? - python

I'm building a game in python, and I want to create an event listener that checks for when the main character's hp is smaller or equal to 0, and then executes a game over function. In other languages (vb.net) i have achieved this by creating a new thread that continuously loops an if statement until the condition is met, then runs the game over code, then closes itself. How do you create/start/close threads in python? Also, is there a better way of doing this that is sitting right in front of me?

from threading import Thread
def my_function():
while True:
if player.lives < 5:
do_stuff()
Thread(my_function).start()
However most of the times the games are developed following a frame-loop rule, with the following structure:
def my_game():
should_continue = False
while should_continue:
should_continue = update_logic()
update_graphics()
What you define in update_logic and update_graphics is up to you and the graphic library you're using (since you're using text, your function would just print text in your console), but some examples of the logic would be like this:
def update_logic():
if player.lives < 5:
return False
# these are just examples, perhaps not valid in your game
player.xdirection = 0
player.ydirection = 0
player.speed = 0
player.hitting = False
if player.damage_received_timer > 0:
player.damage_received_timer -= 1
if right_key_pressed:
player.xdirection = 1
if left_key_pressed:
player.xdirection = -1
if up_key_pressed:
player.ydirection = -1
if down_key_pressed:
player.ydirection = +1
if player.ydirection or player.xdirection:
player.speed = 20
if space_key_pressed:
player.hitting = True
# bla bla bla more logic
return True
This does not make use of threads and using threads is most of the times a bad practice if multiple events occur. However in your text games, perhaps not so much elements are involved, so it's unlikely a race condition would occur. Be careful, however. I always prefer these loops instead of threads.

Related

How could I make a dash in a 2D pygame python program?

If the pygame program is just a basic entity you can move normally with arrow keys, how could i make it so that if space is pressed, based on the arrow key that was being held at the moment of pressing, a player dashes slightly to the specified direction? My idea is that when the space is pressed, program checks if other keys are being held down and based on that it rapidly increases the x and/or y coordinate and then stops at specific time, but I don't know how to make it stop as all of this is happening inside a main game loop. Any insight is highly appreciated.
You can use time.perf_counter.
So for using that function you will need to import module time
import time
And now you can store the value of time.perf_counter in a variable when space is pressed.
import time
jumping = False
start_time = 0
while True:
if ("space is pressed"): # Replace condition with what you want
jumping = True
start_time = time.perf_counter()
# Add your code here
if jumping:
# change of x, y and other jumping mechanism code here
if jumping and time.perf_counter() - start_time > 1: # Replace 1 with the amount you want this if condition to trigger, also add value in seconds
jumping = False
I'm not sure what your code is like, but this is how I'd go about it:
def dash(self):
keys = pygame.keys.get_pressed()
if keys[pygame.K_SPACE] and not self.dashing and self.jumping:
# ^^ Can be any key
if self.facing_right:
self.x += 20
if self.facing_left:
self.x -= 20
self.dashing = True
Make sure that when you hit the ground it does self.dashing = False else you will be able to dash forever.
Just put this function in the player class if you are doing it OOP.
Else if you are not using classes, put it anywhere in the file and take out all self..

How to change value to zero but not in the first iteration of a loop

I am trying to implement turning of front wheels in PyBox2D, for now, I was able to make it turn left/right but I am not able to stop it when the angle reaches zero (to make the car to go straight)
My goal is to stop turning when the angle of a wheel reaches zero or value similar to zero, but not on the beginning (sometimes when the angles are zero they do not move at all, and if possible I would like to make it independent from pressing key on a keyboard (moving those two nested if statements out of the if keyboard_is_pressed(hotkey) part did not help
I hope I made myself clear and thank you very much for any help
EDIT I tried to implement solution given in the answer, it kind of worked but I tried to improve it and now I am stuck again, the wheels turn, but when they return to their initial position they stop moving. One of problems can be that when I press "a" or "d" key my variable self.ticking changes by more than just one, because I am not able to press the key for such a short period of time.
variable self.on_the_beginning is equivalent to on_starting_race from the answer below:
def control(self): # This makes control independent from visualisation
#Front left suspension: index 2
#Front right suspension: index 3
print(self.ticking)
if keyboard.is_pressed("a"):
self.suspensions[2].motorSpeed = -5
self.suspensions[3].motorSpeed = -5
self.ticking -= 1
if keyboard.is_pressed("d"):
self.suspensions[2].motorSpeed = 5
self.suspensions[3].motorSpeed = 5
self.ticking += 1
if self.ticking <= -3:
self.ticking = -3
self.on_the_beginning = True
elif self.ticking >= 3:
self.ticking = 3
self.on_the_beginning = True
if np.radians(-5) <= self.tires[2].wheel.angle <= np.radians(5) and self.on_the_beginning == True and self.ticking !=0:
self.suspensions[2].motorSpeed = 0
self.suspensions[3].motorSpeed = 0
self.tires[2].SetAngularVelocity = 0
self.tires[3].SetAngularVelocity = 0
self.ticking = 0
on_the_beginning = False
If i understand correctly, you can have a variable, say on_starting_race, set to false, then check whenever it is above a set number (say, when it's above 10 you know for a fact that the race has already started and the car moved at least for a few seconds), then change that value to True, now add an if statement to determine whether the value is close to 0 (say val<5) AND on_starting_race is True.
There might be a more elegant way, but this is pretty straight forward(assuming you check the state of the car every frame or a set period of time).
Sorry, because I am not 100% sure of your problem without the whole code.
I think the solution could be using an input parameter in you function, let's say first_run, that you can control from inside and outside the function.
def control(self, first_run=True):
This way, you may start the race (from your main program) setting first_run to True, so you don't care about the angle. And use this same function setting first_run to False the rest of the times.
You can also use a self.first_run variable that you may set to True in the init, then setting self.first_run to False if it is True (which is really the first time you use your control() function).

Python IF statement to see how long an input has been active

I am using a raspberry pi, a pi face and a python script to monitor several home sensors. I want to add the sensing wire from the smoke detectors to that list but I need a bit of help with the if statement.
I'm unsure how to tell the if statement to check how long the input has detected the signal. Under 4 seconds disregard (low battery chirp), over 4 seconds (smoke detected) alert me..
Basically I need help writing the if statement below.
if piface.digital_read(0)==0 >= 4 seconds:
# do x
else:
# do y
Do I need a loop and can it be as easy as what I have above? (Coded correctly of course!)
Something like this (untested pseudo-code):
counter = 0
while True: #your main loop
smoke = digital_read() #assume 0 = no alarm, 1 = alarm
if smoke:
counter += 1
else:
counter = 0
if counter >= 4: #there was smoke for the last 4 seconds
call_the_fire_brigade()
time.sleep(1) #wait one second
I guess you probably need some loop.
Well I think a good solution to this would be spawn a separate thread for each detector and then use a blocking for the number with a loop.. like
count = 0
while count < 4:
if piface.digital_read(0) == 0:
count += 1
else: count = 0
sleep(4000)
# ... rest of code ...

Implementing an Anti Spamming thing?

I have an IRC bot that I made for automating stuff.
Here's a snippet of it:
def analyseIRCText(connection, event):
global adminList, userList, commandPat, flood
userName = extractUserName(event.source())
userCommand = event.arguments()[0]
escapedChannel = cleanUserCommand(config.channel).replace('\\.', '\\\\.')
escapedUserCommand = cleanUserCommand(event.arguments()[0])
#print userName, userCommand, escapedChannel, escapedUserCommand
if flood.has_key(userName):
flood[userName] += 1
else:
flood[userName] = 1
... (if flood[userName] > certain number do...)
So the idea is that flood thing is a dictionary where a list of users who have entered in a command to the bot in the recent... some time is kept, and how many times they've said so and so within that time period.
Here's where I run into trouble. There has to be SOMETHING that resets this dictionary so that the users can say stuff every once in awhile, no? I think that a little thing like this would do the trick.
def floodClear():
global flood
while 1:
flood = {} # Clear the list
time.sleep(4)
But what would be the best way to do this?
At the end of the program, I have a little line called:
thread.start_new_thread(floodClear,())
so that this thing doesn't get called at gets stuck in an infinite loop that halts everything else. Would this be a good solution or is there something better that I could do?
Your logic should be enough. If you have say:
if flood.has_key(userName):
flood[userName] += 1
else:
flood[userName] = 1
if flood[userName] > say 8:
return 0
That should make your bot ignore the user if he has spammed too many times within your given time period. What you have there should also work to clear up your flood dictionary.

pygame.time.set_timer confusion?

So, I have a problem, I don't fully understand the event that is needed to be given to a timer command anyway, it doesn't say anywhere online, to where I searched for hours. So I just used what most people seemed to be using 'USEREVENT + 1'. I'm not sure if it is correct, but my timer is not working. Am I using it correctly? Here is my code:
nyansecond=462346
nyanint=0
spin=0
aftin=452345
def nyanmusic(nyansecond,nyanint,spin):
if nyanint == 0:
nyansound.play()
nyanint= 1
elif nyanint == 1:
nyansecond = pygame.time.set_timer(USEREVENT+1,7000)
if nyansecond < 200 and spin == 1:
spin = 0
nyansecond = pygame.time.set_timer(USEREVENT+1,7000)
elif nyansecond > 6500 and nyansecond < 100000 and spin == 0:
spin = 1
nyansoundm.play()
return nyansecond,nyanint,spin
I then def it into my code on the second page I implemented (which works fine). It runs the nyansound, but doesn't run nyansoundm after 6.5 seconds (6500 milliseconds). I'm making this program to help me learn the basics of python and pygame, before moving on to more complex stuff. I can also use it when I want to listen to nyan cat or other looped songs without having to go on youtube and waste precious bandwidth. Don't worry about that, though.
Oh, and here is the code I have put into my loop, although I do not think this matters too much:
#music
nyansecond,nyanint,spin = nyanmusic(nyansecond,nyanint,spin)
Let's recap what pygame.time.set_timer does:
pygame.time.set_timer(eventid, milliseconds): return None
Set an event type to appear on the event queue every given number of milliseconds. The first event will not appear until the amount of time has passed.
Every event type can have a separate timer attached to it. It is best to use the value between pygame.USEREVENT and pygame.NUMEVENTS.
pygame.USEREVENT and pygame.NUMEVENTS are constants (24 and 32), so the argument eventid you pass to pygame.time.set_timer should be any integer between 24 and 32.
pygame.USEREVENT+1 is 25, so it's fine to use.
When you call pygame.time.set_timer(USEREVENT+1,7000), the event with eventid 25 will appear in the event queue every 7000ms. You didn't show your event handling code, but I guess you do not check for this event, which you should do.
As you can see, pygame.time.set_timer returns None, so your line
nyansecond = pygame.time.set_timer(USEREVENT+1,7000)
doesn't make sense since nyansecond will always be None, and hence comparing it against an integer
if nyansecond < 200 ...
is pointless.
If you want to play a sound every 6.5 seconds using the event queue, simpy call pygame.time.set_timer once(!):
PLAYSOUNDEVENT = USEREVENT + 1
...
pygame.time.set_timer(PLAYSOUNDEVENT, 6500)
and check the event queue for this event in your main loop:
while whatever: # main loop
...
# event handling
if pygame.event.get(PLAYSOUNDEVENT): # check event queue contains PLAYSOUNDEVENT
nyansoundm.play() # play the sound

Categories