Opponent AI: simulate physics off screen to find the best move

Hi everyone! I’m a beginner dev working on my first videogame, which is a simple cap races game in 2D. I have Godot 4.1, every cap is a RigidBody2D with a circular collision shape and the race works by turns, where the caps make their shots one by one. There will be, at most, 10 caps racing at the same time.

I’m at the point of programming the opponent caps AI, and I have come to the conclusion that the only way to do it is to simulate a lot of different shots and rank them according to some function in order to decide which one is the best shot. Every shot implies an impulse and all the possible collisions with several other caps or obstacles. Then, I can add some random error and stuff like that to make it more realistic.

So, my questions are:

  • Is this the correct approach for the opponents AI?
  • How do I simulate a lot of shots in the background without the player noticing anything on screen and without taking too much time?
  • Can I simulate them in parallel? Would it be too expensive to simulate, let’s say, 500 shots in parallel?

Thank you very much!

A lot of these questions are dependent on your game

Do you or the player really care about accuracy or can you just make up a reasonable score for each cpu opponent?

As far as I can tell Godot physics don’t like to be sped up, they may have to run real-time, again unless you pick a random number.

It would be 500x as expensive to simulate them in parallel, for a 2D game I think that’s fairly reasonable on PC, but it depends on your target platform, on mobile that could be really bad.

In general, I don’t need a super accurate shot, unlike in a billiard game, for example. But the opponent AI is similar: caps should be able to improvise ‘realisticly’ depending on the situation of the circuit, which I think I can only achieve by simulating shots and judging them depending on their final position.

Every shot lasts for around 5 seconds, so, if I could simulate every shot in parallel at x2 speed, that would probably be good enough. For normal-size circles colliding, I assume that shouldn’t cause any problems.

But still, I don’t know how to simulate a shot in the background. Do I create a new collision shape with no sprite for each cap and track them? Also, I’m not sure what you mean by “unless you pick a random number”.

Ok, for now it is a PC game but It would be great to make it a mobile phone game in the future. I should be able to reduce that number. In order to do them in parallel, should I create a thread for each shot? Wouldn’t the bodies interfere with each other if they are run at the same time? Sorry if they are naive questions :sweat_smile: but I really want to learn how to do this. Thank you!

I am not familiar with cap races, I figured you could simulate what ever an “opponent” would do via a end-result like score. Like in an Archery game I could just pretend the opponent got a bullseye, missed, or anything in-between with randf() rather than going though a Arrow’s pull, wind, and gravity simulations; fake it.

The game right now looks something like this.

The caps or obstacles might be all around the circuit. You can shoot forward to advance or shoot against other caps to throw them out the circuit. If I just pick a direction and shoot, maybe there is something in the way that prevents that. So I cannot fake the result of the shot (the final position of every cap) because I would have to know how to make that ‘faked’ shot. The number of different situations that might arise during a race is too big, that’s why I don’t see any solution apart from trying a lot of shots and see what happens.

1 Like

Yeah I think you will have to simulate it. If the caps can run into each other then you have a really big problem, you need to go one-by-one. Might just have to limit the number of caps if that’s the case.

1 Like

I understand. Yes, the caps collide with each other, it’s the most fun part, so I guess I should focus on discarding as many possible shots as possible and simulating just a few, or maybe find a way of predicting, more or less, how a shot would end without actually simulating it. Thank you very much for your help!

1 Like

I finally found a more than reasonable solution. As I said in another reply, the key was to predict rather than simulate. I summarize how to mathematically predict the position where my caps (just cirles) will land after colliding with each other with an error of around 0.035 pixels (at least for simple collisions) and a procedure that can be done over 10,000 times per second.

  1. When a player applies an impulse to a cap, predict the position where it is going to end if no collisions occur. Recall that its velocity updates every frame according to the formula: new_vel = previous_vel * (1 - linear_damp/fps), so this prediction is extremely accurate.
  2. Check if it is going to collide with any cap on its way. If not, then your last prediction is your final prediction.
  3. If it is, use some trigonometry to calculate the exact point where they will collide, and use some basic physics to predict the direction and impulse they will suffer due to the bounce.
  4. To calculate their final position now, you can go back to step 1 with the impulses caused by the bounce, in order to see if their path is clear or if they will collide again.

If anyone wants me to detail some of the steps, let me know! The collision frame was especially difficult to understand.

For no collisions or one collision, the prediction misses by around 0.035 pixels on average (in practice, nothing at all). For two collisions, if they are low speed, you get a similar error, although more complicated ones can lead to a couple of pixels of error. Three collisions sometimes lead to an error of 20 or more pixels in some of the involved caps (which is around 25% of a cap size, so actually not extremely bad).

Still, by using 20 threads that work out 500 shots each, I can calculate 10,000 shots that go through three collisions in less than a second.

Pros: I can decide the number of collisions that enemy caps can foresee, which is an interesting way of setting their difficulty level. I can comfortably predict thousands of shots in less than a second.

Cons: The method only considers two collisions per cap: one ‘they-hit-type’ collision and one ‘they-get-hit-type’ collision. This means that, for example, if cap1 hits cap2, cap2 hits cap3, bounces back and hits cap1 again, that last collision is not considered. However, this would be very difficult to foresee for a human player too, so it feels like a realistic flaw.

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.