I'm trying to write a python game loop that hopefully takes into account FPS. What is the correct way to call the loop? Some of the possibilities I've considered are below. I'm trying not to use a library like pygame.
1.
while True:
mainLoop()
2.
def mainLoop():
# run some game code
time.sleep(Interval)
mainLoop()
3.
def mainLoop():
# run some game code
threading.timer(Interval, mainLoop).start()
4.
Use sched.scheduler?
If I understood correctly you want to base your game logic on a time delta.
Try getting a time delta between every frame and then have your objects move with respect to that time delta.
import time
while True:
# dt is the time delta in seconds (float).
currentTime = time.time()
dt = currentTime - lastFrameTime
lastFrameTime = currentTime
game_logic(dt)
def game_logic(dt):
# Where speed might be a vector. E.g speed.x = 1 means
# you will move by 1 unit per second on x's direction.
plane.position += speed * dt;
If you also want to limit your frames per second, an easy way would be sleeping the appropriate amount of time after every update.
FPS = 60
while True:
sleepTime = 1./FPS - (currentTime - lastFrameTime)
if sleepTime > 0:
time.sleep(sleepTime)
Be aware thought that this will only work if your hardware is more than fast enough for your game. For more information about game loops check this.
PS) Sorry for the Javaish variable names... Just took a break from some Java coding.
Related
The project
I am conducting a project where I need to both detect faces (bounding boxes and landmarks) and perform face recognition (identify a face). The detection is really fast (it takes not even a few milliseconds on my laptop) but the recognition can be really slow (about 0.4 seconds on my laptop). I am using the face_recognition Python library to do so. After a few tests, I discovered that it is the embedding of the image that is slow.
Here is an example code to try it out for yourself :
# Source : https://pypi.org/project/face-recognition/
import face_recognition
known_image = face_recognition.load_image_file("biden.jpg")
biden_encoding = face_recognition.face_encodings(known_image)[0]
image = face_recognition.load_image_file("your_file.jpg")
face_locations = face_recognition.face_locations(image)
face_landmarks_list = face_recognition.face_landmarks(image)
unknown_encoding = face_recognition.face_encodings(image)[0]
results = face_recognition.compare_faces([biden_encoding], unknown_encoding)
The problem
What I need to do is to process a video (30 FPS), therefore 0.4s of computation is unacceptable. The idea that I have is that the recognition will only need to be run a few times and not every frame since from one frame to another, if there are no cuts in the video, a given head will be close to its previous position. Therefore, the first time the head appears, we run the recognition which is very slow but then for the next X frames, we won't have to since we'll detect that the position is close to the previous one, therefore it must be the same person that moved. Of course, this approach is not perfect but seems to be a good compromise and I would like to try it.
The only problem is that by doing so the video is smooth until a head appears, then the video freezes because of the recognition and then becomes smooth again. This is where I would like to introduce multiprocessing, I would like to be able to compute the recognition in parallel of looping through the frame of the video. If I manage to do so, I will then only have to process a few frames in advance so that when a face shows up it already computed its recognition a few seconds ago during several frames so that we did not see a reduced frame rate.
Simple formulation
Therefore here is what I have (in python pseudo code so that it is clearer):
def slow_function(image):
# This function takes a lot of time to compute and would normally slow down the loop
return Recognize(image)
# Loop that we need to maintain at a given speed
person_name = "unknown"
frame_index = -1
while True:
frame_index += 1
frame = new_frame() # this is not important and therefore not detailes
# Every ten frames, we run a heavy function
if frame_index % 10 == 0:
person_name = slow_function(image)
# each frame we use the person_name even if we only compute it every so often
frame.drawText(person_name)
And I would like to do something like this :
def slow_function(image):
# This function takes a lot of time to compute and would normally slow down the loop
return Recognize(image)
# Loop that we need to maintain at a given speed
person_name = "unknown"
frame_index = -1
while True:
frame_index += 1
frame = new_frame() # this is not important and therefore not detailes
# Every ten frames, we run a heavy function
if frame_index % 10 == 0:
DO slow_function(image) IN parallel WITH CALLBACK(person_name = result)
# each frame we use the person_name even if we only compute it every so often
frame.drawText(person_name)
The goal is to compute a slow function over several iterations of a loop.
What I have tried
I looked up multiprocessing and Ray but I did not find examples of what I wanted to do. Most of the time I found people using multiprocessing to compute at the same time the result of a function for different inputs. This is not what I want. I want to have in parallel a loop and a process that accepts data from the loop (a frame), do some computation, and returns a value to the loop without interrupting or slowing down the loop (or at least, spreading the slow down rather than having one really slow iteration and 9 fast ones).
I think I found pretty much how to do what I want. Here is an example:
from multiprocessing import Pool
import time
# This seems to me more precise than time.sleep()
def sleep(duration, get_now=time.perf_counter):
now = get_now()
end = now + duration
while now < end:
now = get_now()
def myfunc(x):
time.sleep(1)
return x
def mycallback(x):
print('Callback for i = {}'.format(x))
if __name__ == '__main__':
pool=Pool()
# Approx of 5s in total
# Without parallelization, this should take 15s
t0 = time.time()
titer = time.time()
for i in range(100):
if i% 10 == 0: pool.apply_async(myfunc, (i,), callback=mycallback)
sleep(0.05) # 50ms
print("- i =", i, "/ Time iteration:", 1000*(time.time()-titer), "ms")
titer = time.time()
print("\n\nTotal time:", (time.time()-t0), "s")
t0 = time.time()
for i in range(100):
sleep(0.05)
print("\n\nBenchmark sleep time time:", 10*(time.time()-t0), "ms")
Of course, I will need to add flags so that I do not write a value with the callback at the same time that I read it in the loop.
For an animation project, we want to model a variable number of objects. This means that the computation and rendering part will take variable computation time. But we want the frame rate of the animation to remain constant. So we would like to compute how much time a section of code has taken, and if it is less then the expected frame rate, we wait the remainded before calculation the next frame.
Is there an easy way to do this?
One way to do it is by using the time library that has a method time that allows you to count how much time a section of code has taken to execute, an example down below.
import time
start_time = time.time()
#Your code here
end_time = time.time() - start_time
print(end_time)
You stopwatch the time, and sleep it for 1-x seconds.
sleep_time = 1/1 #Second number is frames per unit of time, first number is unit of time. In seconds.
from time import time #Important function: time(): Secs since unix epoch
while True: #Init animation
init_time = time() #Init stopwatch
#Code here Do your code
timer = time()-init_time #End stopwatch and remember time in a variable.
while time() < init_time+sleep_time-timer: pass #Wait ___ seconds
#Restart and go to next frame.
If the code that you coded in lasts longer than 'sleep time', than it will not wait.
Hope this helps, and good luck with animating!
I am quite new to coding and i am wondering how to make it more efficient. I am running it on a raspberry pi which will have other tasks so i want this to be as easy to run as possible. The code will use a magnetic sensor to record passes made by a magnet mounted on a wheel and from there determine the speed of the outside diameter of the wheel. It would be useful to implement something that takes the five last speed outputs and gives sort of an average but only if it does not affect the complexity of the code much. Would be really greatful for any help!
from gpiozero import Button
import time
global t0
t0 = time.clock()
raduis = 300
button = Button (21)
from signal import pause
def calculate_speed(radius):
global t0
t1 = time.clock
interval = t1 - t0
speed = radius/interval
print (speed, 'mm/sek')
y = True
while y == True:
button.when_pressed = calculate_speed(radius)
time.sleep(0.2)
#used to prevent one pass of the magnet from recording many passes
You should store five last speed outputs in an array (list) and then you can calculate the average speed
speed_records = []
def calculate_speed(radius):
global t0
t1 = time.clock
interval = t1 - t0
speed = radius/interval
print (speed, 'mm/sek')
speed_records.append(speed) # Adds one speed record to the list
if len(speed_records) >= 5: # checks if there are 5 five records available
last_five_records = speed_records[-5:] # Seperates five last records
average = sum(last_five_records) / 5
print('Average Speed:',average) # Prints out the average
if len(speed_records) > 10: # Free Up some memory
speed_records = list(set(speed_records) - set(speed_records[:5])) #removes the first five records
The following code uses modular arithmetic to iterate through a single list, adding and overwriting values, and printing the averaged speed. Adjust iterations to control how many passes to average over.
from gpiozero import Button
from signal import pause
import time
radius = 300
button = Button (21)
iterations = 5
speeds = [0] * iterations
speed_idx = 0
def calculate_speed(radius):
global speeds, speed_idx
t1 = time.time()
speeds[speed_idx] = radius / (t1- t0)
print (sum(speeds) / iterations, 'mm/sek')
speed_idx += 1
speed_idx %= iterations
t0 = time.time()
while True:
button.when_pressed = calculate_speed(radius)
time.sleep(0.2)
t0 = time.time()
Note this takes 5 measurements to "ramp up" in a sense. If you want- you could add an if statement to avoid printing out the first 4 recordings.
Additionally, if you wanted a smoother measurement of speeds, it occurred to me that you could use a single value to hold the sum of the speeds of the last N passes, and each time subtract off the average (assuming N sums), and add the new speed. It would a few extra passes to stabilize, but afterwards it should smooth the reported speeds a bit.
I'm wondering how I can play a .wav file after some time has passed without using the sleep function. Essentially, I was wondering if there is a way to keep track of time in Python, so that, after say 15 seconds has elapsed, I can play a sound without pausing my code.
# checks if I should play the sound or not and, sets the variable
def Tyler(self):
if self.started == 1:
if self.isTaiwan == 0:
if self.myListNames[self.current_player_id / 3].lower() == "tyler":
self.isTyler = 1
else:
self.isTyler = 0
self.Tyler()
if self.isTyler == 1:
time.sleep(6)
winsound.PlaySound("tyler.wav", winsound.SND_ASYNC)
# This is where I would want to check to see
# if some time has passed and the conditions haven't changed.
from time import time
def delay(secs):
init_time = time()
while time() < init_time+secs: pass
This won't use sleep but it is similar. Still uses time module
I'm writing a program for my physics final. Throughout the semester we have used vPython to model situations and get exact answers, etc. Our final project is to create a game using vPython that includes some type of physics.
I chose to remake Bowman except with tanks. So you have a tank on the right hand of the screen and left hand of the screen with a wall in the middle. The object is to aim your cannon and shoot the right velocity to hit your opponent's tank. I have the a good chunk of the program complete however I am stuck on a few different things.
First, how can I time a keystroke? I have it so I shoot from the each cannon however I want to be able to hold down a key and dependent on how long it's held down for the faster the initial velocity will be.
Second, where would I incorporate gravity into the program? I have a general idea of how to but I just don't know which function to put it into.
Lastly, I have a wall height being generated randomly each time the program is run. However sometimes the wall is so small you can't see it. Is there a way I can set a range of values for this?
Here is my code:
from visual import*
from random import*
scene.autoscale=False
scene.width = 1500
scene.height = 800
scene.title='Tanks'
def moveaup(gun):
theta=arctan(gun.axis.y/gun.axis.x)
dtheta=.1
if (theta<pi/2):
theta=theta+dtheta
if not (theta>pi/2):
gun.axis=(cos(theta),sin(theta),0)
else:
gun.axis=vector(0,1,0)
def moveadown(gun):
theta=arctan(gun.axis.y/gun.axis.x)
dtheta=.1
if (theta>0):
theta=theta-dtheta
gun.axis=(cos(theta),sin(theta),0)
def movebup(gun):
theta=arctan(gun.axis.y/gun.axis.x)+pi
dtheta=.1
if (theta>pi/2):
theta=theta-dtheta
if not (theta<pi/2):
gun.axis=(cos(theta),sin(theta),0)
else:
gun.axis=vector(0,1,0)
def movebdown(gun):
theta=arctan(gun.axis.y/gun.axis.x)+pi
dtheta=.1
if (theta<pi):
theta=theta+dtheta
gun.axis=(cos(theta),sin(theta),0)
def shoota(gun):
vel = vector(1,1,0)
bullet = sphere(pos=(gun.pos.x+gun.axis.x,gun.pos.y+gun.axis.y,0),radius=(.0785),color=color.yellow)
bullet.v = vector(0,0,0)
bullet.v = bullet.v+vel
bulletlist.append(bullet)
def shootb(gun):
vel = vector(-1,1,0)
bullet = sphere(pos=(gun.pos.x+gun.axis.x,gun.pos.y+gun.axis.y,0),radius=(.0785),color=color.green)
bullet.v = vector(0,0,0)
bullet.v = bullet.v+vel
bulletlist.append(bullet)
def bulletlistupdate(bulletlist):
dt=.01
for a in bulletlist:
a.pos=a.pos+a.v*dt
def checks(agun,bgun):
if scene.kb.keys:
key=scene.kb.getkey()
if key=='a':
moveaup(agun)
if key=='s':
moveadown(agun)
if key=='l':
movebup(bgun)
if key=='k':
movebdown(bgun)
if key=='d':
shoota(agun)
if key=='j':
shootb(bgun)
#enviroment
ground = box(pos=(0,-8,0),size=(50,5,0),color=color.red)
wall = box(pos=(0,-8,0),size=(.25,20*random(),0),color=color.red)
#playerA
abody = box(pos=(-11,-5.25,0),size=(.5,.5,0),color=color.blue)
agun = cylinder(pos=(-11,-5.1,0),axis=(.8,.8,0),radius=(.08),color=color.blue)
#playerB
bbody= box(pos=(11,-5.25,0),size=(.5,.5,0),color=color.yellow)
bgun = cylinder(pos=(11,-5.1,0),axis=(-.8,.8,0),radius=(.08),color=color.yellow)
bulletlist = []
while True:
rate(1000)
checks(agun,bgun)
bulletlistupdate(bulletlist)
Any and all help is welcome!
Thanks much!
You can time things in python using the time module
import time
start = time.time()
finish = time.time()
print start # 1386269106.18
print finish # 1386269111.11
print (finish - start) # 4.9276599884
So when the player first starts pressing the button, save the time. Then save the time again when the player stops pressing the button. The difference between these two times is the number of seconds the player held the button.
[Edit] If all you can do is check if the key is pressed down, you can get the time inside the main loop and calculate dt (the amount of time that has passed):
t = time.time()
while True:
new_t = time.time()
dt = new_t - t
t = new_t
rate(1000)
checks(agun,bgun, dt)
bulletlistupdate(bulletlist)
Then pass dt to checks, and if the key is pressed down, you know the key has been held down for another dt seconds, and you can add it to your running total of time that it has been held down.
For the random you would need to enter a command similar to this:
random.randrange(5,31) #this would give you a random range between the numbers 4-30
I don't want to do your homework for you as I don't think you are asking for that. I hope this helps you.
I'm sorry this should be the correct code for you:
random.randint(7,40) # this would get you a random integer of 7-40
I apologize for the misinformation.