0

I'm trying to build a simulation on python.

I want a grid of 50x50 cells of different colors And balls that spawn on the grid and move until they collid a cell of another color.

When a ball collide a cell of another color, the cell change her color and the ball disapear.

The basics of the simulation is working but i'm facing a huge performance issue.

Below ~40 balls, i'm able to keep a 60FPS simulation. but when i have more (and i would like to handle thousands balls without problem) the FPS are impacted.

How could i optimize this collision detection please? here is part of the code:

GRID_SIZE = 50
CELL_SIZE = 10
self.balls = arcade.SpriteList()
self.cells = arcade.SpriteList(use_spatial_hash=True, spatial_hash_cell_size=CELL_SIZE)

def on_update(self, delta_time):
    self.balls.update()

class Cell(arcade.SpriteSolidColor):
    def __init__(self, x, y, color, grid_app):
        super().__init__(CELL_SIZE, CELL_SIZE, arcade.color.WHITE)
        self.gridApp = grid_app
        self.center_x = x
        self.center_y = y
        self.update_color(color)

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


class Ball(arcade.Sprite):
    def __init__(self, x, y, velocity, angle, spriteConf, grid_app, scale=1):
        super().__init__(SPRITE_CONF[spriteConf]["imagePath"], scale=scale)
        self.center_x = x
        self.center_y = y
        self.my_color=SPRITE_CONF[spriteConf]["color"]
        self.velocity = velocity
        self.my_angle = angle
        self.gridApp = grid_app

    def update(self):
        self.center_x += self.velocity * REFRESH_RATE * math.cos(math.radians(self.my_angle))
        self.center_y += self.velocity * REFRESH_RATE * math.sin(math.radians(self.my_angle))

        # Check for collision with grid sides
        if self.center_x < 0 or self.center_x > WINDOW_WIDTH:
            self.my_angle = 180 - self.my_angle
        if self.center_y < 0 or self.center_y > WINDOW_HEIGHT:
            self.my_angle = 360 - self.my_angle

        ################################################################################
        #                        HERE THE SLOW CODE                                    #
        ################################################################################
        collidedCells = arcade.check_for_collision_with_lists(
            self,
            [
                self.gridApp.cells
            ]
        )
        hasToBeDelete = False
        for cell in collidedCells:
            if cell.color != self.my_color:
                hasToBeDelete = True
                cell.update_color(self.my_color)
        if hasToBeDelete:
            self.gridApp.removeBall(self)
        ################################################################################
        #                        HERE THE SLOW CODE                                    #
        ################################################################################

This is what the game looks like at the max balls before starting having FPS issues: Game situation before performance issue

  • `i would like to handle thousands balls without problem` — not sure if the Python is the best solution here – Alderven Jul 12 '23 at 11:15
  • what could be good then ? I mean it seems to be an easy task for video games to perofmr this kind of problems – Benjamin Darras Jul 12 '23 at 12:23
  • C++ gives you better performance rather than Python – Alderven Jul 12 '23 at 13:34
  • i would like to connect my game to an API or something further, which is much more easier in python btw... is testing collisions between 1000 balls and 2500 cells (in hash map) is this complicated in python ? – Benjamin Darras Jul 12 '23 at 14:54
  • `is testing collisions between 1000 balls and 2500 cells (in hash map) is this complicated in python` — it's not complicated, it's just slow – Alderven Jul 12 '23 at 15:48
  • So in your opinion, i wont be able whatever i do, to have my 60FPS by testing this amount of collisions test ? – Benjamin Darras Jul 12 '23 at 18:13
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/254469/discussion-between-alderven-and-benjamin-darras). – Alderven Jul 12 '23 at 19:00

1 Answers1

0

You can define cells and balls as pixels so you won't needed to calculate shapes collisions which significantly increases performance.

In the following example I've created 100*100 grid and generated 1000 "balls-pixels". You can see that the FPS (displayed in the left upper corner) is always around 60. For this example I've used pyxel lib which is optimized for pixel drawing.

enter image description here

Code:

import pyxel
import random

class Ball(object):
    def __init__(self):
        self.alive = True
        self.x = pyxel.width/2
        self.y = pyxel.height/2

    def draw(self):
        if pyxel.pget(self.x, self.y) == pyxel.COLOR_BLACK:
            self.x += random.choice([-1, 0, 1])
            self.y += random.choice([-1, 0, 1])
        else:
            pyxel.pset(self.x, self.y, pyxel.COLOR_BLACK)
            self.alive = False

class App:
    def __init__(self):
        pyxel.init(100, 100, fps=60)
        pyxel.rect(0, 0, pyxel.width/2, pyxel.height/2, pyxel.COLOR_RED)
        pyxel.rect(pyxel.width/2, 0, pyxel.width/2, pyxel.height/2, pyxel.COLOR_DARK_BLUE)
        pyxel.rect(0, pyxel.height/2, pyxel.width/2, pyxel.height/2, pyxel.COLOR_LIME)
        pyxel.rect(pyxel.width/2, pyxel.height/2, pyxel.width/2, pyxel.height/2, pyxel.COLOR_YELLOW)
        self.balls = [Ball() for i in range(1000)]
        pyxel.run(self.update, self.draw)

    def update(self):
        self.balls = [b for b in self.balls if b.alive]

    def draw(self):
        [b.draw() for b in self.balls]

App()
Alderven
  • 7,569
  • 5
  • 26
  • 38