Using Math.random to increase re-scannability
Difficulty Level: Intermediate
Coding Knowledge Required: Intermediate
Time to Complete: 45-60 minutes
In this step by step guide, we’ve built on the 3D Photo Feature project by adding a feature which randomizes the 3D model that appears in the experience for the user to take a picture with. Make sure to scan the zapcode above for a sneak peek at what you are about to build.
This project is designed as an introduction to some of the basic functions provided by the JavaScript Math library, covered in our TypeScript Primer which can be accessed through Studio's scripting environment.
Through the use of the Math.random()
function we can increase the re-scannability of our experiences, and give a greater reason for the user to return.
We'll also be covering how to create a home screen in your experience, which users can navigate to and from, through the use of screen symbol communication.
You can download a folder with the completed project, and all of the assets you'll need for this tutorial here.
Step by Step
Part 1: Setting up the project and welcome page assets
1) Setting up the project
Open Studio and create a new ‘Blank Project’, you can give it any name you like, but we’ve gone with ‘randomCharacterPhotoFeature’.
2) Importing the asset files
Import the image files within the 'WelcomeScreenAssets' folder you've downloaded into your project's 'Media Library'.
3) Creating the welcome screen
In this step, we'll be adding a welcome screen that will be displayed to the user when they scan our experience.
3.1) Setting up the welcome screen assets group in the hierarchy
Change your project's orientation to portrait using the option above the 3D view.
In the 'Controllers' panel select the 'orientation' controller, then right-click the 'portrait' state and select 'Make Default'.
Users are more likely to take selfies in portrait mode, so it makes sense for us to set our experience's default orientation to match for a better user experience.
Make sure you've selected the 'base' controller again before continuing.
The base is a special entry in the controller panel that returns Studio to the default behavior where properties that haven't been added to any controller, can be modified.
Create a new group in the hierarchy by right-clicking the 'root' node and selecting 'New > Group'.
Give the group a name which makes it easy to identify, we've gone with 'welcomeScreenAssets'.
Organising the nodes in your hierarchy into groups is good practice, as it makes it easier to navigate.
In the group's 'Properties' panel, change the 'relativeTo' property to 'Z.screenLeft'.
3.2) Setting the transforms for the 'background' node
Drag the 'background' image file from the 'Media Library' into your hierarchy, within the 'welcomeScreenAssets' group.
In its 'Properties' panel, set the:
position | 1.7 | 0 | 0 |
scale | 1.1 | 2 | 1 |
rotation | 0 | 0 | 90 |
Darken the image by changing its 'color' RGB values to [135, 135, 135].
3.3) Setting the transforms for the 'glow' node
Drag the 'glow' media file into the same group, beneath the previous node and set the:
position | 1.1 | 0 | 0 |
scale | 1.5 | 1.5 | 1.5 |
rotation | 0 | 0 | 0 |
Change its color opacity setting to 0.7.
3.4) Setting the transforms for the 'characters' node
Drag the 'characters' media file into the 'welcomeScreenAssets' group, beneath the previous node and set its:
position | 1.1 | -0.04 | 0 |
scale | 1 | 0.8 | 1 |
rotation | 0 | 0 | 90 |
You'll want to keep an eye on the nodes' position in the hierarchy. Nodes later in the hierarchy will be displayed over objects earlier in the hierarchy in the 3D view.
4) Adding the welcome screen text images
Create a new group within the 'welcomeScreenAssets' group and rename it to 'textGroup', making sure it is at the bottom of the group.
Change the group's rotation values to [0, 0, 90].
4.1) Setting the transforms for the 'which.png0' node
Add the 'which.png' media file to the group and set its:
position | 0 | -1.5 | 0 |
scale | 0.5 | 0.18 | 1 |
rotation | 0 | 0 | 0 |
4.2) Setting the transforms for the 'team.png0' node
Add the 'team.png' media file to the group, beneath the 'which.png0' node and set its:
position | -0.3 | -1.8 | 0 |
scale | 0.42 | 0.15 | 1 |
rotation | 0 | 0 | 0 |
4.3) Setting the transforms for the 'are.png0' node
Add the 'are.png' media file to the group, beneath the 'team.png0' node and set its:
position | 0.21 | -1.86 | 0 |
scale | 0.2 | 0.15 | 1 |
rotation | 0 | 0 | 0 |
4.4) Setting the transforms for the 'you.png0' node
Add the 'you.png' media file to the group, beneath the 'are.png0' node and set its:
position | 0.28 | -2.1 | 0 |
scale | 0.35 | 0.16 | 1 |
rotation | 0 | 0 | 0 |
Your hierarchy should look like this:
5) Creating the randomization button
Next, we'll add the visual elements of our randomization button so that it's ready for when we add the functionality later on in the tutorial.
5.1) Setting up the button group in the hierarchy
Right-click on your root node and create a new group called 'buttonGroup'.
Set the group's 'relativeTo' property to 'Z.screenRight'.
Add a plane from your Media Library to this new group.
5.2) Setting the transforms for the 'randomizationButton' node
Right-click the plane node and select 'Rename', then rename it to 'randomizationButton'.
Change its:
position | -0.42 | 0 | 0 |
scale | 0.15 | 0.7 | 1 |
rotation | 0 | 0 | 0 |
Select the color box in the node's 'color' property and change the RGB values to [193, 56, 36].
5.3) Adding text to the button
Import a font to your 'Media Library' by clicking on the + symbol in the panel and selecting 'Fonts > Open Sans Regular'.
Right click the 'buttonGroup' node and select 'New > Text', and rename the new text node to 'buttonText'.
Change its:
position | 0.01 | 0 | 0 |
scale | 0.5 | 0.5 | 1 |
rotation | 0 | 0 | 90 |
Make sure the text node is beneath the 'randomizationButton' node in the hierarchy or it will be obscured.
Don't worry about the size of the text for now as it will be resized once we input our text.
Change its text property to 'LET'S FIND OUT', and make sure its font property is set to 'Open Sans Regular'.
5.4) Adding a 'pointerdown' script to the 'randomizationButton' node
Create a new ‘pointerdown’ script on the 'randomizationButton' node by right-clicking and selecting ‘New > Script > pointerdown’.
We'll be adding some code to this script later on in the tutorial.
Your welcome screen is now set up.
Part 2: Animating your welcome screen
In this step, we'll be adding a simple animation to the welcome screen to make it more visually appealing.
6) Creating a new timeline
Add a new controller to the 'Controllers' panel by clicking the '+' symbol and call it 'glowController'.
Right-click the new controller, select 'New Timeline', and call it 'glowAnimation'.
Change the timeline's 'length' property to 6000.
Use the zoom function to view the entirety of the timeline.
7) Adding the node's properties to the timeline
Select the new timeline in the 'Controllers' panel, and then select the 'glow' node in the hierarchy.
Within the node's 'Properties' panel, click on the '+' symbol next to the 'scale' and 'rotation' properties.
We'll then be able to modify those properties from within our timeline to create an animation.
8) Animating the glow node's rotation
In the timeline set the time to 6001, and change the rotation values to (0, 0, 359).
Click and drag to select both the 'rotation' keyframes on the timeline, right click and select 'Make Transition'.
9) Animating the glow node's scale
In this step, we'll be making the glow appear to grow and shrink at intervals of one second.
We'll be doing this by creating keyframes at 1000ms intervals, and editing the glow node's scale property.
To speed up the process a little, we'll be pasting our keyframes where necessary.
Set the time to 0, right click on an empty section of the timeline and select 'Add keyframe at current time'. Make sure that the node's 'scale' value is still at (1.5, 1.5, 1.5).
Set the time to 1000 and change the scale to (1, 1, 1).
Click and drag to select the keyframes, right click and select 'Make Transition'.
Set the time to 2000 and change the scale back to (1.5, 1.5, 1.5).
Delete the transition that appeared to the right of the keyframe at 2000 by right-clicking it and selecting 'Delete transition'.
Click and drag to select the 'scale' keyframes at times 0, 1000 and 2000.
Right-click on the timeline and select 'Copy Keyframes'.
With time 2000 still selected, right click and select 'Paste'.
Set the time to 4000, and paste the keyframes again.
Click and drag to select the keyframes at 6000 and 6001, then right click and select 'Make Link > with left value'.
Lastly, click on the loop option found to the right of the timeline's 'Length (ms)' property, to make the timeline loop.
The glow animation is now set up, you can preview it by clicking on the play button in the timeline controls.
Now we just need to set the animation to play, which we can do by selecting the 'show' script in the hierarchy, dragging the timeline within the parent.on("show")
function, and selecting 'play'.
Part 3: Preparing the Zapbot photo feature subsymbol
Our welcome screen is now ready, so we'll be setting up one of our photo features next.
10) Creating the subsymbol
Add a new symbol to the project by clicking the '+' in the 'Symbol Definitions' panel and select the ‘Photo feature 3D’ template.
Name the new symbol ‘ZapbotPhotoFeature’.
11) Importing the Zapbot model
Enter the ‘ZapbotPhotoFeature’ symbol by double-clicking on its symbol definition. You should be greeted with the familiar photo feature screen.
If your 3D view is in landscape mode you can change its orientation to portrait by selecting the option above the 3D view.
In the 'Controllers' panel select the portrait state within the orientation controller, right-click it and select 'Make Default'.
Remember to click back on the 'base' controller before making any further changes.
Import the ‘Zapbot.pod’ file from within the ‘3D Models > Zapbot’ inside the assets download folder by dragging the file into your 'Media Library'.
You can click and drag the model's textures files from the folder into the texture pop-up, to assign them automatically.
Drag the resulting model’s symbol definition into the hierarchy under ‘onScreenObject > onScreenObject-objectGroup > objectGroup’.
Set the model's transforms to:
position | 0.5 | 0 | 0 |
scale | 7 | 7 | 7 |
rotation | -15 | 180 | -90 |
Due to changes in the 3D import process, the scale applied to the model in the image below is now outdated, please use the values provided above.
12) Adding the photo feature share message
Select the ‘buttonCode’ script node within the hierarchy, and find Z.device.snapshot();
on line 21 within the script.
In a new line directly before it, add the following line of code:
Z.device.snapshotMessage("#TeamZapbot");
This will pre-fill the social media share field with our message.
Some social media platforms will ignore this pre-filled message and display a blank field.
13) Changing the experience's theme color
We'll also change the color of the Zappar app's UI during our experience.
Find the 'device' node in the hierarchy and make sure its 'themeColor' box is checked.
Change its color property to RGB values (193, 56, 36).
Part 4: Managing the screen symbols
In this step we'll be adding functionality that will let a user head back to the parent symbol from within the photo feature screen symbol.
14) Adding a back button to the Zapbot photo feature
While still in the ‘ZapbotPhotoFeature’ symbol, import the 'backArrow' image file from your downloads folder to your media library.
Select the root node in the hierarchy, right click and select 'New > Group'.
Rename the group to 'backGroup'.
In the 'Properties' panel, change its 'relativeTo' property to 'Z.screenLeft'.
The photo feature template already contains an image called 'circlebg.png' in the Media Library, which we'll be reusing for our own button.
Drag this file into 'backGroup'.
Change the node's transfoms to:
position | 0.14 | -0.72 | 0 |
scale | 0.1 | 0.1 | 1 |
rotation | 0 | 0 | 0 |
Select the color box in the node's 'color' property and change the RGB values to [193, 56, 36].
The photo feature template also contains an existing 'button' symbol which we can use.
Drag the 'button' subsymbol from your 'Symbol Definitions' panel into 'backGroup', beneath the 'circlebg.png0' node.
Rename this new instance of the button subsymbol to 'backArrow'.
Change both its 'NormalIcon' and 'DepressedIcon' properties to 'backArrow.png'.
Set the 'backArrow' node's transforms to:
position | 0.12 | -0.72 | 0 |
scale | 0.12 | 0.12 | 1 |
rotation | 0 | 0 | 90 |
15) Adding the script to the button
Right click the 'circlebg.png0' node and select ‘New > Script > pointerdown’.
We only need to add a single line of code within the pointerdown
function:
symbol.close();
16) Making our new button disappear when a snapshot is taken
Tags are string identifiers that can be added to nodes to easily perform common functions on multiple nodes.
The photo feature template already contains a tag called 'global:UIElement'.
There's an existing function within the template that calls the .hide()
function on all nodes with this tag, when a photo is taken.
Since we also want our button to disappear when the user takes a photo we'll be adding the tag to our button.
Select the ‘backGroup’ node in the hierarchy and change its 'tag’ property from ‘- - -’ to ‘global:UIElement’.
Click on the 'show' state within the 'UI' controller in the 'Controllers' panel, and then select the 'backGroup' node in the hierarchy.
Click the '+' symbol next to the group's 'visible' and 'enabled' properties to add them to the controller.
We want the button to appear towards the end of the transition between the show and hide states, so make sure both its properties are set to 'Instant' and 'Pin to end'.
Select the 'hide' state and within the group's properties and uncheck the 'visible' and 'enabled' properties.
17) Adding a reference to our Zapbot photo feature in the script
Head back to the parent symbol by clicking on it in the breadcrumb bar above the 3D view.
In the hierarchy select the 'randomizationButton_pointerdown0' script we created in Part 1.
Hit 'Enter' at the top of the script a few times to make room for the symbol reference we'll be adding.
Create a reference to our ZapBot photo feature symbol by dragging it from the ‘Symbol Definitions’ panel into the top of our script and selecting ‘Construct symbol’.
18) Displaying our parent symbol when a photo feature symbol is closed
Add the following block of code after the pointerdown
function.
ZapbotPhotoFeature.on("close", () => {
// Runs when the ZapbotPhotoFeature symbol is closed
// Show the current symbol, the parent symbol
symbol.show();
});
The first line of code will listen out for when the close
event is emitted by the ZapbotPhotoFeature
symbol reference we created in the previous step, which happens automatically through the script we set up in step 15.
If the event is fired on the symbol, the displayParentSymbol
function will run and call show()
on our parent symbol.
Part 5: Preparing the other photo feature symbols
Now that the ZapBot photo feature has been set up, we can use it to create our other two photo feature symbols.
19) Preparing the Zappar Bunny photo feature
While in the parent symbol, right click on the 'ZapbotPhotoFeature' symbol in the 'Symbol Definitions' panel, and select 'Duplicate'.
Name the new symbol 'BunnyPhotoFeature', and double-click to enter it.
Right click and delete the 'Zapbot0 (Zapbot)' node within the 'onScreenObject >onScreenObject-objectGroup > objectGroup' group in the hierarchy.
Scroll down the 'Symbol Definitions' panel and find the 'Zapbot' symbol, then right-click it and select 'Delete'.
Import the ‘Zbunnypose03.pod’ file from '3D Models > Zappar Bunny' inside the assets download folder by dragging the file into your 'Media Library'.
You can click and drag the model's textures files from the folder into the texture pop-up, to assign them automatically.
Drag the resulting symbol from your 'Symbol Definitions' into the hierarchy, within the 'onScreenObject >onScreenObject-objectGroup > objectGroup' group in the hierarchy.
Set the model's transforms to:
position | 0.6 | 0 | 0 |
scale | 4 | 4 | 4 |
rotation | -20 | 0.35 | 90 |
Due to changes in the 3D import process, the scale applied to the model in the image below is now outdated, please use the values provided above.
Find the 'buttonCode' script in the hierarchy, under 'buttonGroup'.
On line 21, replace the #TeamZapbot
part of the snapshot message code with #TeamBunny
, so that it looks like this:
Z.device.snapshotMessage("#TeamBunny");
Return to the parent symbol.
Find the 'randomizationButton_pointerdown0' script within the hierarchy and create a reference to the Zappar bunny photo feature by dragging the symbol from the symbol definitions into the script, underneath our 'ZapbotPhotoFeature' reference.
Add an .on ("close")
event handler for the BunnyPhotoFeature underneath the one we created previously for Zapbot.
BunnyPhotoFeature.on("close", () => {
// Runs when the BunnyPhotoFeature symbol is closed
// Show the current symbol, the parent symbol
symbol.show();
});
Our Zappar Bunny photo feature is now set up.
20) Preparing the Dave the Dino photo feature
While in the parent symbol, right click on the 'ZapbotPhotoFeature' symbol in the 'Symbol Definitions' panel, and select 'Duplicate'.
Name the new symbol 'DinoPhotoFeature', and double-click to enter it.
Right click and delete the 'Zapbot0 (Zapbot)' node within the 'onScreenObject >onScreenObject-objectGroup > objectGroup' group in the hierarchy.
Scroll down the 'Symbol Definitions' panel and find the 'Zapbot' symbol, then right-click it and select 'Delete'.
Import the ‘Dave_Dino.pod’ file from '3D Models > Dave the Dino' inside the assets download folder by dragging the file into your 'Media Library'.
You can click and drag the model's textures files from the folder into the texture pop-up, to assign them automatically.
Drag the resulting symbol from your 'Symbol Definitions' into the hierarchy, within the 'onScreenObject >onScreenObject-objectGroup > objectGroup' group in the hierarchy.
Set the model's transforms to:
position | 0.5 | 0 | 0 |
scale | 5 | 5 | 5 |
rotation | 140 | 0 | 90 |
Due to changes in the 3D import process, the scale applied to the model in the image below is now outdated, please use the values provided above.
Find the 'buttonCode' script in the hierarchy, under 'buttonGroup'.
On line 21, replace the #TeamZapbot
part of the snapshot message code with #TeamDino
, so that it looks like this:
Z.device.snapshotMessage("#TeamDino");
Return to the parent symbol.
Find the 'randomizationButton_pointerdown0' script within the hierarchy and create a reference to the Dino photo feature by dragging the symbol from the symbol definitions into the script, underneath our 'BunnyPhotoFeature' reference.
Add an .on ("close")
event handler for the DinoPhotoFeature underneath the one we created previously for Zappar Bunny.
DinoPhotoFeature.on("close", () => {
// Runs when the DinoPhotoFeature symbol is closed
// Show the current symbol, the parent symbol
symbol.show();
});
You now have the three photo feature symbols which we will display at random set up, so it’s time to add the randomization code itself.
Part 6: Adding the randomization script to our experience
In this part of the tutorial, we'll be adding some variety to our experience by sending the user to a random team's photo feature when they tap on a button.
We'll also be making sure that they're not sent to the same photo feature twice in a row, in case they press the button again.
We'll be doing this by using a math function called Math.Random()
.
Studio’s scripting environment provides access to JavaScript's Math library, which itself provides the ‘Math.random()’ function.
By default this function returns a random number between 0 (inclusive) and 1 (exclusive), but can be multiplied, and combined with another JavaScript Math library function, 'Math.floor()’, to return a random number within any range.
The
Math.floor()
function rounds a number down to it's nearest integer.
21) Creating an array to hold our photo feature references
Using variables in our script allows us to store and modify values.
These values can be numbers, strings of text, arrays and much more, but we'll be using an array.
Arrays are a special type of JavaScript object which can be used to store multiple values in a single variable. Arrays can also hold objects, so we'll be using one to hold the photo feature references we created previously in our script.
Variables must be declared before they are used, so we'll be adding our array at the top of the script, beneath the photo feature references we'd created previously.
The array’s elements are separated by commas, and are contained within a set of square brackets i.e. ‘[ ]’.
var photoFeatureArray = [ZapbotPhotoFeature, BunnyPhotoFeature, DinoPhotoFeature];
You can access an element in an array by specifying its index.
The first element in an array is found at index 0, the second element at index 1 and so on.
This concept of zero-based numbering is present in many mathematical and programming approaches, so you may have come across it before.
With this in mind, we know that the first photo feature in our array is at index 0, and the last photo feature is at index 2, which will be important for our next step.
22) Adding the randomization function
Next, we’ll create a new empty variable called randomNumber
after the array but before the pointerdown
function.
var randomNumber;
This variable will hold the randomized value we’ll be generating later on in the code, so it won't be assigned a value until the 'pointerdown' function runs when the user taps on the button.
Within the pointerdown
function itself, we will add the following line of code:
randomNumber = Math.floor(Math.random() * 3);
‘Math.random()’ returns a value between 0 and 1 by default, so we’re multiplying the result by 3, and then using ‘Math.floor()’ to round this down to its nearest whole value, giving a number between 0 and 2.
You’ll notice that the three possible results we’re generating with our code (0, 1 or 2) directly correlate with the index addresses of our photo feature array elements.
23) Displaying the photo feature and hiding our parent symbol
Now we just need to call .display()
on the photo feature at the array index of the random number we generated.
photoFeatureArray[randomNumber].display();
We also want to hide the parent symbol, so that it doesn’t interfere with our photo feature screen symbol.
symbol.hide();
24) Adding a 'do...while' loop
We want to make sure that our random number generator doesn't give the same result twice in a row, which would send the user to the same photo feature consecutively.
This could result in quite a boring user experience, but we can add some variety by using a do/while loop in our code.
This type of loop executes the block of code specified within the do
part of the loop (within the { }) at least once, and then checks if the condition stated in the while
part is true.
If the condition is true it will repeat the loop and will continue to do so for as long as the condition is true.
A do/while loop follows this structure:
do {
execute code;
}
while (thisCondition);
We'll be running the randomization code within the do
part of our loop, and checking that our new result is different to the previous one within the condition
.
Before we add the loop we'll first need to create a new variable, which we’ll declare at the top of our script beneath our randomNumber
variable.
var previousNumber;
We'll be using this variable later to store the last value returned by the randomization code.
We'll be setting up our do/while loop to run the randomization code at least once, before checking whether the result is equal to the one generated in the previous iteration of the loop and if so, to keep running the randomization code until this isn't the case.
Remove the Math.random()
code from its previous location in the script, as we'll be moving it within the do
part of our do/while loop.
Add the following code within the pointerdown
function, before the .display()
and .hide()
functions.
do {
randomNumber = Math.floor(Math.random() * 3);
}
At this point you'll get an error in the script (red underline). This is because the do/while loop is currently incomplete, but we'll be adding that next so you can ignore the error for now.
Within the condition
we will add a check that compares our randomNumber
and previousNumber
variables, which hold the new and previous values returned by the randomization code, respectively.
while (randomNumber == previousNumber);
The complete do/while loop should look like this:
do {
randomNumber = Math.floor(Math.random() * 3);
}
while (randomNumber == previousNumber);
Now that our do/while loop is set up, we’ll tell our previousNumber
variable to store the value held by the randomNumber
variable, so that the next time the pointerdown
code is run we’ll know what the previous result was.
We'll place this code after the loop, but before the .display()
and .hide()
functions.
previousNumber = randomNumber;
Congratulations!
That’s it, you've completed all the steps in this tutorial and used Studio's scripting functionality to create a randomized experience.
You can preview the experience by clicking the ‘Preview’ button in the top left of ZapWorks Studio and scanning the temporary zapcode generated.
Hopefully, you've also learned a few best practice methods when working with Studio.
You are well on your way to mastering ZapWorks Studio. Nice!
Suggested Next Steps
Why don't you try adding your own models to the photo features.
You could even go a step further and add extra photo feature options for the user to be sent to at random!