ZapBox Controller Triggers in Studio 6.5

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.

2 Likes

Thank you so much for your help @simon

1 Like

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

}

});

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.

image

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

1 Like

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

1 Like

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!

1 Like

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

2 Likes

Hi friends
I am developing a project to visualize the layout of an architectural design of interior design.
Can I use the set of pointcodes to view the internal of a room using the Zapbox set?
I look forward to a positive response.
thank you.
Antonio Ribeiro

How big of a room?
When zapbox don’t see the pointcodes it can lose tracing.

But there was just a post from @simon with a world tracking & tracking Image file That I would love to workover to Zapbox. This would give us endless size. Just drop pointcodes around the room to help keep the ground locked. From there we could really take off!!

Steve

Thank you
Stevesanerd
I’m glad you’re working on it now. I will test and as the doubts appear, I report it to you.
The @simon link was very constructive.

1 Like

The project I’m working on measures 10m x 6m.
Can I create enough pointcodes to distribute in an area of ​​this dimension?
Thank you

That’s pretty big. I’d recommend the zapcode extended tracking solution from the other thread for now.

1 Like

@ Simon,
Now I’m a bit confused, what solution do I have to this problem?
What size of room could I fit in the extended zapcode tracking?

ZapBox tracking only updates when at least one of the pointcodes is visible. Building a good map also needs to see as many pointcodes as possible in the same views. It works well for table-top experiences and larger floor-based maps up to a few metres in size.

World Tracking (using ARKit and ARCore) has no maximum size, it constantly estimates how much the user moves from one frame to the next. The downside of that is “drift” - if the estimate for motion is a bit wrong, content might look like it has moved a bit from where you want it to be.

Zapcode Extended Tracking uses world tracking (so no maximum size). Whenever the zapcode is detected it resets the position of the content to align with it so any drift up to that point will be removed. However if the zapcode is not in view then there may be some drift.

For large scale experiences where you want the content to be consistently positioned from some reference point, then Zapcode Extended Tracking is the way to go for now.

1 Like

@Simon
Good afternoon
I’ve been looking at Documentation, I have not found anything to give me an idea how Zapcode Extended Tracking works.
If I start from a project where it works in image tracking, would I change it to Zapcode Extended Tracking?
Thank you

1 Like

Hi @antoniogr - there’s further info regarding this on another of Simon’s forum posts, here: World tracking & Extended tracking

1 Like

Sorry @James
I’ve been traveling,
I have already studied @simon posts, but I have not been able to advance yet.
I’ll have to study some more.

Hi @antoniogr,

The demo in the other thread is already set up for Zapcode Extended Tracking; you can just export the subsymbol from the demo and import it into your project. As I mentioned I was sharing it in a bit of a pre-release form which means it’s not something we have fully documented yet and so we can’t offer a huge amount of support. If you do have specific questions though, please feel free to ask them over on the other thread (this one is not really relevant) and we’ll try our best to help.

2 Likes