Potential Fields AI, 1/3: hello potentials.
Potential fields are an interesting concept in games development. I decided to use them in my upcoming title, so I created a Potential Fields AS3 library (PFLib). This library drives bots AI in my RTS engine. The game is still in development, but here’s a video showing my current results (this gonna be pretty complex defense game with bots attacking your base and fighting against your team):
In this 3-part article I’ll tell you what are potential fields and how you can use my library to create AI in your games. In this first part I’ll start with some theory and basic AI implementation. In the second part we’ll implement RTS bot and you’ll learn more about PFLib APIs. In the third part I’ll reveal secrets of AI in RTS engine from my upcoming game. Stay tuned!
First of all, there is an amazing article about Potential Fields (PFs) written by Johan Hagelbäck: Using Potential Fields in a Real-time Strategy Game Scenario. If you’ve no idea what are potential fields or how to use them you could start with this. My following theoretical intruduction to PFs is not as comprehensive as Johan’s, but should be sufficient for beginners. In the rest of this article (and serie) I focused on the implementation.
First things first, to talk about potential fields AI we need to define three terms:
1. Potential map. It’s simply a matrix. Each cell contains an integer value. We fill this matrix by putting some positive or negative value in cell (x, y) and then fading it to zero around this point. In fact, potential map doesn’t have to be implemented as a matrix. I’ll write about it later.
2. Potential field. Potential field consist of: center point, potential value and gradation size. Gradation says how succeeding cell’s value differs from antecedent. On the previous image you see two potential fields. First with center (4, 4) and potential value 10, second with center (10, 10) and value -10. Both have gradation of 2. Each cell of map stores sum of fields influences. Next image shows how two fields overlap resulting with zero-potential area between them.
Look at pretty complex map with more potential fields added.
3. Agent. Agent has a position. Agent moves between adjacent cells on potential map. He always moves in the direction of biggest potential chosing from 8 surrounding cells or keeps its position if it’s currently the best one. Look at the following agent. He stands on cell with 10 potential value (it has the same color as its top and left neighbour). He checks surrounding potentials and choses cell with “16” because it’s the best one. In result he does bottom-right move.
Agent also can be a field! If we say so, things start to be more interesting :). Imagine that we put more agents on the map. Each agent emits repel potential which can be felt by other agents. They start to avoid each other! If you cannot see that, there’s an interactive example:
[kml_flashembed publishmethod=”dynamic” fversion=”11.1.0″ useexpressinstall=”true” movie=”http://www.n-created.com/wp-content/uploads/2013/08/BasicBehaviors_release.swf” width=”400″ height=”300″ targetclass=”flashmovie”]
Move mouse cursor on the map to change interactive potential position. Click to switch from “repel” to “attract” potential type. Red, static areas are obstacles. Source code for above example can be found here: Basic Behaviors demo. I suggest you to look at this code later, now we’re going to start with some basics.
That’s all for the theoretical part. Let’s move to implementation! However, if you feel this is not sufficient for you at this point or have some troubles with understanding, then I strongly recommend you reading Johan Hagelbäck’s article Using Potential Fields in a Real-time Strategy Game Scenario. Then come back here and take a look at my practical use of his ideas.
Potential Fields AS3 library – basic usage.
Those three kinds of objects: potential map, potential field, agents build up my Potential Fields AS3 library. Whole library consists of 8 classes shown on the following diagram:
Let’s look closer into PFPotentialField inherited classes: PFRadialPotentialField, PFHorizontalPotentialField, PFVerticalPotentialField, PFRectangularPotentialField. They define potential fields of various shapes:
We will focus on PFRadialPotentialField because it’s most intuitive one. To create radial potential you need to define it’s position, type (attract or repel), potential value and gradation size. Simple:
var field:PFRadialPotentialField = new PFRadialPotentialField(); field.type = PFPotentialField.PF_TYPE_ATTRACT; field.potential = 250; field.gradation = 5; field.position.setTo(50, 38);
This field has PFPotentialField.PF_TYPE_ATTRACT type – it will attract agents on the map. Let’s create an obstacle that will repel them.
var obstacle:PFRectangularPotentialField = new PFRectangularPotentialField(2, 2); obstacle.type = PFPotentialField.PF_TYPE_REPEL; obstacle.position.setTo(40, 45); obstacle.potential = 250; obstacle.gradation = 100;
Ok, we’ve created our fields. Now we need a potential map.
var map:PFDynamicPotentialsMap = new PFDynamicPotentialsMap(100, 75);
Then add our fields to this map:
Great! But we don’t see anything… Potential maps have small set of functions for drawing debug view. If you are familiar with libraries like Box2D or Nape you probably know what debug view is. With my PFLibrary you can setup bitmap object to draw map’s potentials.
var debugBitmap:Bitmap = new Bitmap(new BitmapData(100, 75));// equals map size debugBitmap.scaleX = stage.stageWidth / map.tilesWidth;// scale it to the size of stage debugBitmap.scaleY = stage.stageHeight/ map.tilesHeigh;// scale it to the size of stage addChild(debugBitmap);
Rendering debug view is easy:
debugBitmap.bitmapData.fillRect(debugBitmap.bitmapData.rect, 0xFFFFFF);// clear map.debugDrawPotentials(debugBitmap.bitmapData);// draw
Here’s the result:
[kml_flashembed publishmethod=”dynamic” fversion=”11.1.0″ movie=”http://www.n-created.com/wp-content/uploads/2013/08/HelloPotentials_release.swf” width=”400″ height=”300″ targetclass=”flashmovie”]
You can see our green, attracting field and red, repelling obstacle.
Now it’s time for our first agent! Let’s create one:
var agent:PFAgent = new PFAgent(); agent.position.setTo(80, 50);
and register a potential map for it:
In the theoretical part I said that agent moves between adjacent cells on potential map. It searches surrounding cells and choses the one with best potential. This can be done with following call:
Above line of code is a heart of your AI. It decides where to move agent. You’ll usually call it somewhere in your game loop.
Finally, we need to display the agent. As we have debug bitmap already setup, we can use it:
debugBitmap.bitmapData.fillRect(debugBitmap.bitmapData.rect, 0xFFFFFF);// clear map.debugDrawPotentials(debugBitmap.bitmapData);// draw debugBitmap.bitmapData.setPixel32(agent.position.x, agent.position.y, 0xFF0000FF);// mark agent's position with blue dot
And our basic example is ready:
[kml_flashembed publishmethod=”dynamic” fversion=”11.1.0″ movie=”http://www.n-created.com/wp-content/uploads/2013/08/HelloPotentials_release1.swf” width=”400″ height=”300″ targetclass=”flashmovie”]
Click on the stage to reset agent’s position. Our agent is alive! Wherever you click it will follow best potential path to the center of green field. Notice that if you click outside of the green field the agent doesn’t move. It’s because all surrounding potentials equal 0 and while he is standing on 0 potential cell there’s no better choice. Full source code for this example is available as Hello Potentials demo.
This was part 1 of my 3-part article about potential fields. If you liked it, read the next part: Potential Fields AI, 2/3: RTS bot implementation.