{"assets":[],"author_link":"author\/euske\/","author_name":"euske","cat":"LD #28","categories":["LD #28"],"comments":[],"epoch":1385271180,"event":"LD28","likes":39,"metadata":{"p_key":"79406","p_author":"euske","p_authorkey":"0","p_urlkey":"288416","p_title":"Path Planning (AI) in Platformer Games: A Layered Approach","p_cat":"LD #28","p_event":"LD28","p_time":"1385271180","p_likes":"39","p_comments":"0","p_status":"WAYBACK","us_key":null,"us_name":null,"us_username":null,"event_start":"1386892800","event_key":"20","event_name":"LD28"},"source_url":"2013\/11\/24\/path-planning-ai-in-platformer-games-a-layered-approach\/","text":"<p>\nI\u2019ve been working on path planning (path finding) for platformer games<br\/>\nfor a while. My original intention was to create a reusable framework<br\/>\nfor scripting characters in a platformer, but it can have other<br\/>\ninteresting applications. I have a working prototype and its source<br\/>\ncode.<\/p>\n<ul>\n<li> <a href=\"http:\/\/ludumdare.tabesugi.net\/ppp\/\">Playable prototype<\/a>\n<li> <a href=\"https:\/\/github.com\/euske\/planpathplat\/\">GitHub<\/a>\n<\/li><\/li><\/ul>\n<p>\n<img src=\"http:\/\/tabesugi.net\/memo\/2013\/img\/ppp\/movie.gif\"\/><\/p>\n<h2>Handling Continuous Space<\/h2>\n<p>\nThe first problem that we need to address is the continuous nature of<br\/>\na platformer game. In a typical path planning, a problem is usually<br\/>\nreprespented as a discrete space. However, in a platformer game where<br\/>\ncharacters often are affected by gravity, handling its continuous<br\/>\nmovement is crucial. We addressed this issue by dividing the problem<br\/>\ninto two layers: macro-level planning and micro-level planning. The<br\/>\nmacro-level planning takes care of a coarse-grained plan such as \u201cgo<br\/>\nto this block\u201d or \u201cjump here and try to land at that block\u201d. The<br\/>\nmicro-level planning takes care of more fine-tuned movement that<br\/>\ninvolves a continuous 2-D space. The macro-level planning is done by<br\/>\nusing typical AI techniques like a navigation mesh, constructing a set<br\/>\nof possible actions at each discrete location. The micro-level<br\/>\nplanning is done in a more greedy manner, by taking care of physical<br\/>\ncollisions and character coordinates.<\/p>\n<p>\nThe macro-level planning can be implemented with a pretty<br\/>\nstraightforward Dijkstra or A* search. It starts from the goal<br\/>\nposition, and iteratively searches all the possible movements until it<br\/>\nreaches the initial state (the position of the character). Each<br\/>\naction is associated to its next action until it reaches the goal,<br\/>\nforming a DAG structure. A plan graph can be dynamically constructed<br\/>\nand periodically updated based on the current state of the game.<br\/>\nAdditional restrictions (e.g. space constraints and ladders) are also<br\/>\nconsidered.<\/p>\n<p>\n<img src=\"http:\/\/tabesugi.net\/memo\/2013\/img\/ppp\/plangraph.png\"\/><\/p>\n<p>\nWhen each action is executed, a character is entirely controlled by<br\/>\nthat action, and the character cannot execute the next action until<br\/>\nthe current action is finished. Each action only considers the<br\/>\nstarting and ending point, and it moves the character in a rather<br\/>\nstraightforward way. When the macro-level planner detects the<br\/>\nsituation change (e.g. the goal is moved) during the execution, the<br\/>\ncurrent plan is abandaned after the action is finished and a new plan<br\/>\ngraph is created.<\/p>\n<h2>Jumping \/ Falling<\/h2>\n<p>\nJumping and falling is the key element of platformer games and the<br\/>\ncore of its planning problem. It has so many parameters that using<br\/>\nnaive methods will easily lead to computational explosion. In the<br\/>\nproposed method, the macro-level planner only takes care of its<br\/>\nstarting point and ending point in the block coordinates. The planner<br\/>\nhas to know in advance the speed of a character in question and its<br\/>\njump impulse, as well as the gravity acceleration, so that it can know<br\/>\nwhere it will exactly land. The macro-level planner uses two bounding<br\/>\nboxes (a jumping part and a falling part) to approximate the clearance<br\/>\nthat a character needs, but it doesn\u2019t take care of an actual<br\/>\ntrajectory of the character.<\/p>\n<p>\nAn actual jump action is divided into three steps. First, it tries to<br\/>\nmove the character to its starting position (Set), maneuver the<br\/>\ncharacter movement during ascending (Ascend), and then land the<br\/>\ncharacter to the ending position (Land). It takes a precise<br\/>\n(pixel-wise) control of the character at each frame update.<\/p>\n<p>\n<img src=\"http:\/\/tabesugi.net\/memo\/2013\/img\/ppp\/jumping.png\"\/><\/p>\n<h2>Landing Prediction<\/h2>\n<p>\nThe planner can find a solution only when the path planning is<br\/>\nsolvable. When a character is following another character that is<br\/>\njumping or falling, the goal position is often considered unreachable,<br\/>\nmaking it impossible to find a solution. In a case like this, the<br\/>\nplanner can optionally make a guess of the final landing position<br\/>\nbased on the current speed of the character.<\/p>\n<p>\n<img src=\"http:\/\/tabesugi.net\/memo\/2013\/img\/ppp\/prediction.png\"\/><\/p>\n<h2>Moving Platforms, Sprinting, Double Jumping, etc.<\/h2>\n<p>\nA path finding is based on an assumption that the floor map<br\/>\nyou\u2019re planning on is static. When there are some parts that are<br\/>\nchanging dynamically, those parts need to be handled specially by<br\/>\nthe micro-level planner. In this case, the macro-level planner only<br\/>\ntakes care of moving into that part and getting out of it.<\/p>\n<p>\n<img src=\"http:\/\/tabesugi.net\/memo\/2013\/img\/ppp\/movingplatform.png\"\/><\/p>\n<p>\nHandling moves like sprinting or double jumping can be more tricky,<br\/>\nbecause such actions depend on the current state of a character. In<br\/>\nother words, a plan becomes context-dependent. Now we would have<br\/>\nto consider multiple possible states at each grid, making the search<br\/>\nspace several times larger. I am not sure if we can find a really good<br\/>\nsolution for this kind of planning, but in theory we can still take<br\/>\nthis into account in the current framework.<\/p>\n<h2>Source Code Structure<\/h2>\n<p>\nThe prototype is written in ActionScript. However, the planning parts<br\/>\nare separated from the UI and I believe it\u2019s relatively easy to port them to<br\/>\nanother language. Here are a couple of important classes:<\/p>\n<dl>\n<dt> <code>Actor<\/code>\n<dd> This is an object that the planner takes control of.<br\/>\n  The object needs to support methods like <code>move(x, y)<\/code>, <code>jump()<\/code><br\/>\n  to perform actions on each character, as well as query methods such as<br\/>\n<code>bounds()<\/code>.\n<dt> <code>TileMap<\/code>\n<dd> An object that stores a 2D map that a planner can use.<br\/>\n  This is basically a two dimensional array of tile numbers.<br\/>\n  The class needs to handle a query like <code>getTile(x, y)<\/code> as well as<br\/>\n  range queries like \u201cif there\u2019s a certain block within this area\u201d in order to<br\/>\n  serve the efficient planning.\n<dt> <code>PlanMap<\/code>\n<dd> A macro-level planner that has a plan graph and its constructor.<br\/>\n  It maintains a grid of actions, which correspond to each tile in<br\/>\n  a <code>TileMap<\/code> object, so that an <code>Actor<\/code> can query<br\/>\n  a possible action to take at each place. An action,<br\/>\n  which is a <code>PlanAction<\/code> object, is represented as<br\/>\n  an edge of a plan graph. There\u2019s one important method <code>addPlan()<\/code>,<br\/>\n  which fills the grid according to the given parameters and the associated<br\/>\n<code>TileMap<\/code>.<br\/>\n  A plan graph can be shared and reused as long as the goal is not changed<br\/>\n  and all the <code>Actor<\/code>s has the same property (e.g. speed, gravity, etc.).\n<dt> <code>PlanAction<\/code>\n<dd> This object represents a single action planned by the macro-level planner.<br\/>\n  An action object has a detailed information about the action such as a<br\/>\n  starting and ending point, the type of the action, and the tile map it\u2019s referring to.<br\/>\n  The object is responsible for performing micro-level planning.<br\/>\n  It fires events to an Actor object so that it can carry out<br\/>\n  the corresponding actions (jumping or moving, etc).\n<\/dd><\/dt><\/dd><\/dt><\/dd><\/dt><\/dd><\/dt><\/dl>\n<h2>Is This Smarter than Humans?<\/h2>\n<p>\nUnfortunately, no. Due to computational limits, the current<br\/>\nimplementation considers only a handful of ways of possible jumps at<br\/>\neach position. Since it uses a rectangle as an approximation of jump<br\/>\ntrajectory, it cannot emulate a complex manuever that humans could<br\/>\ndo. Also, since it\u2019s on block-by-block basis, it cannot consider a<br\/>\npossibility of barely-make-it kinds of jumps. These are mostly<br\/>\ncomplexity problems, but in some cases there might be need to give up<br\/>\nto find an optimal solution.<\/p>","time":"November 24th, 2013 5:33 am","title":"Path Planning (AI) in Platformer Games: A Layered Approach","title_was_empty":false}