ZapBox Controller Triggers in Studio 6.5


#1

The current version of Studio 6.5 does not expose access to the trigger input on the ZapBox controllers. However it is possible to still make use of the Studio “ZapBox Tracker” setup and enable input support. Here’s how:

  1. Add zapbox_trigger_controllers_withworld.zpt (28.8 KB) to the Media Library.
  2. Switch the “Source” property on the ZapBox Tracker node to use this file. You can also delete the “ZapBox User Map.zpt” that Studio uses by default.
  3. Add a script node as a child of the ZapBox Controller in the hierarchy. You can hook into the seen event emitted by this node to access the underlying TargetInstance for the controller, and then attach functions to handle the input events.

See below for the script I was using to test this out. Note I used a parent.one rather than parent.on to make sure we only attach one handler for the input change event.

const hand_colour_triggerOff = symbol.controllers.hand_colour.elements.triggerOff;
const hand_colour_triggerOn = symbol.controllers.hand_colour.elements.triggerOn;

parent.one("seen", instance => {
    var input = instance.booleanInput('slider');
    if(!input) return;

    input.on("change", value => {
        if(value) {
            hand_colour_triggerOn.activate();
        } else {
            hand_colour_triggerOff.activate();
        }
    });
});

You can see this in context in the modified ZapBox template here:
ZapBox Demo With Trigger.zpp (3.5 MB)

We will update Studio’s ZapBox wrappers to expose the trigger inputs in an easier way in a future version, but I hope this helps you out in the short term.

The earlier demo project I posted here shows the full input API and has more comments to describe it, but this one was more about trying to hook into the input events using the current Studio ZapBox support.


ZapBox Controller Triggers in ZapWorks Studio
#2

Thank you so much for your help @simon


#3

Hi @simon, Trigger code works perfectly… any suggestions how to share that state in other listening scripts?

const hand_colour_triggerOff = symbol.controllers.hand_colour.elements.triggerOff;
const hand_colour_triggerOn = symbol.controllers.hand_colour.elements.triggerOn;

export var triggerStatus = 0

parent.one(“seen”, instance => {
var input = instance.booleanInput(‘slider’);
if(!input) return;

input.on("change", value => {
    if(value) {
       var triggerStatus = 1
        hand_colour_triggerOn.activate();
    } else {
       var triggerStatus = 0
        hand_colour_triggerOff.activate();
    }
});

});

and then I’m using a simple if statement within a intersection in the second script…

symbol.nodes.Menu_filter.on(“intersectionenter”, (e) =>; {
const Script = symbol.nodes.controllerScript;

if (symbol.nodes.controllerScript.triggerStatus == 1){

//CODE

}

});


#5

Hi @stephen.bedser,

I have replied to your support request directly, but for the help of others, I will post my response here.

The issue is because the variable is trying to be retrieved before it is declared.

When an Zappar experience is run, the content within the Hierarchy is read and loaded from the root node down.

If there is a script retrieving an exported variable that is higher up in the Hierarchy order than the script it is being declared in, then this will cause an error as that variable cannot be found.

Simply dragging the declaration script higher up the Hierarchy will solve the issue.

Hope this helps.

George


#6

Yup, super appreciate the help! Hopefully it will be a good reference for others :slight_smile:


#7

Hi Stephen,

There are a few other patterns you can also use to communicate between scripts.

Exporting variables and accessing them in other scripts is one perfectly fine way to do it, though you do need to bear in mind the script initialization order in that case as George mentions. There is another issue in your script that you have the word var inside the event handler - that is declaring a new variable scoped to that function, rather than using the one declared above that you have exported.

Another option is to emit a custom event from a node in the hierarchy, and attach an on listener to that in your other script. For example, I could have used the following snippet in the script I posted at the top, rather than directly changing the state in that listener:

parent.one("seen", instance => {
    var input = instance.booleanInput('slider');
    if(!input) return;

    input.on("change", value => {
        parent.emit("custom:boolinputchange", value);
    });
});

NB: The use of the “custom” prefix isn’t required, it’s a convention we often use to distinguish the core events Zappar emits from ones that we have added in scripts, and helps to “namespace” the custom ones to avoid issues down the line if Zappar ever adds an event called the same thing as your custom one.

The event can be attached to like any other one from a node; so in your other script, just drag the ZapBox Controller node from the hierarchy into the scripting view, and add use .on("custom:boolinputchange", value => {...}) to add behaviour when the value changes.

The reason I’d suggest this approach is that the snippet you posted only checks the state of the trigger on the frame where the raycaster intersection becomes true. So therefore you’d need to hold down the trigger, and then point at the button to activate it, but if you point first and then click it wouldn’t work.

Basically I expect what you want is to trigger the action as soon as both the raycaster intersection is true, and the trigger value is also true. To add a bit more complexity, the controller “collar” that we use to detect the trigger isn’t always visible to the camera, especially with “pointing”-type gestures, so you probably also want to handle that case too (input.on("seen", {...}) and input.on("notseen", {...}) would be the events you need to monitor that.

Essentially something like if(input_visible && input_true && raycaster_intersecting) is probably the condition you want to trigger your action. Those 3 variables would need to be updated individually in the relevant event handlers, and then I’d probably have a shared function to update the overall state of a combined variable and trigger whatever action you want when the combination first becomes true.

Sorry I don’t have time to write you a full example, but hopefully that helps a bit!


#8

Thanks for taking the time to respond @simon, appreciate it :slight_smile: