NPC Plugins #

Non ego traffic vehicles (NPCs) are plugins that have the ability to be customized in appearance and behavior in your simulations. This allows you to add local variations of vehicle styles and brands or implement encounters with vehicles displaying distinguished behaviors, such as erratic driving or vehicles stopping frequently (e.g., delivery vehicles, trash pick up, bicycles).

NOTE: Default NPCs are no longer included in simulator source and must be built locally from source for custom binaries. See build instructions.

Table of Contents

NPC Models top#

NPC Models allow customization of NPC visuals, most notably the 3D model of vehicles, but also the material used for the car paint or windows. With supporting custom NPC models we also added the support for motorbikes and vehicles with more two axles as well as more than one steering axle as is found on heavy vehicles.

NPC models must include a named Collider node that is a simplified mesh to attach a Unity mesh collider. The mesh renderer is NOT active and the collider must be marked Convex.

NPCs now support different RigidBody and WheelCollider settings. Users can add these components exactly like EGO vehicle creation and they will override the NPC defaults. This allows support for smaller or larger vehicles with very different physics settings.

  • RigidBody component can be added to the root of the NPC model.
  • RigidBody reference must be set in the inspector if it is added to the model.
  • Wheel Collider Holder object can be added to the root of the model with child wheel collider objects, aligned at the origin.
  • Wheel Collider Holder object reference must be set in the inspector if it is added to the model.
  • Wheel Data must be set for each wheel if a Wheel Collider Holder object was set in the inspector.

We classify NPCs into size categories which decide the spawning frequency as well as which paint colors are more common for which NPC size type. The following table lists all currently implemented size types and the weight that correlates to their relative probability of encountering this type.

NPC Size Type Spawn Weight
Compact 5
MidSize 6
Luxury 2
Sport 1
LightTruck 2
SUV 4
MiniVan 3
Large 2
Emergency 1
Bus 1
Trailer 0
Motorcycle 1
Bicycle 1
F1Tenth 1

In this example, trailer type has a weight of zero, indicating that it should not spawn by itself, while MidSize type has the highest weight, indicating that it is the most frequent vehicle on the road. The NPC Type is configured via a Component added to the root of the prefab.

Similarly, colors for each spawned vehicle is picked from a table for each vehicle type.

NPC asset bundles are detected from <simulator installation path>/AssetBundles/NPCs and added to the simulation automatically.

NPC Animations top#

NPC animations are supported in SVL Simulator. They must be included in the model data and require an AnimationController.controller asset. This controller is the animation state machine that is referenced in the Animator component on the model root object.

In the animation controller, the animation parameter named speed must be setup and set as a transition condition. With this parameter, npc animation speed and transitions can be effected by the npc rigidbody speed. This is the only default parameter supported in the npc controller.

Building NPC Plugins top#

NPCs can be grouped into collections to allow you to import regional collections of vehicles. Place NPCs in Assets/External/NPCs/CollectionFolder/ in separate named folders to have all CollectionFolder vehicles show up in the build menu in a group.

To build an external NPC,

  1. Open Simulator -> Build... menu item
  2. Select an NPC in the build window
  3. Click Build

The bundle named npc_XXX will be placed in the AssetBundles/NPCs/CollectionFolder folder in source code. This is where simulator loads NPC bundles at runtime. To have NPCs load in a binary, they must be placed in the AssetBundles/NPCs/ folder in that binary.

See build instructions for more details.

NPC Behaviors top#

NPC Behaviors allow NPC Vehicles to behave in custom ways different from the provided built-in NPC behavior and are enabled and configured through the Python API.

Before running the simulator (running the executable or pressing Play in the Editor) NPC behavior plugins must be built by the simulator using Simulator -> Build... menu and the built asset placed in the AssetBundles/NPCs folder in the source code or binary.

Open-source example:

Creating NPC Behavior Plugins top#

Create a folder in under your collection folder for each behavior, eg. Assets/External/NPCs/BehavioursNPC/DrunkDriver/.

If your plugin is a pure behavior, that is it has no visual components, we require one C# file to have the same name as the folder under the collection folder, for example note how the name DrunkDriver repeats in both the folder name and file name: Assets/External/NPCs/BehavioursNPC/DrunkDriver/DrunkDriver.cs.

The main class has to inherit from NPCBehaviourBase and implement its abstract methods to hook into the NPC simulation framework. It is also possible to inherit from NPCLaneFollowingBehaviour if you want to implement a slight modification of the default lane following behavior, such as this simple DrunkDriver example:

using System.Collections;
using System.Collections.Generic;

using UnityEngine;
using Simulator.Api;
using Simulator.Map;
using Simulator.Utilities;
using SimpleJSON;

// In this example, we try to simulate a driver under ther influence of alcohol.
// We modify the default NPCLaneFollowingBehaviour because in principle this behavior
// shuld just add variation the the default behavior.
public class NPCDrunkDriverBehaviour : NPCLaneFollowBehaviour
{
    public float steerCorrectionMinTime = 0.0f;
    public float steerCorrectionMaxTime = 0.4f;

    public float steerDriftMin = 0.00f;
    public float steerDriftMax = 0.09f;

