Search code examples
pythonpygame

How to stop fluctuations in pygame calcualtions


I am making a game and I have a problem with my code one mathematic function in ball speed has some fluctuations because Python can not calculate with 100% accuracy.

In normal code, it is not a problem but because I have a condition to change the type of movement from rising to falling it is set on some value which is interesting.

If I change this number to 300 it will do just one cycle of falling and rising but if I change it to 320 it will make a few.

The problem is that y coordination of the ball is so random ChatGPT said to me that it is because this fluctuation will accumulate but the last ball_y is so random sometimes it is 319 next 317, 320, but if it jumps on 322 or something bigger then 320 it will stop

I know that the easy way is to use some range but it is not working because it will always jump on some not good value. Can you tell me how to remove this problem with my code?

import pygame as pg
import random

pg.init()

# Constants
BACKGROUND_COLOR = pg.Color('black')
BALL_X = 240

# Initialize the game window
screen = pg.display.set_mode((500, 800))
clock = pg.time.Clock()

# Load the background image
background_image = pg.image.load("bg.png")

# Draw a rhombus shape
def draw_rhombus(screen, color, x, space):
    point1 = (170 + x, 740 - space)
    point2 = (350 + x, 740 - space)
    point3 = (300 + x, 790 - space)
    point4 = (120 + x, 790 - space)
    points = [point1, point2, point3, point4]
    return pg.draw.polygon(screen, color, points)

# Generate a list of unique colors
def generate_color_list():
    colors = ["RED", "ORANGE", "BLUE", "WHITE"]
    available_colors = colors.copy()
    color_list = []

    while len(color_list) < 43:
        if not available_colors:
            available_colors = colors.copy()

        color = random.choice(available_colors)

        if color_list and color == color_list[-1]:
            continue

        color_list.append(color)
        available_colors.remove(color)

    return color_list

# Draw a stack of rhombus shapes
def draw_rhombus_stack(color_list, space, x):
    positions = []
    for i in range(43):
        x_position = x if i == 42 else 0
        position = draw_rhombus(screen, color_list[i], x_position, space + i * 8)
        positions.append(position)

    return positions

# Move the rhombus stack left or right
def move_rhombus_stack(x, keys):
    x_coor = x  # Define x_coor variable
    if keys[pg.K_LEFT] and x_coor > -500:
        x_coor -= 5
    if keys[pg.K_RIGHT] and x_coor < 500:
        x_coor += 5
    return x_coor

# Functions for handling the ball
def draw_ball(color, x, y):
    pg.draw.circle(screen, color, (x, y), 15)

def move_ball_down(y, speed, acceleration, direction):
    if direction == "down":
        speed += acceleration
        y += int(speed)
        if y >= 415:
            direction = "up"
    return y, speed, direction

def move_ball_up(y, speed, acceleration, direction):
    if direction == "up":
        speed -= acceleration
        if speed <= 2:
            speed = 0
        y -= int(speed)
        if y <= 320:
            direction = "down"
    return y, speed, direction

def check_collision(space, color_list, ball_color, index):
    if 0 < space < 500 and color_list[ball_color] != color_list[index]:
        return True
    return False

def choose_ball_color(color_list):
    return random.randint(0, 4)

# Main game loop
done = False
color_list = generate_color_list()
ball_color_index = choose_ball_color(color_list)
ball_x = BALL_X
ball_y = 300
ball_speed = 1
ball_acceleration = 0.1
ball_direction = "down"
space = 0
x_coor =0

while not done:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            done = True

    keys = pg.key.get_pressed()
    x_coor = move_rhombus_stack(x_coor, keys)
    screen.fill(BACKGROUND_COLOR)

    # Draw rhombus stack and store their positions
    rhombus_positions = draw_rhombus_stack(color_list, space, x_coor)

    # Check for collisions
    end = False
    for i, position in enumerate(rhombus_positions):
        end = check_collision(space, color_list, ball_color_index, i)
        if end:
            break

    draw_ball(color_list[ball_color_index], ball_x, ball_y)

    # Ball movement
    ball_y, ball_speed, ball_direction = move_ball_down(ball_y, ball_speed, ball_acceleration, ball_direction)
    ball_y, ball_speed, ball_direction = move_ball_up(ball_y, ball_speed, ball_acceleration, ball_direction)

    # Check for game over condition
    if end:
        # Implement game over logic here
        pass

    pg.display.flip()
    clock.tick(30)

pg.quit()

Solution

  • I suggest changing the condition when the ball changes direction and starts to fall. The ball does not change its direction at a certain height, but when the velocity is 0.
    You will also get a more smooth movement if you do not convert the position of the ball to an integral value each time (y -= int(speed)), but keep the floating point value (y -= speed).

    def move_ball_down(y, speed, acceleration, direction):
        if direction == "down":
            speed += acceleration
            if y >= 415:
                direction = "up"
            else:
                y += speed
        return y, speed, direction
    
    def move_ball_up(y, speed, acceleration, direction):
        if direction == "up":
            speed -= acceleration
            if speed <= 0.0:
                direction = "down"
                speed = 0
            y -= speed
        return y, speed, direction
    

    round instead the position of the ball when it is drawn on the screen:

    draw_ball(color_list[ball_color_index], ball_x, round(ball_y))
    

    Also, if you start with a speed of 0 instead of 1 (ball_speed = 0), the ball will always start falling at a height of 300.