Saturday, August 10, 2013

Programming Aggressiveness, Building Personality Traits Into Game AI (Part 2)

Howdy! Welcome to Part 2 of Building Personality Traits Into Game AI. Part 1 can be found here. The personality trait that I will be covering in this section is aggressiveness. Similar to the bravery trait, I'm looking for a function that will return a value of either true or false. This returned value will indicate whether or not a unit is aggressive enough to attack an enemy.

Drawing Board: Idea #1 - Skewed Randoms

Originally I had thought of using skewed random chance to determine if a unit was aggressive enough to attack--meaning that when an enemy entered a unit's sight, a number between 1 and 100 would be randomly generated and then be compared to the unit's aggressiveness trait (also a number between 1 and 100). If the number was less than the unit's aggressiveness trait, then the unit would be aggressive enough to attack the enemy.

The problem with this method is that the AI behavior is a little too random--too inconsistent. The player would have trouble figuring out how exactly the aggressiveness trait affected a unit's behavior. So I decided to do some research.

Drawing Board: Idea #2 - Interpersonal Space Multiplier

The most applicable (and interesting) thing that I found while researching was the concept of Proxemics, pioneered by Edward T. Hall.

Take a look at the following diagram regarding interpersonal space:


Edward T. Hall's personal reaction bubbles, showing radius in feet and meters.

This is what Wikipedia has to say about interpersonal space:

Interpersonal space is the psychological "bubble" that exists when one person stands too close to another. Research has revealed that there are four different zones of interpersonal space:
  • Intimate distance ranges from touching to about 18 inches (46 cm) apart, and is reserved for lovers, children, close family members, friends, and pet animals.
  • Personal distance begins about an arm's length away; starting around 18 inches (46 cm) from the person and ending about 4 feet (122 cm) away. This space is used in conversations with friends, to chat with associates, and in group discussions.
  • Social distance ranges from 4 to 8 feet (1.2 m - 2.4 m) away from the person and is reserved for strangers, newly formed groups, and new acquaintances.
  • Public distance includes anything more than 8 feet (2.4 m) away, and is used for speeches, lectures, and theater. Public distance is essentially that range reserved for larger audiences.
To summarize:
  • Intimate space <= 1.5 ft
  • Personal Space <= 4 ft
  • Social Space <= 8 ft
  • Public Space <= 25 ft

How is this useful?


How can I relate the concept of interpersonal space to my game? My AI units have a limited sight range (each unit can only see a certain distance around itself) that essentially creates a sight "bubble" around each unit. See where I'm going with this? What if I utilized the different levels of interpersonal space to help determine whether or not a unit is aggressive enough to attack an enemy? Taking the most literal translation of the diagram, I made each section of interpersonal space correlate to a certain distance from the unit. Each section would be relative to a certain percentage of a unit's sight range.

If I were to equate the outer radius (25ft) of the diagram to the outer radius of a unit's sight bubble, I would get the following ratios:
  • Intimate space:
    1.5 ft / 25 ft -> 0.06
    At or closer than 6% of a unit's sight range
     
  • Personal Space:
    4 ft / 25 ft -> 0.16
    At or closer than 16% of a unit's sight range
     
  • Social Space:
    8 ft / 25 ft -> 0.32
    At or closer than 32% of a unit's sight range
     
  • Public Space:
    25 ft / 25 ft -> 1
    At or closer than 100% of a unit's sight range
     
Currently I'm using a distance of 100 for a unit's initial sight range so:
  • Intimate Space <= 6
  • Personal Space <= 16
  • Social Space <= 32
  • Public Space <= 100
For reference, each unit's body has an initial radius of 6. This causes an issue. This means that intimate space would be inside of a unit's body! Considering that we will not have a fixed body size (units can grow) or sight range, this could get difficult to manage. I'm going to scrap this idea for now. Yes, there is a workaround for this issue, but I've already done a considerable amount of work beforehand in determining the best way (for me) to evaluate aggressiveness, and I've found a better, less complicated solution. However, this is still a valuable concept so I'm going to finish explaining my train of thought.

The idea was to add a multiplier (dependent on which bubble an enemy was located in) to how likely a unit was to be aggressive. I tried to translate each bubble into a value based on the premise of how someone might react towards a stranger within that bubble.

