To make a simple simulation of people walking through city streets for use in video games
Moving NPC's help make the game feel alive and allows players to feel more invested in the game they are playing
When they move and care like real people, players are able to believe that they are real and want to help with their problems, or engage with them
This simulation has to run fast and be low profile to leave as much processing time as possible for gameplay and other functions
The simulation also has to run smoothly, despite frame rates, so that the NPC's aren't seen to be jumpy or janky
Different games have different requirements for how NPCs should act
Some games want each part of the city street to feel truly alive, and have groups walking together, people stopping to hug each other and rubber neck to look at crashes
While some other games only need people who walk from destination to destination, and need to interact with their world a lot less
Realism in games doesn’t have to be perfect, many small details can be overlooked and exaggerated because of the human minds propensity for suspension of disbelief
However, because this simulation is meant to help the world feel alive, it can't be too far off from actual humans, otherwise it might break this suspension of disbelief that we are trying to build up
When done correctly, flocking can be extremely quick as it requires very few processor heavy checks and statements
It is also possible to use spatial partitioning to cut down on the number of checks being done, making flocking even faster
Flocking is easy to add “rules” and movement possibilities. Each rule would be something that humans actually unconsciously follow, and would change the behavior of the NPC's
It is possible to add rules to “rubber neck” or “hug” other NPCs they know, or a game can use only the basic rules for less interactive NPC's
With additions of rules, you can simulate different behaviors, each add to the realism of the NPCs walking around
There are also many small adjustments that are possible to make for already existing rules, that can exaggerate or tone down certain aspects of the movements for additional realism in the scene
Rules are the functions that take in the environments around each NPC (henceforth known as boid), and return the next movement that they should make
In unity they return a Vector3, that the boids manager will add to their total movement, along with the "weight" that this rule is given
A higher weight rule means that rule should account for more of the total movement of that boid
This “rule” keeps boids that are close to each other going the same way
A boid will check the direction that all near by boids are moving in, get the average direction of its neighbors and begin to align itself with that direction
At the same time however, all of its neighboring boids are checking their direction off of the original boids as well, allowing a group of boids to turn smoothly together, and base their directions off of everyone else
public override Vector3 CalculateMove(FlockAgent agent, List<Transform> context, Flock flock)
{
if (context.Count == 0)
{
return agent.transform.forward;
}
Vector3 alignmentMove = Vector3.zero;
List<Transform> filteredContext = (filter == null) ? context : filter.Filter(agent, context);
foreach (Transform item in filteredContext)
{
alignmentMove += item.transform.forward;
}
alignmentMove /= context.Count;
return alignmentMove;
}
This Code is written for Unity 3D
context is a list of the transforms of all boids within a specific radius of the boid
agent is the boid this movement will apply to
Just like you might see on a sidewalk or road, where people going one way will stick to one side, and others going the other way will be on the other side, the boids should mimic a similar movement, turning to face the average of the group
This allows for some “order” when boids are going different ways on the same path
This “rule” keeps boids away from each other, they try to be a certain distance away from each other, but if this is not possible they will try to keep the maximum equal distance away from all
A boid checks the locations of all other boids around it, and moves in the direction that will get it away from them, however it only avoids them to a point, until it stops counting them as too close and stops trying to move away
They actually keep a farther distance from “stranger” boids, than boids in their own group, and while it checks for these in the same radius, it only counts strangers as a good distance away farther than those in its own group
public override Vector3 CalculateMove(FlockAgent agent, List<Transform> context, Flock flock)
{
if (context.Count == 0)
{
return Vector3.zero;
}
Vector3 avoidenceMove = Vector3.zero;
int nAvoid = 0;
//List<Transform> filteredContext = (filter == null) ? context : filter.Filter(agent, context);
foreach (Transform item in context)
{
if(item.gameObject.tag == ("agent") && item.gameObject.GetComponent<FlockAgent>().AgentFlock != flock)
{
if (Vector3.SqrMagnitude(item.position - agent.transform.position) < flock.SquareAvoidenceRadius * 2)
{
nAvoid++;
avoidenceMove += new Vector3(agent.transform.position.x - item.position.x, 0, agent.transform.position.z - item.position.z);
}
}
else if((item.position - agent.transform.position).magnitude < flock.neighborRadius)
{
nAvoid++;
avoidenceMove += new Vector3(agent.transform.position.x - item.position.x, 0 , agent.transform.position.z - item.position.z);
}
}
if (nAvoid > 0)
{
avoidenceMove /= nAvoid;
}
return avoidenceMove;
}
This Code is written for Unity 3D
context is a list of the transforms of all boids within a specific radius of the boid
agent is the boid this movement will apply to
Just like you are more likely to be close to a friend than a random other pedestrian, but you don't want to get too close to either of them. The boids will avoid touching each other, and stay an ideal distance apart
This “rule” keeps boids close to each other, and boids with the cohesion rule will move towards the center of the group around them
A boid checks the locations of all other boids around it, and moves in the direction that will get it closest to the average position of all of them
Boids will only try to stick with members of their own group, and while they are doing this all boids around them will also be trying to get to the center of the group keeping all boids in the group close together and moving at once
public override Vector3 CalculateMove(FlockAgent agent, List<Transform> context, Flock flock)
{
if(context.Count == 0)
{
return Vector3.zero;
}
Vector3 cohesionMove = Vector3.zero;
List<Transform> filteredContext = (filter == null) ? context : filter.Filter(agent, context);
foreach (Transform item in filteredContext)
{
cohesionMove += new Vector3(item.position.x , 0 , item.position.z);
}
cohesionMove /= context.Count;
cohesionMove -= agent.transform.position;
return cohesionMove;
}
This Code is written for Unity 3D
context is a list of the transforms of all boids within a specific radius of the boid
agent is the boid this movement will apply to
This would be like walking with friends, and trying to stay near them. Everyone in your group works to keep close, no one person moving too far ahead or behind
Obstacle Avoidance works similar to regular avoidance, however it only runs on objects that are specifically labeled obstacle, and has a stronger weight to make sure that boids do not hit them, however instead of trying to avoid all obstacles evenly, the boids try to move away from the obstacle at the same time as they are walking forward
This works on both stationary objects and moving ones, so boids should avoid buildings and cars correctly
This code actually uses a version of raycast that checks for the closest point of an object, meaning that instead of trying to just stay away from the center of the obstacle, boids will steer away from the closest point they could hit
This is much more efficient for larger objects, and means that boids will hug square walls a lot better, as instead of just trying to move away from the center of the object, the boid will take into consideration whether its on a corner, near the center or somewhere between
public override Vector3 CalculateMove(FlockAgent agent, List<Transform> context, Flock flock)
{
List<Transform> objectContext = new List<Transform>();
List<Collider> objects = new List<Collider>();
GameObject[] gameObjects = GameObject.FindGameObjectsWithTag("obstacle");
foreach (GameObject o in gameObjects)
{
objects.Add(o.GetComponent<Collider>());
}
foreach (Collider c in objects)
{
objectContext.Add(c.transform);
}
if (objectContext.Count == 0)
{
return Vector3.zero;
}
Vector3 avoidenceMove = Vector3.zero;
int nAvoid = 0;
foreach (Transform item in objectContext)
{
Vector3 closestPoint = item.gameObject.GetComponent<Collider>().ClosestPoint(agent.transform.position);
if (Vector3.SqrMagnitude(closestPoint - agent.transform.position) < flock.SquareObstacleAvoidenceRadius)
{
nAvoid++;
avoidenceMove += new Vector3(agent.transform.position.x - closestPoint.x,
0,
agent.transform.position.z - closestPoint.z);
}
}
if (nAvoid > 0)
{
avoidenceMove /= nAvoid;
}
return avoidenceMove;
}
This Code is written for Unity 3D
context is a list of the transforms of all boids within a specific radius of the boid
agent is the boid this movement will apply to
Boids will now not run into walls, or get themselves hit by cars, because they will specifically move away from them, just like you would expect people to do
Boids know where they want to go, and use a pathfinding algorithm to get the next steps to get there
This pathfinding algorithm uses the current location of the boid and where the boid wants to get to. It gets 12 places the boid could get to in the next frame, makes a list of these, and a list of the places it could go from there. Using this list they can then find how to get to where they want to go
This algorithm is based on breadth first search, and there are many modifications that could be made for efficiency in this goal seeking function
They follow this path they find pretty loosely, allowing them to adjust to their environment every step of the way
public override Vector3 CalculateMove(FlockAgent agent, List<Transform> context, Flock flock)
{
Vector3 goalMove = Vector3.zero;
goalMove += NextStep(flock.goalPosition.transform.position, agent, flock);
return goalMove;
}
Vector3 NextStep(Vector3 to, FlockAgent agent, Flock flock)
{
List<Vector3> nodesToVisit = new List<Vector3>();
nodesToVisit.Insert(0, agent.transform.position);
List<Vector3> path = new List<Vector3>();
List<Vector3> visitedNodes = new List<Vector3>();
visitedNodes.Add(agent.transform.position);
Vector3 pCurrentNode = agent.transform.position;
bool toNodeAdded = false;
while ((pCurrentNode - to).magnitude <= flock.maxSpeed && nodesToVisit.Count > 0)
{
pCurrentNode = nodesToVisit[0];
nodesToVisit.RemoveAt(0);
path.Add(pCurrentNode);
List<Vector3> connections = new List<Vector3>();
for (int i =0; i < 360; i += 30)
{
Vector3 newPoint = new Vector3(Mathf.Cos(i) * flock.maxSpeed + pCurrentNode.x,
pCurrentNode.y,
Mathf.Sin(i) * flock.maxSpeed + pCurrentNode.z);
connections.Add(newPoint);
}
for (int i = 0; i < connections.Count; i++)
{
Vector3 pConnection = connections[i];
if (!toNodeAdded &&
!path.Contains(pConnection) &&
!nodesToVisit.Contains(pConnection) &&
!Physics.Linecast(pCurrentNode,pConnection))
{
nodesToVisit.Add(pConnection);
if ((pConnection - to).magnitude <= flock.maxSpeed)
{
toNodeAdded = true;
}
visitedNodes.Add(pConnection);
}
}
}
return path[0];
}
This Code is written for Unity 3D
context is a list of the transforms of all boids within a specific radius of the boid
agent is the boid this movement will apply to
This function would be very similar to using google maps, and knowing what turns to make. People don't necessarily follow a perfect straight line and make exact 90 degree turns when going where they need to go, and instead make plenty of small movements to avoid people, and stay together with their group
With these rules the boids will act and move similarly to humans. They know how to get where they want to go, and not hit walls on their way. They stick together with people they know, and avoid those they don’t. All of these are activities that we expect to see when we watch a real group of people walking through the city
When all of the rules work together they can walk around city streets, reacting to their environment and each other
void Update()
{
List<FlockAgent> agentsCopied = agents;
foreach (FlockAgent agent in agentsCopied)
{
if(agent)
{
List<Transform> context = GetNearbyObjects(agent);
Vector3 move = behavior.CalculateMove(agent, context, this);
move *= driveFactor;
if (move.sqrMagnitude > squareMaxSpeed)
{
move = move.normalized * maxSpeed;
}
agent.Move(move);
}
}
}
This is written for Unity 3D