Potential Fields AI, 3/3: real game example.

Categories ai, code

Today I’ll write about my pretty advanced implementation of Pontential Fields AS3 library in upcoming game. This is the last part of Potential Fields AI article. It started with theory and basics in part 1. In part 2 you learned more about PFLib details and we implement RTS fighting bots demo. However, In near future I plan to port the library to few other languages and keep the update status on this blog.

Potential Fields real game example

Units in my game have their own attack behaviors. Zeppelins throw bombs at the target and drones attack with machine gun from right or left. Bots avoid collisions while being idle. They fly with different speed and acceleration and have various shot distances. It’s all running with my Pontential Fields AS3 library and Nape physic. Let me write about details!

Potential Fields & physic engine.

My game is driven by potential fields. However, when you look at units movement, you won’t see any jerking effects suggesting that agents jump from one tile to another on potential map:

Game world is divided into 8px squares. 2048x768px of world size gives me 256×96 tiles for potential maps.

Potential fields, grid size

Each moveable unit has its representation in Nape physic engine. Movement component updates body’s velocity according to potential agent’s next position. Here’s a part of implementation:

override public function update(potential:PotentialsComponent, physic:PhysicComponent, position:PositionComponent, dt:Number):void {
    var body:Body = physic.body;
    var velocity:Vec2 = body.velocity;
    var displayObject:DisplayObject = display.displayObject;

    _aiNextTileLookupTimeLeft -= dt;

    if (_targetTile.x == position.tileX && _targetTile.y == position.tileY) {// target tile reached
        potential.agent.moveToPositionPoint(_targetTile);
        _aiNextTileLookupTimeLeft = -1;// force next targettile lookup
    }

    if (_aiNextTileLookupTimeLeft < 0) {         // consider target's position (if any) in next tile lookup.         var xLookupOrder:int = 1;         var yLookupOrder:int = 1;         if (target) {             if (target.position.tileX > position.tileX) xLookupOrder = -1;
            if (target.position.tileY > position.tileY) yLookupOrder = -1;

            if (!target.military.isAlive) {
                target = null;
            }
        }

        _targetTile = potential.agent.nextPosition(xLookupOrder, yLookupOrder);

        _aiNextTileLookupTimeLeft = AI_TILE_LOOKUP_INTERVAL;
    }

    var targetX:Number = _targetTile.x * Configuration.TILE_SIZE + Configuration.TILE_SIZE * 0.5;
    var targetY:Number = _targetTile.y * Configuration.TILE_SIZE + Configuration.TILE_SIZE * 0.5;

    if (body.position.x < targetX) {         if (velocity.x > 0) {
            velocity.x += _xAcc * dt;
            velocity.x = Math.min(velocity.x, _xMaxVel * maxVelocityScale);
        }
        else {
            velocity.x += _xAcc * _brakesFactor * dt;
        }
    }
    else {
        if (velocity.x < 0) {
            velocity.x -= _xAcc * dt;
            velocity.x = Math.max(velocity.x, -_xMaxVel * maxVelocityScale);
        }
        else {
            velocity.x -= _xAcc * _brakesFactor * dt;
        }
    }

    // ... the same for velocity.y

    // ... the rest of implementation.
}

The physics’ inertia sometimes causes potential agent to move onto “bad” tile. This is however not a mistake! It’s truly a feature, which makes the movement look more natural. This won’t cause any problems in agent’s AI, because it’s completely okay to move agent onto bad potential. He will escape in next iteration.

Unit potentials.

In the next video you see two units from my game. Drone attacks the target from the left. Zeppelin finds the target, stands above and throws bombs. It’s all done with mix of different potential fields.

Both units have its own command maps. Drone’s map consists of 3 following potential fields:

Follow target potential. This radial potential field attract drone to the target. Position of this field is updated with target’s position. It must be big enough to cover whole map:

drone follow position

Keep distance potential. This radial field repels drone from target. In result, drone stands at “shot distance” from the target:

drone keep distance

Attack potential. This horizontal potential field attracts drone to target y position:

drone how to attack

If we add this 3 fields, resultant potential will attract drone, keep him at “shot distance” and align his y position with the target. That’s how it works in my game :). Zeppelin uses vertical potential field for attack potential.

Terrain complexity.

Potential Fields AI works best in large, open areas without complicated terrain. They cannot be used for pathfinding! If you put your agent inside potential’s cave don’t expect it to find the exit :).  However, there’s one solution that could help you if your agent is stuck. Johan Hagelbäck wrote about it in his amazing Using Potential Fields in a Real-time Strategy Game Scenario article under “What about the Local Optima Problem?” section. His solution is reflected in my library implementation. Let’s talk about trail potential.

PFAgent API contains a method:

public function setTrailLength(value:int):void

It’s set to 0 by default. If you set it to a different value, say N, your agent will put small, repelling potentials on his last N positions. He will leave a N-potentials-length trail behind him. This repelling potential will force the agent to change position preventing him from being stuck.

Potential fields balancing.

It’s often difficult to tune potential fields main parameters: potential value and gradation. This process could be irritating, especially if you’re new to this subject. On my way of creating Potential Fields AS3 library and balancing it for my upcoming game I’ve created Potential Fields Editor application. It helps you to test various scenarios with potential fields. With PFEditor you can:

  • create map of any size;
  • add different types of potential fields from Potential Fields AS3 library;
  • tune fields parameters;
  • add agents to your map;
  • play agents simulation step by step or with custom time delta;
  • mark targets and objects with simple “markers”;

Editor sources are part of my GitHub repository.

Potential fields editor

It took me some time to choose right fields values for my game. It was a process of trails and errors, that was essential to gain an important intuition. However, there are people who try different approaches. During my study of potential fields subject I found an Evolutionary Multi-Agent Potential Field based AI approach for SSC scenarios in RTS games (Master Thesis, 2011)” by Thomas Willer Sandberg. Thomas used evolutionary algorithm for tuning fields values in RTS bots implementation. He did really interesting work with nice results that really deserve your attention if you’re going to go deeply into potential fields.

This is the end of my 3-part article about potential fields implementation. I really hope you enjoyed it. If so, don’t forget to leave a comment :)!

As a bonus, here’s a concept art from my upcoming game:

hero concept

Visit my blog from time to time to get updated about it’s progress and to read about more interesting stuffs! :)

3 Comments

  • John
    14/05/2014

    Maciek,

    Brilliant set of articles, loved it all. I’ve long been a fan of ‘boids/bots’ and have just started coding on ObjC after MANY years out of the game. Having got my boids up and running I wanted more movement options and apparent intelligence, eventually I stumbled across this.. Trouble is I’m trying to run before I’m even comfy walking :)

    Thanks for taking the time to put all your thoughts down for everyone to see, not to mention explaining it in understandable terms.

  • Maciek
    24/05/2014

    Thank you John! It’s cool to see feedback on my work :).

  • Summoner
    24/02/2015

    Thanks for your work.
    I work on A-star implementation and quite interested on this approach.
    I have several questions and need some help.

    1. It requires one static and one dynamic map for each bot. So it requires N + 1 maps for N bots which seems a bottleneck on memory usage.
    2. The potential fields are calculated with approximation, not flood-fill. How to deal with maps with rooms/target inside concave shape?
    3. Bot with turning radius. Say for vehicles such as tanks/flight

Comments are closed.