Reputation: 13
I'm french so pardon my language error.
I try to make a little pygame-zero game with ball physic. So I want to control precisely a ball object in time.
I created a Ball's class, which implement built-in Rect object (to use default collision in case).
import pygame
WIDTH = 300
HEIGHT = 300
class Color:
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
class Ball(pygame.Rect):
def __init__(self, cx, cy, radius, color: Color = Color.WHITE):
position = (cx - radius, cy - radius)
size = (radius * 2, radius * 2)
super(Ball, self).__init__(position, size)
self.radius = radius
self.color = color
@property
def cx(self):
return self.x + self.radius
@cx.setter
def cx(self, cx):
self.x = cx - self.radius
@property
def cy(self):
return self.y + self.radius
@cy.setter
def cy(self, cy):
self.y = cy - self.radius
def draw(self):
position = (self.cx, self.cy)
screen.draw.filled_circle(position, self.radius, self.color);
# screen.draw.rect(self, Color.RED) # draw hitbox
I instance a ball object and want make it go at x position 0 to 300. (the width of the window).
I use the default delta time (it's the time in millisecond elapsed between each frame 1/60) in update's function.
With a speed of 100 pixel * delta time, the ball will traverse the screen at 3 seconds (logical).
But it's not ... after some manipulation I created a second ball (I switch ball's name) and when I use variable instead all works fine.
cl = 0 # get time elpased at launch
ball = Ball(0, 10, 10, Color.RED)
x = ball.cx
ball2 = Ball(0, 30, 10, Color.BLUE)
speed = 100
def draw():
screen.clear()
ball.draw()
ball2.draw()
screen.draw.text(f"{cl:7.2f}", (0, 150))
def update(dt):
global cl, x
if ball.cx <= WIDTH:
cl += dt
# the ball make 3 seconds exactly
x += speed * dt
ball.cx = x
ball2.cx += speed * dt # don't make 3 seconds WHY ?
My question ... why ? Create a custom property, it's much slower than classic variable definition ?
Upvotes: 1
Views: 90
Reputation: 210947
If you want to ensure time precise calculations, then you've to do all the calculations with floating point values. Only convert the coordinates to integral data types when drawing the objects on is integer pixel position.
The .cx
property has an integral data type. Every time when the position is incremented, then the fraction part of the number which is added is lost. This causes an increasing inaccuracy. The object appears to be slower than expected.
Note, if the frame rate would be higher, then the object will even stand still, if the step per frame is smaller than 1.0. The integer part of a value < 1.0 is 0.
Note
ball2.cx += speed * dt
does the same as
ball2.cx += int(speed * dt)
The x
variable is a floating point value. It is incremented with full accuracy. The exact value is cast to int when it is assigned to the .cx
property, but the inaccuracy is always smaller than 1.0 and thus almost negligible.
x += speed * dt ball.cx = x
does the same as
x += speed * dt
ball.cx = int(x)
Upvotes: 1