Using RVO2 Library C#

Structure

A program performing an RVO2 Library C# simulation has the following global structure.

using System;
using System.Collections.Generic;
namespace RVO
{
class Example
{
// Store the goals of the agents.
IList<Vector2> goals;
Example()
{
goals = new List<Vector2>();
}
public static void Main(string[] args)
{
Example example = new Example();
// Set up the scenario.
example.setupScenario(sim);
// Perform (and manipulate) the simulation.
do {
example.updateVisualization();
example.setPreferredVelocities();
Simulator.Instance.doStep();
} while (!example.reachedGoal());
}
}
Contains all classes, functions, and constants used in the package.
Definition: Line.cs:40

In order to use RVO2 Library C#, the first step is specifying the simulation scenario and its parameters. In the above example program, this is done in the method setupScenario(), which we will discuss below. The step stage is the actual performing of the simulation.

In the above example program, simulation steps are taken until all the agents have reached some predefined goals. Prior to each simulation step, we set the preferred velocity for each agent, i.e. the velocity the agent would have taken if there were no other agents around, in the method setPreferredVelocities(). The simulator computes the actual velocities of the agents and attempts to follow the preferred velocities as closely as possible while guaranteeing collision avoidance at the same time. During the simulation, the user may want to retrieve information from the simulation for instance to visualize the simulation. In the above example program, this is done in the method updateVisualization(), which we will discuss below. It is also possible to manipulate the simulation during the simulation, for instance by changing positions, radii, velocities, etc. of the agents.

Setting up the Simulation Scenario

A scenario that is to be simulated can be set up as follows. A scenario consists of two types of objects: agents and obstacles. Each of them can be manually specified. Agents may be added anytime before or during the simulation. Obstacles, however, need to be defined prior to the simulation, and RVO.Simulator.processObstacles() need to be called in order for the obstacles to be accounted for in the simulation. The user may also want to define goal positions of the agents, or a roadmap to guide the agents around obstacles. This is not done in RVO2 Library C#, but needs to be taken care of in the user's external application.

The following example creates a scenario with four agents exchanging positions around a rectangular obstacle in the middle.

void setupScenario()
{
// Specify global time step of the simulation.
Simulator.Instance.setTimeStep(0.25f);
// Specify default parameters for agents that are subsequently added.
Simulator.Instance.setAgentDefaults(15.0f, 10, 10.0f, 5.0f, 2.0f, 2.0f. new Vector2(0.0f, 0.0f));
// Add agents, specifying their start position.
Simulator.Instance.addAgent(new Vector2(-50.0f, -50.0f));
Simulator.Instance.addAgent(new Vector2(50.0f, -50.0f));
Simulator.Instance.addAgent(new Vector2(50.0f, 50.0f));
Simulator.Instance.addAgent(new Vector2(-50.0f, 50.0f));
// Create goals (simulator is unaware of these).
for (int i = 0; i < Simulator.Instance.getNumAgents(); ++i)
{
goals.Add(-Simulator.Instance.getAgentPosition(i));
}
// Add (polygonal) obstacle(s), specifying vertices in counterclockwise order.
IList<Vector2> vertices = new List<Vector2>();
vertices.Add(new Vector2(-7.0f, -20.0f));
vertices.Add(new Vector2(7.0f, -20.0f));
vertices.Add(new Vector2(7.0f, 20.0f));
vertices.Add(new Vector2(-7.0f, 20.0f));
Simulator.Instance.addObstacle(vertices);
// Process obstacles so that they are accounted for in the simulation.
Simulator.Instance.processObstacles();
}

See the documentation on RVO.Simulator for a full overview of the functionality to specify scenarios.

Retrieving Information from the Simulation

During the simulation, the user can extract information from the simulation for instance for visualization purposes, or to determine termination conditions of the simulation. In the example program above, visualization is done in the updateVisualization() method. Below we give an example that simply writes the positions of each agent in each time step to the standard output. The termination condition is checked in the reachedGoal() method. Here we give an example that returns true if all agents are within one radius of their goals.

void updateVisualization()
{
// Output the current global time.
Console.Write(Simulator.Instance.getGlobalTime());
// Output the current position of all the agents.
for (int i = 0; i < Simulator.Instance.getNumAgents(); ++i)
{
Console.Write(" {0}", Simulator.Instance.getAgentPosition(i));
}
Console.WriteLine();
}
bool reachedGoal()
{
// Check whether all agents have reached their goals.
for (int i = 0; i < Simulator.Instance.getNumAgents(); ++i)
{
// Agent is further away from its goal than one radius.
if (RVOMath.absSq(Simulator.Instance.getAgentPosition(i) - goals[i]) > Simulator.Instance.getAgentRadius(i) * Simulator.Instance.getAgentRadius(i))
{
return false;
}
}
return true;
}

Using similar functions as the ones used in this example, the user can access information about other parameters of the agents, as well as the global parameters, and the obstacles. See the documentation of the class RVO.Simulator for an exhaustive list of public functions for retrieving simulation information.

Manipulating the Simulation

During the simulation, the user can manipulate the simulation, for instance by changing the global parameters, or changing the parameters of the agents (potentially causing abrupt different behavior). It is also possible to give the agents a new position, which make them jump through the scene. New agents can be added to the simulation at any time, but it is not allowed to add obstacles to the simulation after they have been processed by calling RVO.Simulator.processObstacles(). Also, it is impossible to change the position of the vertices of the obstacles.

See the documentation of the class RVORVOSimulator for an exhaustive list of public functions for manipulating the simulation.

To provide global guidance to the agents, the preferred velocities of the agents can be changed ahead of each simulation step. In the above example program, this happens in the method setPreferredVelocities(). Here we give an example that simply sets the preferred velocity to the unit vector towards the agent's goal for each agent (i.e., the preferred speed is 1.0). Note that this may not give convincing results with respect to global navigation around the obstacles. For this a roadmap or other global planning techniques may be used (see one of the example programs that accompanies RVO2 Library C#).

void setPreferredVelocities()
{
// Set the preferred velocity for each agent.
for (int i = 0; i < Simulator.Instance.getNumAgents(); ++i)
{
if (RVOMath.absSq(goals[i] - Simulator.Instance.getAgentPosition(i)) < Simulator.Instance.getAgentRadius(i) * Simulator.Instance.getAgentRadius(i) ) {
// Agent is within one radius of its goal, set preferred velocity to zero.
Simulator.Instance.setAgentPrefVelocity(i, new Vector2(0.0f, 0.0f));
}
else
{
// Agent is far away from its goal, set preferred velocity as unit vector towards agent's goal.
Simulator.Instance.setAgentPrefVelocity(i, RVOMath.normalize(goals[i] - Simulator.Instance.getAgentPosition(i)));
}
}
}

Example Programs

RVO2 Library C# is accompanied by two example programs, which can be found in the $RVO_ROOT/examples directory. The examples are named Blocks and Circle, and contain the following demonstration scenarios:

Blocks A scenario in which 100 agents, split in four groups initially positioned in each of four corners of the environment, move to the other side of the environment through a narrow passage generated by four obstacles. There is no roadmap to guide the agents around the obstacles.
Circle A scenario in which 250 agents, initially positioned evenly distributed on a circle, move to the antipodal position on the circle. There are no obstacles.