05 Tutorial ‐ Step 3 - project-SIMPLE/simple.toolchain GitHub Wiki

Step 3: Adding Interactions

Objectives

The aim here is to enable simple interactions from the VR headset. In particular, we want to be able to block roads by selecting them with the interaction ray. We'll then look at other types of interaction, with the example of using a button on one of the controllers to change the brightness.

Modification of the GAMA model

Traffic and Pollution.gaml

First, we will modify the "Traffic and Pollution.gaml" file to integrate the notion of blocked roads. In the road species, we will add a boolean blocked attribute (initial value: false), and change the color of blocked road from white to red.

species road {
	...

	bool blocked <- false;

	aspect default {
		draw (shape + 5) color:blocked ? #red: #white;
	}

}

TutorialGAMARoad

We will then modify the reflex update_road_speed of the global section to take into account the fact that speed on a blocked road will be close to 0.

//Reflex to update the speed of the roads according to the weights
reflex update_road_speed {
	road_weights <- road as_map (each::(each.shape.perimeter / each.speed_coeff * (each.blocked ? 100000.0 : 1.0)));
	road_network <- road_network with_weights road_weights;
}

TutorialGAMAGlobal

Finally, we will modify the "carte" display to be able to see the change for the display of roads. For that, just change the line "species road refresh: false;" by "species road refresh: true;".

TutorialGAMADisplay

Traffic and Pollution-VR.gaml

We're going to add an action to block a specific road (based on its name) in the Unity Linker species. In order to make a direct link between roads and their names, we've defined a map called "roads" that allows you to obtain the right road directly from a road name. This action will be called directly from Unity. If the route is not null, if the route is already blocked, the action will unblock it, and block it otherwise.

	map<string,road> roads <- road as_map (each.name :: each);

	action block_road(string id) {
		road b <- roads[id];
		if (b != nil) {
			ask b {
				if (not blocked) {
					blocked <- true;
				} else {
					blocked <- false;
				}
			}

		}
	}

TutorialGAMAUnityLinker

Modification of the Unity project

The first step is to extend the ray interaction distance. Inspect "GodViewPlayer/XR Origin (XR Rig)/Camera Offset/Right Controller/Ray Interactor", then set the "Max Raycast Distance" (in "XR Ray Interactor/Raycast Configuration") to 500 and in "XR Interactor Line Visual" uncheck "Override Line Length".

TutorialRayInteractor

The second step is to specify what will happen when the player points the ray at a route and selects it. Open the script Assets/Gama Provider/SimulationManagerInteraction. In this script, 3 functions control the XR-interaction: HoverEnterInteraction, HoverExitInteraction and SelectInteraction.

HoverEnterInteraction specifies what happens when the player passes over an object that can be interacted with. In the tutorial we want to change the color to blue when the ray passes over a road. So if the object the ray is hovering over has the tag "road", change its color to blue.

   //Defines what happens when a ray passes over an object
    protected override void HoverEnterInteraction(HoverExitEventArgs ev)
    {
        GameObject obj = ev.interactableObject.transform.gameObject;
        if (obj.tag.Equals("road"))
            ChangeColor(obj, Color.blue);
    }

HoverExitInteraction specifies what happens when the player is no longer in contact with an object that can be interacted with. In the tutorial, the object if its tag is "road" changes color and reverts to gray if not selected, or red otherwise.

   //Defines what happens when a ray passes not anymore over an object
    protected override void HoverExitInteraction(HoverExitEventArgs ev)
    {
        GameObject obj = ev.interactableObject.transform.gameObject;
        if (obj.tag.Equals("road"))
        {
            bool isSelected = SelectedObjects.Contains(obj);
            ChangeColor(obj, isSelected ? Color.red : Color.gray);
        }
    }

SelectInteraction specifies what happens when the player selects an object (by default using the controller's second trigger - the white button along the handle). In the tutorial, if it's an object with the "road" tag, Unity will trigger the GAMA action "block_road" which takes the name of the selected object as an argument and change the color of the object to red. Note that to prevent the player from pressing several times on the object too quickly, a timer is used to prevent a second action before "timeWithoutInteraction" seconds (set to 1s by default).

//Defines what happens when a object is selected
    protected override void SelectInteraction(SelectEnterEventArgs ev)
    {
        if (remainingTime <= 0.0)
        {
            GameObject obj = ev.interactableObject.transform.gameObject;
            if (("road").Equals(obj.tag))
            {
                Dictionary<string, string> args = new Dictionary<string, string> {
                         {"id", obj.name }
                    };
                ConnectionManager.Instance.SendExecutableAsk("block_road", args);
                bool newSelection = !SelectedObjects.Contains(obj);
                if (newSelection)
                    SelectedObjects.Add(obj);
                else
                    SelectedObjects.Remove(obj);
                ChangeColor(obj, newSelection ? Color.red : Color.gray);
                remainingTime = timeWithoutInteraction;
            }
        }
    }

TutorialSelectInteraction

Other types of interactions

It's possible to go further in specifying interactions. In particular, it is possible to define exactly what will happen when the player uses a specific button/stick on the controller. To do this, a first step consists in defining a specific action by editing the "XRI Default Input Actions" file in "Assets/Samples/XR Interaction Tookit/2.4.3/Starter Assets". Double-click on this file to open it. Specific actions can be added afterwards. For example, in "XRI RightHand Interaction", we've added a "Main Button" action of type "Button". In the Path, we specified that this action will be triggered by the main button on the right-hand controller.

To be able to use this action, we added a SerializedField in the SimulationManager Script:

[SerializeField] private InputActionReference primaryRightHandButton;

And in the Game Manager, we drag and drop the MainButton action to this field. Then, in the update function (that is activated at each frame),in order to trigger the TriggerMainButton when the main button is pushed we added:

 if (primaryRightHandButton != null && primaryRightHandButton.action.triggered) {
       TriggerMainButton();
 }

Note that what happens when the main button is triggered can be defined using the "TriggerMainButton" method in the SimulationManagerInteraction class.

⚠️ **GitHub.com Fallback** ⚠️