Sign in to follow this  
BaneTrapper

Making AI, Decisions for logic on 2d tiled map

Recommended Posts

Hello.

Currently i am at point of making AI in my game.

Take a note i have never done a AI.

 

I am thinking about making AI that will "Look for food when hungry", "Look for water when thirsty", "Run from Predator(Unit)", "Sleep/Rest"

What i have planed on making

int sightRange = 5;

Sight range represent tiles the unit can see around it self.

 

When hunger is low "Look for food when hungry". Loop tiles around to see if any "food" is in "sight range", if false walk in direction, and after some time check for food again.

 

"Look for water when thirsty" same as food.

 

"Run from Predator(Unit)"

From time to time loop around unit for "Sight range" and check if there is a unit that is counted as "Predator" to this type of unit and if true make logic from now on to "Run from Predator(Unit)", and from time to time check if escaped.

 

"Sleep/Rest"

Depending on unit. ZzZzZ at night(IDLE) for X

 

What i am concerned with is that what i will make will be slow and pain full to use in lager scale (10-20 unit) + all other there is in game.

So the question is: Any suggestions and tutorials you can provide that do indepth c++ AI

Edited by BaneTrapper

Share this post


Link to post
Share on other sites

Hello.

 

One thing that came to mind, its very simple but it will help.

Dont do all the checks every frame.

 

There are several different ways of doing this, either by creating some timer and callback system or by simply
using a counter.

The later being the easiest and fastest way to see if it will help boost performance.

In your unit class you could have something like:

void Unit::Tick(float deltaTime)
{
    if(frameCounter > updateRange)
    {
        frameCounter = 0;
        UpdateBehaviour();
    }
}

Where of course "UpdateBehaviour" would check the surroundings and decide what to do next.

As you can see its very simple, straight forward.
Instead of checking every frame you only check every X frame.

 

One thing to note here.
Lets say you have 100 units, they all use this type of code.

If they are all created on the same frame and they all start their frameCounter at 0.

Then every X frame you will get a spike in performance, since they will all call "UpdateBehaviour" at the same frame.

So, it would be nice to add an offset in the constructor: 

Unit::Unit()
{
    updateRange = 3;
    frameCounter = rand() % updateRange;
}

In this example i used 3. It may in fact be very low.

Lets say your game runs in 60 FPS.
That would mean that during one second your units would do 20 checks.
Perhaps 10 would be a better, that would equal to 6 checks every second.
To find what suits your needs best, you will have to do some trail and error smile.png

 

Hope it helps

Share this post


Link to post
Share on other sites

That is a good technique to split up AI logic intro separate loops like that.

 

I am also not satisfied with my logic for "Look for food when hungry", "Look for water when thirsty".

Its not efficient and if all units on map would run this function the game would be running mare 30fps.

 

What i do is loop all tiles around the unit and check its type if it fits the food type that unit eats.

I was thinking about making a representation of area.

For example:
This 10x10 blocks have "Berry bushes, grass, dirtm sand, water" While the unit is in this square it regenerates if(Omnivore or Herbivore) food from berry bush and grass, water.

 

But i am feeling i am stretching stuff out to long, the point of a game is 2d exploration with crafting and leveling and raiding dungeons(Sounds allot) but i will take the time.

Edited by BaneTrapper

Share this post


Link to post
Share on other sites

Sure the game will take some performance hits if all units at all time is checking for food, water etc.

 

Im sure there are loads of interesting techniques to optimize ai for such behavior. But on simple thing

one could do to optimize if there are lots of ais in the game world.
Is to do something along the lines of this:

void Unit::updateTickPriority()
{
    float squaredDistanceToPlayer = player->getLocation().squareDistance(location);

    if(squaredDistanceToPlayer < squaredDoubleScreenRadius)
    {
        tickPriority = TICK_PRIORITY_HIGH;
    }
    else if(squaredDistanceToPlayer < squaredDoubleScreenRadius * 2.0f)
    {
        tickPriority = TICK_PRIORITY_MEDIUM;
    }
    else
    {
        tickPriority = TICK_PRIORITY_LOW;
    }
}

So the point of this function would be to set a tick priority.
Essentially saying, the  further away an ai is from the player, the less frequently it will receive tick calls.

This however can and will create some other problems depending on how you would like things to be and act.

Just to keep it simple, if a spider is really far away and gets the low priority. It would move really slow and wont cover as much land as someone close to the player would.

void World::tick(float deltaTime)
{
    // update all world units
    for(int i=0; i<units.size(); ++i)
    {
        if(units[i]->shouldTick())
        {
            units[i]->resetTickCount();
            units[i]->tick(deltaTime);
            units[i]->updateTickPriority();
        }
    }
}
bool Unit::shouldTick()
{    
    ++tickCount;

    switch(tickPriority)
    {        
        // always tick        
        case TICK_PRIORITY_HIGH:
            return true;
         
        case TICK_PRIORITY_MEDIUM:
            return tickCount > 10;

         case TICK_PRIORITY_LOW:
            return tickCount > 20;
    }
    return false;
}

Just to reiterate, its a very simple optimization.
It has it share of flaws depending on how you want ais who are far off screen to act.

But its easy to implement and its dynamic and will work for all ais who inherit from the same base class.

 

Im sure if you where to dig deeper into entity management and updating world entites you could find all sorts of interesting techniques and methods to handle large amount ais and what not.
But what i have presented here is a simple method to gain some performance (if the performance loss lies with to many units are getting updated that is),

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this