HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Video Game AI State Change

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
videogamechangestate

Problem

I have here a snippet of code from a simple video game AI enemy. The basic idea behind the enemy is that he can be in one of a few states. By default he is in a patrolling state, where he moves around at a slow speed to random points within a circle. While in this state, he is rolling every X seconds to switch to one of his more interesting states. Currently, he may either transition into a state where he spins rapidly while patrolling as stated above, but at a much faster move speed. Or he will can instead go into a stationary defensive spin. Here is the current method which handles this state change:

IEnumerator RollForStateChange()
{
    while (true)
    {
        yield return new WaitForSeconds(RollTickTime); 

        if (CanRollForStateChange)
        {
            float spinAttackRoll = Random.Range(0, 100);

            if (spinAttackRoll <= SpinAttackChancePerTick)
            {
                StartCoroutine(SpinAttackTimer());
                continue;
            }

            float spinDefenseRoll = Random.Range(0, 100);

            if(spinDefenseRoll <= SpinDefenceChancePerTick)
            {
                StartCoroutine(DefenseSpinTimer());
                continue;
            }
        }
    }
}


While this sort of dice rolling method works just fine for now, I can't help but feel that it is more likely to choose the spin attack, since that roll goes first. I can also see this becoming more inefficient if I wanted to add more states for the enemy to transition into. However, I am struggling to come up with a more elegant way to decide which state the enemy should go into.

Solution

You are right that this roll is biased. Instead of using a fixed range and less-than for your rolls, you should just roll once and use specific ranges to choose which action is taken. Something like:

int spinAttackChancePerTickMin = 0;
int spinAttackChancePerTickMax = 50;

int spinDefenceChancePerTickMin = 50;
int spinDefenceChancePerTickMax = 100;

int roll = Random.Range(0, 100);

if (roll >= spinAttackChancePerTickMin && 
    roll < spinAttackChancePerTickMax)
{
    StartCoroutine(SpinAttackTimer());
    continue;
}

StartCoroutine(DefenseSpinTimer());

Code Snippets

int spinAttackChancePerTickMin = 0;
int spinAttackChancePerTickMax = 50;

int spinDefenceChancePerTickMin = 50;
int spinDefenceChancePerTickMax = 100;

int roll = Random.Range(0, 100);

if (roll >= spinAttackChancePerTickMin && 
    roll < spinAttackChancePerTickMax)
{
    StartCoroutine(SpinAttackTimer());
    continue;
}

StartCoroutine(DefenseSpinTimer());

Context

StackExchange Code Review Q#96161, answer score: 5

Revisions (0)

No revisions yet.