L 8 ECTURE Announcements

advertisement
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!
Related documents
Download