0

This collisionHandler function seems to be correctly identifying when there's a collision between my two objects, but when I set the velocities of the objects, nothing happens.

Here's my full code:

import numpy as np
import pygame
from sys import exit

#Initializing Parameters
pygame.init()

displayInfo = pygame.display.Info()
screenWidth = displayInfo.current_w
screenHeight = displayInfo.current_h

display_surface = pygame.display.set_mode((screenWidth, screenHeight-50), pygame.RESIZABLE)
pygame.display.set_caption("Python Physics Engine")

clock = pygame.time.Clock()
fps_limit = 60

backgroundColor = (255,255,255)
objectList = []

class PhysicsObject():
    global objectList

    def convert(self, measurement):
        return int(measurement)*self.conversion_factor

    def __init__(self, x, y, width, height, mass, velocity, acceleration, max_velocity):
        self.conversion_factor = 50 #meaning 50 px = 1m

        self.x = round(x)
        self.y = round(y)
        self.width = self.convert(width)
        self.height = self.convert(height)
        self.mass = mass
        self.velocity = (self.convert(velocity[0]), self.convert(velocity[1]))
        self.acceleration = (self.convert(acceleration[0]), self.convert(acceleration[1]))
        self.max_velocity = (self.convert(max_velocity[0]), self.convert(max_velocity[1]))
        self.color = (255,0,0)
        self.objectRect = pygame.Rect(self.x, self.y, self.width, self.height)
        self.canCollide = True
        
    def update(self, dt):
        #updating velocity
        self.velocity = (int(self.velocity[0] + self.acceleration[0] * dt),
                         int(self.velocity[1] + self.acceleration[1] * dt))
        
        #ensuring velocity doesn't go past maximum
        self.velocity = (min(self.velocity[0], self.max_velocity[0]),
                         min(self.velocity[1], self.max_velocity[1]))

        #updating position
        self.x += int(self.velocity[0] * dt)
        self.y += int(self.velocity[1] * dt)

        #checking that object won't go off screen
        screenWidth, screenHeight = display_surface.get_size()

        if self.x < 0:
            self.x = 0
            self.velocity = (-self.velocity[0], self.velocity[1])
        elif self.x + self.width > screenWidth:
            self.x = screenWidth - self.width
            self.velocity = (-self.velocity[0], self.velocity[1])
        
        if self.y < 0:
            self.y = 0
            self.velocity = (self.velocity[0], -self.velocity[1])
        elif self.y + self.height > screenHeight:
            self.y = screenHeight - self.height
            self.velocity = (self.velocity[0], -self.velocity[1])

        self.objectRect = pygame.Rect(self.x, self.y, self.width, self.height)
        collisionHandler()

    def draw(self):
        self.objectRect = pygame.Rect(self.x, self.y, self.width, self.height)
        pygame.draw.rect(display_surface, self.color, self.objectRect, 0, 1)

    def set_color(self, color):
        self.color = color

    def set_velocity(self, new_velocity):
        print("Changing velocity from ", self.velocity, " to ", new_velocity)
        self.velocity = new_velocity

    def change_collision_status(self):
        print("Changing collision status")
        self.canCollide = not self.canCollide

def collisionHandler():
    global objectList
    for object in objectList:
        for object2 in objectList:
            if object != object2 and object.objectRect.colliderect(object2.objectRect) and object.canCollide and object2.canCollide:
                calculateCollision(object, object2)

def calculateCollision(object1, object2):
    object1.change_collision_status()
    object2.change_collision_status()

    m1 = object1.mass
    m2 = object2.mass
    v1x, v1y = object1.velocity
    v2x, v2y = object2.velocity

    v1xf = round(((m1-m2)/(m1+m2))*v1x + (2*m2/(m1+m2))*v2x)
    v2xf = round((2*m1/(m1+m2)) * v1x + ((m2-m1)/(m1+m2))*v2x)

    object1.set_velocity((v1xf, v1y))
    object2.set_velocity((v2xf, v2y))

    object1.change_collision_status()
    object2.change_collision_status()

gravity = (0, 0)

#input parameters in SI units, class will handle conversion
testObject1 = PhysicsObject(100,             300, 1, 1, 1,  (3,0), gravity, (1000, 1000))
testObject2 = PhysicsObject(screenWidth-100, 275, 2, 2, 100, (-3, 0), gravity, (1000,1000))

testObject2.set_color((0,255,0))

objectList = [testObject1, testObject2]

running = True
#main game loop
while running:
    time_scale =  1e-3
    dt = clock.tick(fps_limit) * time_scale

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            exit()

        elif event.type == pygame.VIDEORESIZE:
            display_surface = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)

    testObject1.update(dt)
    testObject2.update(dt)
    display_surface.fill(backgroundColor)
    testObject1.draw()
    testObject2.draw()
    
    pygame.time.delay(int(1000/fps_limit))
    pygame.display.flip()

pygame.quit()

I've tried adjusting only one of the objects' velocities, which seemed to work slightly better. However, when I tried adjusting only the other object, I observed the same behavior. I've tried rounding all of my velocities and positions in case the error is caused by some misalignment with the pixels on the screen which is messing up the collideRect function, but to no avail. I wonder if using sprites is the only way I can get it to work, but I'm unsure.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Ben Felson
  • 21
  • 4
  • 2
    Welcome to SO! Can you please [edit] to pose a clear, precise question? ['Can somebody help me?' is not an actual question](https://meta.stackoverflow.com/a/284237/11107541) – starball Dec 30 '22 at 00:11

1 Answers1

0

In fact, the code that changes the direction works. But the problem is that the objects do not collide exactly, but overlap a little when they collide. This means that after you have detected a collision and changed the direction, you will detect another collision in the next frame and change the direction of movement back to the original direction. The problem is similar to the one described here: Sometimes the ball doesn't bounce off the paddle in pong game.
I haven't studied your code in detail, but one possible solution is to align the objects after a collision. For example, change the position of object 1 so that the right side of object 1 just touches the left side of object 2:

def calculateCollision(object1, object2):
    object1.change_collision_status()
    object2.change_collision_status()
    
    object1.objectRect.right = object2.objectRect.left
    object1.x = object1.objectRect.x
 
    # [...]

Also see Animation glitch in Pygame.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174