    protected float currentSteerDrift = 0.0f;
    protected float nextSteerCorrection = 0;
    // This function in the base class controls the NPC steering
    protected override void SetTargetTurn()
    {
        // we reduce the frequency at which steering is updated to the target heading to
        // simulate loss of attention or reduced reaction time
        if(nextSteerCorrection < Time.fixedTime)
        {
            float steerCorrectionIn = RandomGenerator.NextFloat(steerCorrectionMinTime, steerCorrectionMaxTime);
            nextSteerCorrection = Time.fixedTime + steerCorrectionIn;

            // we add drift to the steering to simulate loss of fine motor skills
            currentSteerDrift = RandomGenerator.NextFloat(steerDriftMin, steerDriftMax);
            currentSteerDrift = currentSteerDrift * Mathf.Abs(RandomGenerator.NextFloat(-1.0f, 1.0f));
            // we can reuse the base steering at reduced frequency
            base.SetTargetTurn();
        }
        else
        {
            // steering drift correlating to driving speed
            currentTurn += currentSteerDrift * currentSpeed;
        }
    }
}

If you want to configure aspects of your NPC behavior from the Python API, you can add a class implementing the ICommand interface as shown in this example:

class DrunkDriverControl : ICommand
{
    public string Name => "agent/drunk/config";

    public void Execute(JSONNode args)
    {
        var uid = args["uid"].Value;
        var api = ApiManager.Instance;

        if (!api.Agents.TryGetValue(uid, out GameObject npc))
        {
            api.SendError(this, $"Agent '{uid}' not found");
            return;
        }

        var behaviour = npc.GetComponent<NPCDrunkDriverBehaviour>();
        if (behaviour == null)
        {
            api.SendError(this, $"Agent '{uid}' is not a drunk driving NPC agent");
            return;
        }

        if(args.HasKey("correctionMinTime")) behaviour.steerCorrectionMinTime = args["correctionMinTime"].AsFloat;
        if(args.HasKey("correctionMaxTime")) behaviour.steerCorrectionMaxTime = args["correctionMaxTime"].AsFloat;
        if(args.HasKey("steerDriftMin")) behaviour.steerDriftMin = args["steerDriftMin"].AsFloat;
        if(args.HasKey("steerDriftMax")) behaviour.steerDriftMax = args["steerDriftMax"].AsFloat;

        api.SendResult(this);
    }
}

example usage from Python:

# common setup code omitted, check PythonAPI/quickstart for examples
# you can check if it has been loaded:
behaviours = sim.available_npc_behaviours
for i in range(len(behaviours)):
    if behaviours[i]["name"]=="NPCDrunkDriverBehaviour": drunkDriverAvailable = True

#later...
npc = sim.add_agent(agent, lgsvl.AgentType.NPC, state)
if drunkDriverAvailable:
    inp = input("make drunk driver? yN")
if (inp == "y" or inp == "Y"):
    npc.set_behaviour("NPCDrunkDriverBehaviour")
    # usage of example command from C# plugin.
    npc.remote.command("agent/drunk/config", { "uid": npc.uid, "correctionMinTime":0.0, "correctionMaxTime":0.6, "steerDriftMin": 0.00, "steerDriftMax":0.09})

# can still use lane following methods as those are inherited
npc.follow_closest_lane(True, 5.6)

Creating NPC Model Plugins top#

Create a folder under your collection folder for each Model, eg. Assets/External/NPCs/YourCollection/YourNPC/. All assets of a NPC such as textures, additional materials or scripts are expected to live inside its folder.

When an NPC is spawned, a NPCController script is attached to the root of the prefab instance, and it will try to find several components to animate the vehicle. To identify the components, we look at the Name property of the Components as follows:

GameObject Name Component Type Description
LightHeadLeft Light Headlights which are controlled by the NPCController to off, regular, high beam etc states
LightHeadRight Light Headlights which are controlled by the NPCController to off, regular, high beam etc states
LightBrakeLeft Light Braking lights, controlled by the NPCController to reflect the vehicle braking
LightBrakeRight Light Braking lights, controlled by the NPCController to reflect the vehicle braking
IndicatorLeftFront Light Left indicator lights controlled by the NPCController when turning or when hazard lights are on
IndicatorLeftRear Light Left indicator lights controlled by the NPCController when turning or when hazard lights are on
IndicatorRightFront Light Right indicator lights controlled by the NPCController when turning or when hazard lights are on
IndicatorRightRear Light Right indicator lights controlled by the NPCController when turning or when hazard lights are on
IndicatorReverse Light Light turns on when NPC is reversing
Body Renderer Main vehicle body mesh
Body Material Material will have its _BaseColor changed to give each NPC a random color from a list
Collider Renderer Simplified collision mesh set to convex and less than 256 polygons
LightHead Material Material which is controlled to emit light in conjunction with headlight Lights
LightBrake Material Material which is controlled to emit light in conjunction with brake Lights
IndicatorLeft Material Material which is controlled to emit light in conjunction with left indicator Lights
IndicatorRight Material Material which is controlled to emit light in conjunction with right indicator Lights
IndicatorReverse Material Material which is controlled to emit light in conjunction with reversing Light
Wheel Renderer Renderer is used to create WheelCollider. NPCs with less than four wheels are assumed to be bikes
"Wheel" and "Front" or "Rear" Renderer Wheels that are supposed to turn when steering

NPC Meta Data top#

Additional information about the NPC prefab is added by attaching a Unity MonoBehaviour Component called NPCMetaData added to each prefab.