I am practicing on pygame and I was wondering how can we do so that the framerate does not affect the speed of execution of the game
I would like FPS to not be locked and the game to always run at the same speed.
Until now I used the pygame.time.Clock.tick function but the speed of the character was changing depending on the number of FPS, which I don't want.
You have to calculate the movement per frame depending on the frame rate.
pygame.time.Clock.tick returns the number of milliseconds since the last call. When you call it in the application loop, this is the number of milliseconds that have passed since the last frame. Multiply the objects speed by the elapsed time per frame to get constant movement regardless of FPS.
For instance define the distance in number of pixel, which the player should move per second (move_per_second). Then compute the distance per frame in the application loop:
move_per_second = 500
FPS = 60
run = True
clock = pygame.time.Clock()
while run:
ms_frame = clock .tick(FPS)
move_per_frame = move_per_second * ms_frame / 1000
# [...]
Related
First off, I have never asked a question on stackoverflow before, and I will do my best to follow the site guidelines, but let me know if I should changes something about my post.
I am attempting to write a function that can quickly extract the pore size distribution from a binary 3D image. I do this by computing the local thickness of the image, in a similar fashion to that implementing in ImageJ's local thickness plugin. I need this function ideally to run in under 1 second, as I am calling it ~200000 times in a simulated annealing process. It is performed partially on the CPU (12th Gen Intel(R) Core(TM) i7-12700KF, 20 cores, 16GB RAM) and partially on the GPU (RTX GeForce 3050, 8GB).
The function works, but there is something happening I think on the backend, which is slowing it down artificially. This may have to do with threading, or GPU to CPU overhead, or some kind of 'cool down' period.
There are three parts of the function:
Euclidean Distance Transform - performed on CPU, in parallel using edt package. Currently takes ~0.25 seconds on a 250^3 binary image
3d Skeletonization - performed on CPU using skimage.morphology.skeletonize_3d, but with the image split into chunks using dask. This implementation is provided by porespy.filters.chunked_func. Multiply skeleton by the distance transform to get a skeleton with values equal to the minimum distance to nearest background voxel. This process takes 0.45 to 0.5 seconds.
Dilate each voxel on the skeleton using a spherical structuring element with radius equal to the value of the skeleton voxel. This is done in a for loop, starting from the maximum structuring element size, and in decreasing order. Larger spheres do not get overwritten by smaller spheres. The dilations are accomplished using fft convolution on the GPU using cupyx.scipy.signal.signaltools.convolve, which takes ~ 0.005 seconds.
Less code is required to reproduce the effect I am seeing, however. The essential part is performing many fft convolutions in sequence.
A minimum reproducible example is as follows:
import skimage
import time
import cupy as cp
from cupyx.scipy.signal.signaltools import convolve
# Generate a binary image
im = cp.random.random((250,250,250)) > 0.4
# Generate spherical structuring kernels for input to convolution
structuring_kernels = {}
for r in range(1,21):
structuring_kernels.update({r: cp.array(skimage.morphology.ball(r))})
# run dilation process in loop
for i in range(10):
s = time.perf_counter()
for j in range(20,0,-1):
convolve(im, structuring_kernels[j], mode='same', method='fft')
e = time.perf_counter()
# time.sleep(2)
print(e-s)
When run as is, after the first couple of loops, each dilation loop takes ~ 1.8 seconds on my computer. If I uncomment the time.sleep(2) line (ie pause for 2 seconds between each loop), then the loop function call only takes 0.05 seconds. I suspect this has to do with threading or GPU use, as it takes a couple of loops for it to reach the 1.8 seconds, then it remains steady at that value. When I monitor my GPU usage, the 3D monitor quickly spikes to 100% and stays close to there.
If I am just being limited by the capacity of my GPU, why do the first couple of loops run faster? Could a memory leak be happening? Does anyone know why this is happening, and if there is a way to prevent it, possibly using backend controls in cupy?
I'm not sure if this is necessary, but my local thickness function in its entirety is as follows:
import porespy as ps
from skimage.morphology import skeletonize_3d
import time
import numpy as np
import cupy as cp
from edt import edt
from cupyx.scipy.signal.signaltools import convolve
def local_thickness_cp(im, masks=None, method='fft'):
"""
Parameters
----------
im: 3D voxelized image for which the local thickness map is desired
masks: (optional) A dictionary of the structuring elements to be used
method: 'fft' or 'direct'
Returns
-------
The local thickness map
"""
s = time.perf_counter()
# Calculate the euclidean distance transform using edt package
dt = cp.array(edt(im, parallel=15))
e = time.perf_counter()
# print(f'EDT took {e - s}')
s = time.perf_counter()
# Calculate the skeleton of the image and multiply by dt
skel = cp.array(ps.filters.chunked_func(skeletonize_3d,
overlap=17,
divs=[2, 3, 3],
cores=20,
image=im).astype(bool)) * dt
e = time.perf_counter()
# print(f'skeletonization took {e - s} seconds')
r_max = int(cp.max(skel))
s = time.perf_counter()
if not masks:
masks = {}
for r in range(int(r_max), 0, -1):
masks.update({r: cp.array(ps.tools.ps_ball(r))})
e = time.perf_counter()
# print(f'mask creation took {e - s} seconds')
# Initialize the local thickness image
final = cp.zeros(cp.shape(skel))
time_in_loop = 0
s = time.perf_counter()
for r in range(r_max, 0, -1):
# Get a mask of where the skeleton has values between r-1 and r
skel_selected = ((skel > r - 1) * (skel <= r)).astype(int)
# Perform dilation on the mask using fft convolve method, and multiply by radius of pore size
dilation = (convolve(skel_selected, masks[r], mode='same', method=method) > 0.1) * r
# Add dilation to local thickness image, where it is still zero (ie don't overwrite previous inserted values)
final = final + (final == 0) * dilation
e = time.perf_counter()
# print(f'Dilation loop took {e - s} seconds')
return final
Now, in theory, the function should take ~ 0.80 seconds to compute. However, when called in a loop on separate images, it takes ~1.5 seconds. However, if I add a time.sleep(1) after each function call, then the function does take approximately 0.8 seconds.
I have a small problem that I really cant figure out how to solve after some time googling.
My project consists of a rasberry pi and a rotary encoder that I will use as a sensor to see how fast a shaft is spinning. This is then coded in Python. My problem then is that I have no clue how to calculate this. The rotary encoder is connected directly to the shaft and will spin at the same speed as the shaft, and if I use this code the variable "counter" will increment with 1 per "click". Assuming thats what I have to go on, I need to know how I can calculate how fast counter increases so I can get the speed of the shaft. This is because the shaft will be spinning at diffrent speeds at all times.
The reason I need to know how fast the shaft is spinning is because I have a UI that will display the speed.
I would also appreciate if you have any other approuches that I have missed.
Thank you in advance.
This is how I would calculate the speed :
first add this at the top
import time
then a start time variable
clk = 17
dt = 18
starttime = time.time() ## used to calculate a duration
if you subtract starttime from time.time() you will get how long it has been since you defined the start time variable
( time.time() - starttime )
therefore you then add this
if dtState != clkState:
counter += 1
timetakentospinshaft = time.time() - starttime ## this is how long the shaft took to spin 360 degrees
starttime = time.time() # resetting the duartion
now you have the time taken, so you can calculate the speed by dividing the distance by the time
( speed = distance / time )
the distance can be calculated by multiplying the length of the shaft by 2π (calculation for circumference)
( distance = length of shaft * 2π )
I'm currently in the early stages of making a classic 2D platformer using pygame for a school project, and I was experimenting with the jump mechanic, when I ran into this rather strange problem. Even though I have factored in the timedelta between updates, the jump length and height both get shorter when the amount of time between frames increases.
Here I've let 3 instances of the Player object jump across the screen, moving right with a constant speed of 700 pixels/second, and an initial upwards speed of 700 pixels/second. They each had an artificially increased minimal delta_time of 0.001s, 0.017s and 0.1s respectively.
This is how the new speed and position vectors are calculated inside of the Player object's update function, which is called once every frame (the delta_time is passed in from the main update loop):
self.speed.y += 1000.0 * delta_time
self.position += self.speed * delta_time
And this is how delta_time is calculated at the end of every cycle of the main update loop (it is initialized with a value of zero for the first frame):
delta_time = time.time() - pre_time
pre_time = time.time()
if delta_time < min_delta_time:
time.sleep(min_delta_time - delta_time)
delta_time += time.time() - pre_time
pre_time = time.time()
The min_delta_time represents the minimal time between frame updates.
After many attempts at fixing this, I'm pretty certain that the flaw lies in the line that updates the speed, but even after double checking the math, I still can't figure out what the problem is. At first I thought that it could be the delta_time being imprecise, but if that was the case the horizontal speed would also be affected, and the trails would still line up.
So can i change anything to make the jump/gravity more consistent?
This isn't an error in coding so much as a logical error in the model: by modeling an acceleration as taking place instantaneously at update points rather than continuously across the full time span, inaccuracies are introduced which will get larger the sparser the updates.
To illustrate this, consider an object accelerating at 1m/s^2 from stop. If we model this as above with a 1-second interval, after six seconds our model will put the object at 21m. Repeating with a 2-second interval, we'll put it at 24m, and a 3-second interval will put it at 27m. However, the real position should be 18m.
The solution is to consider average velocity over the simulated time span rather than the instantaneous velocity at the sample points. In other words, rather than counting the full span's worth of acceleration, then using the result as the velocity for the whole span, add half of the span's acceleration, use that velocity to calculate distance traveled in that span, then add the remaining half of the acceleration.
So your update logic would become something like:
self.speed.y += 500.0 * delta_time
self.position += self.speed * delta_time
self.speed.y += 500.0 * delta_time
I am working on a project where i want to move a virtual joystick (vJoy) x number of pixels. However I am unable to find a method of converting pixels into the Joystick's Axis (Axis are -32768 to 32767). An example might be more helpful to explain:
lets say i would like to move 50 Pixels on the x axis in a given time of 100 milliseconds, therefore i would have to find the exact axis force between -32768 to 32767 which would move the object 50 pixels in the given time.
As i require a great deal of accuracy in the movement, i am stumped on a method which would accomplish this task. Any help is appreciated
Thanks
Figure out the total range the physical joystick axis can traverse. You've given min and max values of -32768 to 32767, for a total range of 65535.
Figure out the total range of pixels you want the virtual joystick (vJoy) to be able to move. Let's say it starts out at pixel position 0, and its min and max allowed values are -100 and 100. This gives us a total range of 200.
Now, figure out how many units the physical joystick should travel per virtual joystick movement. We get that by dividing: 65535 / 200 (physical joystick range / virtual joystick range), which gives us 327.675. This number in hypothetical terms would mean "for every pixel the virtual joystick travels, the physical joystick travels 327.675". Let's assign this value to the variable "axis_per_pixel".
Finally, if we know that the value axis value has changed by amount "a" (from your example 50), we can determine how much the physical joystick value needs to change by multiplying: a * axis_per_pixel. In our example, this would be 50 * 327.675, which is 16383.75. So, when the virtual joystick position increases by 50, the physical joystick position should increase by 16383.75 to match.
I'll give a code example. You tagged this as Python, but I'm going to answer in JavaScript because my Python is still a bit rough. Hopefully it's clear enough.
function getPositionIncrease(
minPixelValue, // lowest allowed pixel position, ie -100
maxPixelValue, // highest allowed pixel position, ie 100
minAxisValue, // lowest allowed axis value, ie -32768
maxAxisValue, // highest allowed axis value, ie 32767,
pixelDelta, // what amount the pixel position has changed by
) {
const distPixel = maxPixelValue - minPixelValue;
const distAxis = maxAxisValue - minAxisValue;
const axisPerPixel = distAxis / distPixel;
const axisDelta = pixelDelta * axisPerPixel; // how much the physical axis needs to change to match
return axisDelta;
}
I am practicing on pygame and I was wondering how can we do so that the framerate does not affect the speed of execution of the game
I would like FPS to not be locked and the game to always run at the same speed.
Until now I used the pygame.time.Clock.tick function but the speed of the character was changing depending on the number of FPS, which I don't want.
You have to calculate the movement per frame depending on the frame rate.
pygame.time.Clock.tick returns the number of milliseconds since the last call. When you call it in the application loop, this is the number of milliseconds that have passed since the last frame. Multiply the objects speed by the elapsed time per frame to get constant movement regardless of FPS.
For instance define the distance in number of pixel, which the player should move per second (move_per_second). Then compute the distance per frame in the application loop:
move_per_second = 500
FPS = 60
run = True
clock = pygame.time.Clock()
while run:
ms_frame = clock .tick(FPS)
move_per_frame = move_per_second * ms_frame / 1000
# [...]