Latest developer blog from Gameplay Football forum.
Hello everyone!
Today, I'll try to give some insights into the decision-making of AI players while dribbling with the ball. I was approached by gamedevs Oscar Torrijos and Chris Rollinson from Football Strategy, an online football manager game (
http://www.footballstrategy.org - you can check their latest newsletter at
http://imgur.com/RhKegGy). We decided to share some insights; they were curious how my AI worked, and I thought it would be nice to make a developer blogpost out of it, since some of you here may be interested in it as well!
I'll start with posting this cryptically looking image - don't worry, I'll explain it all
http://www.properlydecent.com/data/gfxmeuk/AI_dribble_explanation.png
The green dot with the arrows shooting out of it is the AI player currently controlling the ball, for which we need to calculate the most appropriate dribble movement. The red dots are - you guessed it - the 'bad guys', looking to stop our hero from advancing towards their goal, which can be seen on the left. The arrows are the possible moves our player could make (the 'tips' of the arrows being the resulting position after one 'step' or 'animation'). So, where to start?
1. Collect all possible dribble directions - the arrows in the image. For the sake of simplicity, I've drawn 8 - this numbers is arbitrary though, and depends on how much CPU power you want to be used.
2. For every possible direction, collect 2 values: (a) the 'amount' of 'opponent proximity', and (b) the 'amount' of 'direction towards opponent goal'.
2(a). The amount of opponent proximity is basically the cumulative inverted distance between our player and all opponents in his vicinity (on an arbitrary scale). For point 2, for example, opponent A is at 3 metres distance, and opponent B is at 1 metre distance. Let's say we stop taking opponents seriously after 6 metres, so opponent A is at 50% proximity and opponent B at roughly 84%. (Higher = closer, because I chose high values to mean 'bad'.) I simply add these percentages and get 134, which is pretty high.
2(b). The amount of direction towards opponent goal is simple: I calculate the angle between the arrow's direction and the goal direction. Lower == better - remember, high values mean 'bad'. I normalize this angle so that 0 == towards goal, 100 == away from goal (50 == perpendicular to goal direction).
3. Once we've calculated these 2 values for all 'arrows', we get this table. (For the sake of simplicity, I've only included 3 possible directions).
http://www.properlydecent.com/data/gfxmeuk/AI_dribble_explanation_table1.png
Now simply pick the arrow/direction belonging to the lowest 'total' value - in this case, arrow 8 - and we have our desired direction.
4. However, we may want to tweak the balance between avoiding enemies and trying to get into the direction of the goal. For this, simply add weights. Pseudocode would look like this:
opponentProximityWeight = 1.0;
goalDirectionWeight = 3.0; // heavier penalty for going 'the wrong way'
total = a * opponentProximityWeight + b * goalDirectionWeight;
Using the values from our example table above, this would result in this table:
http://www.properlydecent.com/data/gfxmeuk/AI_dribble_explanation_table2.png
Resulting direction will now be 'arrow 2'.
5. With these weights, the player will try to dribble past the 2 defenders, thereby probably losing the ball - but if he can get past, he'll end up 1 on 1 with the goalie. So there's an important pointer on how to use the weights: Defenders should have a higher opponentProximityWeight (or a lower goalDirectionWeight), the other way around for more offensive players.
Some ideas I've used:
* To make things less rigid, one could add random values to the weights (or the a/b values), even different random values for all 'arrows'. The worse 'dribble' or 'mental' stats a player has, the more diverse the random values. This way, worse players make more illogical decisions.
* In this example, I've used arrows of just 1 (arbitrary) unit in length. In 'real life', I use 3 times as much arrows: 3 different lengths per direction for the 3 different velocities at which a player can dribble in my game. I also add a 0-length arrow, meaning 'stand still'.
* In this example, the players are all supposed to be 'static' in the beginning. In 'real life', they may have momentum (already going some direction), so for their positions, I add their current momentum * the duration of the average animation (220ms in my game). Basically, this gives the average situation in 220ms - they may change direction, but according to Newton, they won't be able to change that much that soon anyway, so this gives a proper approximation. I also add this momentum offset to the dribbling player himself, since he won't be able to actually sprint 180 degrees backwards when he's sprinting forwards at the moment.
* This 'weighted ratings' system is very versatile - I also use it for off-the-ball positioning, but then with a lot more factors, like how close the player is to other team players (you want some distance there, usually), to the ball, to the goal, to the sidelines.. go wild!
I hope you enjoyed these insights!