Here are my interpretations of aggressiveness in each bubble:
  • Intimate Space:
    The hug zone. This space starts at about elbow's length away. This is a space where someone would be very uncomfortable if a stranger were to get within. This space represents an area where physical damage can easily be inflicted and an attack would be hard to dodge.
    Multiplier: 2
     
  • Personal Space:
    This is about arm's length away. This space represents an area where physical damage can easily be inflicted, but an attack would be fairly easy to dodge.
    Multiplier: 1.5
     
  • Social Space:
    This is an acceptable space for strangers to be located in, but this space also implies that there is likely some form of interaction between everyone within. This is the space where someone might start to get defensive.
    Multiplier: 1.25
     
  • Public Space:
    This is an acceptable space for strangers to be in and does not imply any sort of direct interaction.
    There is no multiplier for this space.
     
The final piece of this concept involves a comparison. Presuming we could generate a value, between 1 and 100, based on proximity, we would take that value and multiply it by the interpersonal space multiplier. If the resulting value was greater than or equal to the aggressiveness trait (also a value between 1 and 100), then the unit would be considered aggressive enough to attack an enemy and the function would return true. Let's continue on to the next idea.

Drawing Board: Idea #3 - Territory Multiplier

Animals have been known to be more aggressive in areas that they consider to be their territory. This concept is fairly simple. What if I used a multiplier (dependent on where, in regards to the unit's territory, the unit and enemy are located) in determining whether or not a unit is aggressive enough to attack?

Scenarios and interpretations translated into multipliers:
  • The unit is in the unit's territory while the enemy is not:
    This could be equated to being in your house, or on your lawn, and watching someone ride a bike down the middle of the street. This might prompt a unit to be slightly more aggressive because it is in its own territory, but not too aggressive because the enemy is not in the unit's territory.
    Multiplier: 1.25
     
  • Both the unit and the enemy are in the unit's territory: This could be equated to having a stranger enter your property while you are on the premises. You aren't too concerned about the stranger because you can keep your eye on them and can potentially stop any form of property damage/theft they might intend to do. However, there is still a stranger on your property so you will be more aggressive than normal.
    Multiplier: 1.5
     
  • The enemy is in the unit's territory while the unit is not:
    This could be equated to being at your neighbor's house and watching someone walk up to your down. There is an unattended stranger on your property. If the stranger were to attempt any property damage or theft, it would be very difficult for you to stop them. You might be very aggressive in this situation.
    Multiplier: 2
     
  • Neither the unit nor the enemy is in the unit's territory:
    This would be like seeing a stranger in public.
    There is no multiplier for this scenario.
     
Final Implemented Idea:

I wanted something inherently simple for implementation so I settled with a form of proximity based aggressiveness. The aggressiveness trait would correlate with a certain percentage of a unit's sight range. So a unit with a value of 50 for aggressiveness would be aggressive enough to attack any enemies that are at or closer than 50% of the unit's sight range. This idea utilizes a linear progression between the aggressiveness trait value and how aggressive a unit actually is. However, this idea still retains dynamism due to varying sight ranges and aggressiveness trait values. If it wasn't apparent, this idea is a modified version of the Interpersonal Space Multiplier.

Here is the function:
float percentOfSightEnemyIsAt = Vector2.Distance(Position, enemy.Position) / Sight; 

if (percentOfSightEnemyIsAt <= Personality.Aggressiveness / 100f) 
{ 
 return (true); 
} 
else 
{ 
 return (false); 
}

This function is designed so that an aggressiveness trait value of 100 will always result in a unit being aggressive enough to attack an enemy within the unit's sight range. While this is an elegant solution to defining aggressiveness, this function seems to be a little too simple. The aggressiveness behavior would greatly benefit from some additional logic. I'm going to try and work the Territory Multiplier concept into this function.

Here is the final function:
float territoryMultiplier = 1; 

if (isUnitInTerritory) 
{ 
 territoryMultiplier = 2f; 

 if (isEnemyInTerritory) 
 { 
  territoryMultiplier = 3f; 
 } 
} 

float percentOfSightEnemyIsAt = Vector2.Distance(Position, enemy.Position) / Sight; 

if (percentOfSightEnemyIsAt <= (territoryMultiplier * Personality.Aggressiveness) / 100f) 
{ 
 return (true); 
}
else 
{ 
 return (false); 
}

As you can see, the territory multiplier increases the range at which a unit will be aggressive. This leads to some interesting behavior.

Here's a video demoing this behavior:


That's all I have for today! I know that there are other ways this could have been done, so feel free to share any of your thoughts!

1 comment:

  1. In regards to the video, if you were wondering why the units didn't try to attack the enemy every time the units reentered the territory:

    I had some code in place that prevented units from evaluating the same enemy for an attack twice in a row. The code was something I had accidentally left from my previous coding structure. What was happening was the enemy was ignored the second time around because that enemy had just been evaluated previously. Without that code, the units should have been teetering on the edge of the territory. Hope that clears things up! This was something I noticed after recording the video.

    ReplyDelete