I have an issue with pygame module. I have a small program where user can click on any place in a window and a ball will spawn there and start moving up-left with 45 degrees angle (135 actually) and everything seems pretty fine but if you spawn many of this orbs so they move like a beam you'll notice that they reflect not actually 90 degrees everytime. I mean, reflection isn't random, it's calculated, but direction of 45 degrees is obviously closer to 90 than it should be. You'll see what I mean if you start the program. So here is the code:
import math
import pygame
NE = 45
NW = 135
SW = 225
SE = 315
V = 5
TICK = 60
RADIUS = 10
def convert_degrees_to_radians(angle_degrees):
return math.radians(angle_degrees)
class Circle:
def __init__(self, center_pos, direction_angle):
self.center_pos = center_pos
self.movement_angle, self.movement_direction = None, None
self.change_direction(direction_angle)
def change_direction(self, new_angle):
self.movement_angle = new_angle
self.movement_direction = pygame.math.Vector2(math.cos(convert_degrees_to_radians(new_angle)),
math.sin(convert_degrees_to_radians(new_angle)))
def change_position(self, new_position):
self.center_pos = new_position
def draw_circle(position):
pygame.draw.circle(screen, pygame.Color('white'), position, RADIUS)
if __name__ == '__main__':
pygame.init()
pygame.display.set_caption('Balls')
size = width, height = 1000, 600
screen = pygame.display.set_mode(size)
running = True
circles = []
clock = pygame.time.Clock()
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
draw_circle(event.pos)
circles.append(Circle(event.pos, NW))
screen.fill(pygame.Color('black'))
clock.tick(TICK)
for circle in circles:
new_center_pos = [int(circle.center_pos[0] + V * circle.movement_direction.x),
int(circle.center_pos[1] - V * circle.movement_direction.y)]
circle.change_position(tuple(new_center_pos))
draw_circle(circle.center_pos)
if circle.center_pos[0] <= RADIUS:
if circle.movement_angle == SW:
circle.change_direction(SE)
if circle.movement_angle == NW:
circle.change_direction(NE)
if circle.center_pos[0] >= width - RADIUS:
if circle.movement_angle == SE:
circle.change_direction(SW)
if circle.movement_angle == NE:
circle.change_direction(NW)
if circle.center_pos[1] <= RADIUS:
if circle.movement_angle == NW:
circle.change_direction(SW)
if circle.movement_angle == NE:
circle.change_direction(SE)
if circle.center_pos[1] >= height - RADIUS:
if circle.movement_angle == SW:
circle.change_direction(NW)
if circle.movement_angle == SE:
circle.change_direction(NE)
pygame.display.flip()
pygame.quit()
Does someone have an idea how to solve it? I tried, but really can't see any mistake.
I tried to calculate directions by myself instead of using degrees, but result was the same, so maybe there is a problem with calculations of new_center_pos but I can't see it.
The problem is here:
new_center_pos = [int(circle.center_pos[0] + V * circle.movement_direction.x), int(circle.center_pos[1] - V * circle.movement_direction.y)]
You calculate everything correctly with floating point coordinates, but unfortunately you convert the coordinates to int
before assigning them to the new position. Since the new position is used for the calculation in the next frame, the movement is inaccurate.
Do not convert to int
new_center_pos = [circle.center_pos[0] + V * circle.movement_direction.x,
circle.center_pos[1] - V * circle.movement_direction.y]
round
only the position for drawing the circle
pygame.draw.circle(screen, pygame.Color('white'), (round(position[0]), round(position[1])), RADIUS)