kalaw said:
What is the best way ….
“The best way” in general is not to aim for the best way at all. The simple reason for that is that you're not going to know where the problems are going to be until **after** you built everything.
At that point it's pretty much 100% guaranteed that problems are **not** where you expect(ed) them, it's a safe bet. Really, problems are always elsewhere where you least expect them. That includes all code in the program, including any “systems” you add for performance. Assuming all code is equally possibly problematic, that means that adding more code for improved performance means a higher risk that performance problems will arise in that code.
A second point here is that “optimize this game as much as possible” as you phrase it, takes time, loads of time. All that time cannot be spend on enhancing or polishing gameplay, and making sure it's fun to play the game. So even if you do optimize, you don't want to do everything that can be done as it's too expensive (in time or money ).
Normally you stop optimizing if it's “fast enough”. You don't need code capable of handling more than the game will ever contain.
----
The better tactic here is to aim for a simple but effective approach until the game runs. That means use a light-weight solution and don't do things that knowingly scale badly. Having 500,000 bullets and 10,000 enemies both in two lists, doing
for bullet in bullets:
for enemy in enemies:
if hits(enemy, bullet):
apply_damage(enemy)
every frame is likely not a good idea.
On the other hand, don't overthink it, it's amazing how fast modern computers are if you use them effectively. For a first version of the game this is perhaps fine for a limited number of enemies / bullets.
Getting the program running has an extremely big advantage, even if it's terribly slow. When it runs, you can measure performance of the code, actually find the precise spot with the problem, and understand and fix it.