1
\$\begingroup\$

Until now, I know 2 bullets' algorithms/configurations : (I mean 2 ways of logically creating a bullet class)

  1. Make a bullet class and create only one instance from it that represents the bullet. The bullet hides under the player's image until he shoots it. Then, he won't be able to shoot again before it reaches the end of the screen and then hides again under the player's sprite. (This configuration uses only one bullet instance for every bullet type, it means that the player cannot shoot multiple bullets of the same type at once)

  2. Make a bullet class and repeatedly create new instances of it as much as the player needs and as long as he can shoot. (depending on the firerate)

Personally, I like the second algorithm, however, I think that creating many instances does slow the performance especially when the number of these instances becomes bigger and bigger (especially at an advanced stage of the game).

So I have some questions in this topic:

  1. Will using numpy arrays instead of lists (in order to store the instances) decrease the risk of slowing the game performance ?

  2. Will deleting some instances regularly prevent from losing the performance ?

pseudocode for this :

if len(bullets_list) >= 5:
    del bullets_list[:4]

Btw I want to know what happens exactly (technically) when an instance is deleted from a list in Python/Pygame. And if there is already an answer for this, just attach it with your answer ;)

  1. Are there any other algorithms to develop a bullet class ?

  2. Are there any improvements to the previous algorithms in order to have a good & stable performance and to ensure a good UX ?

  3. Finally, how can I choose which configuration/algorithm to use depending on my game ? (What are the standards to follow to determine the answer to this question)

\$\endgroup\$
3
  • 2
    \$\begingroup\$ It sounds like you might be looking for "Object Pooling," a pattern where you keep a collection of instances that are dormant, not destroyed, and recycle a dormant instance when possible instead of spawning a new one. \$\endgroup\$
    – DMGregory
    Jul 22 at 14:32
  • \$\begingroup\$ @DMGregory, yeah exactly \$\endgroup\$
    – Salem
    Jul 22 at 14:52
  • 1
    \$\begingroup\$ Just a note that you should profile your game before adding complexity that could be not required. If there is no issue with the most simple pattern to create/use the bullets, you don't need a fancy algorithm. \$\endgroup\$
    – Vaillancourt
    Jul 22 at 16:45
1
\$\begingroup\$

The method that most games use is a variant on 1 where there are multiple instances which get recycled. How many you pool depends on your game.

This way the CPU performance doesn't vary as more bullets are spawned.

You can do this by adding a field in your bullet data that lets you test whether the bullet is active (like a time to live that become negative).

On each shot you look through the list to find the first bullet that is inactive and then initialize it to be active. When updating you can do what's needed to prevent the dead bullets from affecting the render and gameplay

When doing numpy processing then do numpy processing, don't grab the data one by one into python code to do math on instead tell numpy to do the math in bulk on the array directly. The strength of numpy lies in these bulk operations.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ One other trick I've seen is to swap inactive bullets to the tail end of the collection, or to their own separate collection. That way you can iterate over all the active ones in one dense chunk, without testing active flags on each iteration. \$\endgroup\$
    – DMGregory
    Jul 22 at 15:23
  • 1
    \$\begingroup\$ or do a partition operation on the array to move all active bullets to the front when there are multiple that will expire on the same frame \$\endgroup\$ Jul 22 at 16:24
  • \$\begingroup\$ @ratchetfreak, thanks, but you didn't answer my 2nd question, and could you explain more the part about numpy ? \$\endgroup\$
    – Salem
    Jul 23 at 13:59
  • 1
    \$\begingroup\$ @Salem I went forward with the assumption that you would cap the length of the array. That way you know the time to process them all is capped. As for numpy, the cost of each call to numpy and getting data in and out is not zero, it's low enough that a few hundred numpy calls per frame isn't too bad, but not dozens per bullet to process. \$\endgroup\$ Jul 24 at 0:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.