LECTURE 8 Announcements Platformer2 Feedback • Week 2 of 4 is done! • Collision debugger code should be completely gone – You should be working in a different project – May still be helpful for hacks (including ramp hack) • Feel free to use other levels – Find obj models online – Procedurally generate your own like minecraft! Falling through the floor? • Might be acute angles – Lots on island/hard, don’t worry about it • Might be too few sweep iteration – Try bumping up to 4 or 5, see if it helps • Might be forgetting to bump by the plane normal – Check and make sure • Might be getting the wrong plane for the edge/vertex case – Test your normals in the collision debugger Announcements QUESTIONS? LECTURE 8 Pathfinding (Geometric Engine) Pathfinding (Geometric Engine) MOTIVATION Basic Definition • At the simplest level, pathfinding tells how to get from A to B – Never as easy as it sounds • Result is a “path” – A list of instructions for getting from A to B – Not necessarily a list of locations! • Jump, climb ladder, teleport, etc… • But this is so important… What’s the big deal? • Pathfinding is *the most basic* AI construct in our 3D games – Our games have collisions – Collisions require movement – (Non-player) entities should be able to move – All entities restricted by same environment • Essential to an immersive experience! 3D world representation • Arbitrary world means we need an efficient encoding of relevant environment features – Navigable space (where entities can move to) – Obstacles (objects entities must navigate around) – Important locations (health, safety, bases, objectives) • Two different approaches: – Field-based approaches (potential fields) – Graph-based approaches (waypoints, navigation meshes) Pathfinding (Geometric Engine) - Motivation QUESTIONS? Pathfinding (Geometric Engine) FIELD-BASED APPROACHES Pathfinding with potential fields • Potential field: a region of potential values – Usually a 2D grid in games – Good paths can be found by hill climbing • The potential at a location represents how desirable it is for the entity to be there – Obstacles have low potential – Desirable places have high potential Basic algorithm details • On startup – Generate potential fields for static objects • Periodically (~5 times a second) – Generate potential fields for dynamic objects – Sum static and dynamic potential fields to get the final potential field • Each tick (~60 times a second) – Pathfinding entities move towards direction of greatest potential increase (hill climbing) Choosing a potential function • Potential function applies to a single entity – Usually defined radially – Non-radially symmetric ones useful too • Example: cone of negative values ahead of player to encourage enemies to stay out of view • Example cross section: Choosing a potential function • Potential functions don't need linear falloff Linear falloff leads to a target Rise then fall leads ranged units to a safe distance away Multiple potential fields • Multiple ways of combining potentials – Maximum sometimes works better than sum Summing creates false desirable spot for ranged units Maximum correctly identifies desirable areas for ranged units Pros and cons • Advantages – – – – Able to represent fully dynamic world Hill climbing doesn't need to generate and store the entire path Naturally handles moving obstacles (crowds) Can be efficiently implemented on the GPU • Drawbacks – Tuning parameters can be tricky – Hill climbing can get stuck in local maxima Local maximum Agent Global maximum Avoiding local maxima • Agents drop negative-potential "pheromone trail" – Bulge behind them pushes them forward – Doesn't prevent agents from turning around in corners • Still doesn't avoid all local maxima – Potential fields are better suited for dynamic worlds with large open areas – Classical graph-based pathfinding works better for complex terrain with lots of concave areas The more you know! • Not actually used in many real games – Couldn’t find any major commercial releases that use them – But there are at least custom Starcraft bots that do • Much harder to fine tune than graph-based approaches • Most games use graph-based – Yours will too! Pathfinding (Geometric Engine) – Potential Fields QUESTIONS? Pathfinding (Geometric Engine) GRAPH-BASED APPROACHES The basics • Represent the world as a graph – Nodes represent open space – Edges represent ways to travel between nodes – Use graph search to find paths • Two common types – Waypoint graphs – Navigation meshes Waypoint Graphs • Each node is a “waypoint” • Edges between adjacent nodes • Paths travel through a series of waypoints • Fixed number of paths in the world Wait a second… • Entities only ever stop at a node • Optimal paths likely not on the graph – Zig-zag paths depending on density of the graph – Higher density graph = more memory, search time • No model of space between nodes – Dynamic objects require recomputing the graph • Hard to handle entities with different radii – Turn off edges and add more waypoints? • Works fine for well structured worlds – Tac in CS1971, or any other grid based game – Minecraft (maybe – radii can still cause issues) Navigation Meshes • Convex polygons represent navigable space – Triangles! YAY! • Nodes are polygon • Edges are polygons that share a side – Forms a mesh over navigable space Sounds great! • More efficient and compact representation – Equivalent waypoint graph would have many more nodes and would take longer to traverse • Models entire navigable space – Can plan path from anywhere inside navigation mesh – Paths can be planned around dynamic obstacles – No zig-zags required! • Naturally handles entities of different radii – Don't go through edges less than 2 * radius long – Leave at least a distance of radius when moving around navigation mesh vertices – More on this later Navigation meshes • Different than, but related to, the collision mesh! – Only contains walkable faces – Stairs become a single, rectangular polygon • Might clip the collision geometry – Polygons are usually smaller to account for player radius The possibilities… • Annotate special regions for special navigation commands – Regions for jumping across, falling down, crouching behind, climbing up, ... – Can be computed automatically based on enemy parameters • Ex. Slow enemies can’t jump the gap but fast enemies will The navigation loop • Find sequence of polygons (corridor) using graph algorithm • Find corner using string pulling (funnel algorithm) • Steer using smoothing – Accelerate in a direction, don’t just instantly change velocity – Avoids jerky changes in direction • Actually move/collide entity Graph search • First step in our pathfinding process • Graph search problem statement – Given starting point A, target point B, and a navigation mesh – Generate a list of navigation mesh nodes from A to B (called a corridor) • Simplest approach: Breadth-first search – Keep searching until target point is reached – Each edge has equal weight • Most common approach: A-star – Variable edge weights (mud or steep surfaces may have higher cost) – Uses a heuristic to arrive at an answer faster • Keep it simple – use BFS to start Path generation: statement • Given a list of polygons (output of a graph search) – The light polygons are our “corridor” • Construct the shortest path for the agent – Where a path is a sequence of connected segments – The path must lie entirely in the list of polygons Path generation: first try • Can we just connect polygon centers? • No: the path might not even be within the polygons • Polygons’ convexity only tells us that any 2 points in a single polygon can be connected with a segment Path generation: second try • Can we just connect centers of polygon sides? • This always produces a valid path (within the polygons) • But not always the optimal path (zig-zagging) • This is just a waypoint graph! • What next…? Pathfinding (Geometric Engine) – Graph Approaches QUESTIONS LECTURE 8 Funnel Algorithm (Geometric Engine) Funnel Algorithm (Geometric Engine) SIMPLE STUPID FUNNELING Path generation: third try • The funnel algorithm finds the optimal path – More on this in a bit • Hugs corners correctly • Is like “pulling a string” that connects A and B until it is taut – Harder than it sounds The funnel algorithm • Traverses through a list of polygons connected by shared edges (portals) • Keep track of the leftmost and rightmost sides of the "funnel" along the way • Alternates updating the left and right sides, making the funnel narrower and narrower • Add a new point to the path when they cross Funnel Funnel Algorithm Funnel Algorithm • Start – Apex point = start of path – Left and right points = left and right vertices of first portal • Step – Advance to the next portal P – Try to move left point to left vertex of P • If inside the funnel, narrow the funnel (C-D in previous slide) • If past the right side of the funnel, turn a corner (E-G in previous slide) – Add right point to path – Set apex point to right point – Restart at portal where right point came from – Try to move right point to right vertex of P (analogous) Edge cases • Zero-length funnel side (portals that share a vertex) – Always use left*0.99+right*0.01 for the left and left*0.01+right*0.99 for the right (shrinks portal slightly) • End iteration of the funnel algorithm – End point is portal of size 0, need to check for potential restart like other portals Funnel algorithm example Don't restart search from any of these points Restart search from here (apex) Math: Funnel Crossing • Concept: consider the sides of the funnel as 2D vectors and use the direction of their cross product to determine if they are crossed • leftVec = (left-apex).xz • rightVec = (right-apex).xz • Funnel is crossed if: • 𝑟𝑖𝑔ℎ𝑡 × left . y < 0 left right apex Math: Funnel Width • One way to measure the “width” of the funnel is to take the angle between the sides • Find leftVec and rightVec as before (only the horizontal components!) • Angle is: • 𝜃= left ∙ right acos( ) ||left|| ∗ ||right|| • Remember to check for division by zero! – Will occur if left or right are zerolength vectors left right 𝜃 apex Funnel Algorithm (Geometric Engine) QUESTIONS? More Pathfinding (Geometric Engine) STEERING AND OBSTACLE AVOIDANCE Steering • Many different ways for an entity to move towards a point • Moving in straight lines towards each destination gives a robotic look • Many alternatives exist: which to use depends on the desired behavior – Seek, arrive, wander, pursue, etc. • Steering behaviors may be influenced by a group – Queue, flock, etc. • Example: arrival should slow down, so apply a deceleration Moving and Colliding • If there are no collisions, moving and colliding is as simple as using the destination and steering to move • Collisions can cause a variety of issues – May need to re-plan path if a collision is impeding movement – Can detect getting stuck if the entity stays in roughly the same spot for a few seconds Obstacle avoidance • Static obstacles can be avoided by generating the right navigation mesh • Dynamic obstacles are trickier • Baseline approach for dynamic obstacles – Use raycast or sweep test to determine if in obstacle is in the way – Apply steering force away from obstacle – Adjust force based on distance to obstacle Dynamic obstacle avoidance • If we consider each obstacle individually, this is purely local avoidance – Can easily get stuck in local minima – Remember, this step is added on top of global path planning • We need an approach between purely local and global for handling temporary obstacles – Will not perfectly handle all cases – Only perfect solution is to adjust navigation mesh – Example approach: "Very Temporary Obstacle Avoidance" by Mikko Mononen Very Temporary Obstacle Avoidance • For the obstacle blocking the path – Calculate tangent points – Choose tangent that generates a shorter path from the start position to the goal through the tangent • Cluster overlapping objects into one object Very Temporary Obstacle Avoidance • Handling multiple obstacles – Check for obstacles on newly chosen path – Iterate until path is clear • Might take many iterations to converge • Only run 2-4 iterations, usually good enough Very Temporary Obstacle Avoidance • Handling objects along walls – Check for intersections along navigation mesh boundary – If one is hit, exclude that path Very Temporary Obstacle Avoidance Robustness • Can't find path from off the navigation mesh – Clamp agents inside boundary of navigation mesh – Special-case climbing up ledges • Crowds can't all follow the same path – – – – Don't precompute the path, assume it's wrong Use a more loose structure of path (polygons) Just navigate to the next corner Use local object avoidance to handle crowds Case study: Recast and Detour • Open source middleware – Recast: navigation mesh construction – Detour: movement over a navigation mesh – Developed by Mikko Mononen (lead AI on Crysis) • Widely used in AAA games – Killzone 3 – Bulletstorm – Halo Reach Case study: Recast and Detour • Recast: navigation mesh generation – Start with arbitrary mesh • Divide world into tiles • Voxelize a tile at a time • Extract layered heightfield from voxels – Extract walkable polygons from heightfield • Must have minimum clearance • Merge small bumps in terrain and steps on stairs together • Shrink polygons away from edge to account for radius of agent • Detour: navigation mesh pathfinding Case study: Recast and Detour References • Recast and Detour – http://code.google.com/p/recastnavigation/ • Funnel algorithm – http://digestingduck.blogspot.com/2010/03/simple-stupidfunnel-algorithm.html • Obstacle avoidance – http://digestingduck.blogspot.com/2011/02/very-temporaryobstacle-avoidance.html • Potential fields – http://aigamedev.com/open/tutorials/potential-fields/ LECTURE 8 Final Project Overview Overview • Can be any 3D game • You should work in groups! • Each person is responsible for 5 “points” worth of new engine features – – – – More members in a group means more engine features More details in the final project handout Capstone is 7 “points” Must do at least one 3 “point” feature each Timeline • 4 main parts: – Week 1: Idea – Week 2: Form groups and get approved – Week 3: Design – Weeks 4-8: Code, playtest, polish, present Week 1: Idea • A 1 page document • Describe basic gameplay idea – How is your game fun? – Why should someone want to help make it? • Describe engine feature(s) you plan on implementing • Give a 90-second “elevator pitch” of your game in class • Everyone must give a pitch, even if you already know your group and which project you’re working on Week 2: Groups • Form a group (or decide to work alone) • Finalize game and engine features • Each group must meet with a TA to present the following: – A more polished idea of the game – Breakdown of member responsibilities for engine Week 3: Design • • • • • Research new engine features Design the engine and game Exact breakdown of member responsibilities Choose someone’s engine to use or integrate engines For groups of 3 or more – Explain how you will use version control Weeks 4-8 • Week 4: – Engine should be mostly done – Game exists • Week 5: – Engine should be done – Game is playable – 5 playtests per member from people not in CS1972 Weeks 4-8 • Week 6: – Game should be mostly done – 5 more playtests per member from outsiders • Week 7: – Game should be done – 5 playtests per member – Powerpoint slideshow for postmortem presentation Weeks 4-8 • Week 8: – Polish up your game, bug fixes, etc – Create an executable and put it in /contrib – Make a video demo of your game from gameplay footage • It is now May 9th • And then you’re done! Final Project Overview QUESTIONS? LECTURE 8 Tips for Platformer 3 The Big Picture Your pathfinding process will necessarily include each of the following steps: 1. 2. 3. 4. 5. 6. Read the nav mesh from an OBJ file Find the triangles that the entity and target reside in Perform a shortest path search on the set of triangles Create a list of portals for string pulling Do string pulling to create a path Move the entity (next week) 1. Reading the nav mesh • Create a VAO/VBO for it so you can draw it later (visualization is required!) • Generate a data structure that allows neighbor lookup for particular triangles – Simplest: map of edges to list of triangles that share that edge – A graph data structure works too – You’ll need this for your shortest path search 1. Reading the nav mesh • To get a map of edges to lists of triangles that share that edge: – Edges are min/max vertex indices for the OBJ – Assume at most 2 triangles share an edge • Use this to construct your graph • This is just a suggestion – feel free to represent the graph however you want! QHash<QPair<int, int>, QList<Triangle *> > edges; for Triangle t: for Edge e in t: QPair<int, int> key = [min(e.v0,e.v1),max(e.v0,e.v1)]; edges[key] += t; 2. Triangle locations of entities • Simply raycast downward onto the nav mesh from the center of the entity • Use your triangle raycast code from earlier weeks! • Remember to raycast on the nav mesh, not the world 3. Shortest path search • Input: – Graph of triangles – Start triangle – End triangle • Output: – Ordered list of adjacent triangles from start to end • Breadth-first search is the easiest option • Dijkstra or A* works too! 4. Creating a list of portals • Iterate through the list of triangles from your search and determine which edges are shared – Of the six vertices between the two triangles, the two that are shared are the portal • Simplest portal representation: a pair of vec3’s – Make sure they are consistently ordered so you know which one is left and which one is right! 5. String pulling 8 variables to keep track of: vec3: • apex • right • left • potentialLeft • potentialRight int: • currentIndex • leftIndex • rightIndex 5. String pulling Pseudocode: add endpoint to portals as <end, end> for currentIndex in portals.length: // these are shrunk slightly get potentialLeft, potentialRight from portal @ currentIndex // try left if funnelCrossed(apex, potentialLeft, right): add right to path apex = right currentIndex, leftIndex = rightIndex left, right = apex else if potential funnel smaller than current funnel OR left == apex: [you figure this out!] // try right ... add endpoint to path return path 5. String pulling • Helper methods will make your code much cleaner! – Suggested names: funnelCrossed, getAngle, getLeft, getRight • Remember to pretend that all of the portals are 2D in the horizontal plane – Collision response will handle vertical movement • The debugger is your friend! – If you’re having trouble, try stepping through very simple examples General Advice • You’re creating a huge pipeline this week • Make sure each individual part is functioning correctly before moving on to the next one – If you took cs32, think unit testing! • Keeping everything modular and encapsulated will go a long way towards reducing debugging headaches Looking Ahead • In your handin: – Navigation mesh is visualized – Path is visualized from player to a target position – Target position can be set to a position by raycasting the environment OR the current target • In week 4, you will create at least one enemy that uses pathfinding – So starting thinking about that, too... LECTURE 8 C++ Tip of the Week C++ Tip of the Week LAMBDAS Lambdas • Inline functors: int i = ...; double terrainHeight = ...; auto adjustEntityHeight = [terrainHeight, i](Vector3 &pos) { pos.y = terrainHeight*complicatedMath(); }; ... // code that would use this math multiple times // template auto functionName = [capturedVariables](input arguments) { ... }; Lambda syntax auto functionName = [capturedVariables](input arguments) { ... }; • auto tells the compiler to determine the type – in this case, it’s a generated lambda definition – auto only works like this starting in C++11 • Before that it declared a variable as ‘local’ storage, and now is almost never used • Captured variables give the function access to variables in the surrounding scope More lambda syntax • To capture member variables, pass this as an argument auto func = [this](int intput) { ... }; • You can optionally specify the return type for clarity auto functionName = [capturedVariables](input arguments) -> returnType { ... }; Even more lambda syntax auto func = [&localOne, localTwo](int intput) { ... }; • localOne is passed by reference, whereas localTwo is passed as a copy (by default) • Use ‘=‘ or ‘&’ to capture all local variables in scope auto func = [&](int intput) { ... }; // captures all variables by copy auto func = [=, &localTwo](int intput) { ... }; // captures all variables // by reference, except localTwo which is captured by copy Using Lambda anonymously • As you probably guessed from the name lambda, you can also use them completely anonymously: std::vector<int> some_list{ 1, 2, 3, 4, 5 }; int total = 0; std::for_each(begin(some_list), end(some_list), [&total](int x) { total += x; }); Lambda based algorithms • The standard library contains a bunch of helpful functions that make use of lamdas and lists • Besides for_each, some examples are transform, count_if, remove_if, and binary_search • Read more about lambdas: https://stackoverflow.com/questions/7627098/whatis-a-lambda-expression-in-c11 C++ Tip of the Week QUESTIONS? LECTURE 8 Elevator Pitches! Presentation Order 1. 2. 3. 4. 5. 6. 7. ajlin zquan mc184 urangane cmgianca zloery mjfuller 8. 9. 10. 11. 12. 13. lpriebe bsheeran jlkao nschank battal pkirschn PLATFORMER 2 PLAYTESTING! To the Sunlab!