Creating your first Headjack Template

Introduction

This page will teach you how to make a very basic Template for the Headjack platform, that just streams a 360 video. We will then expand this Template to show a very basic menu and make it interactive. When developing a Template for Headjack, you will be working in a stripped down version of our project, which contains the full Headjack API, but simulates all its functionality.

Making the simplest Template

You first need the Template Unity Project. It is recommended to start with a working Template from the Headjack Template Marketplace, but for this tutorial we will be starting from scratch with the empty development Template. Download the empty development Template from the Template Marketplace: https://app.headjack.io/#/templates/my-templates/2816ac0b90e2eef35b9a3e27825a6f08/view Extract the Template zip file, it contains the entire Unity project, including Assets and ProjectSettings

Warning
For performance and consistency, Unity settings set in a Template are ignored by Headjack when building an app. The Unity settings of the Empty Template project are the settings used by Headjack when building an app.

Download and install the latest version of Unity and load the empty project. Open the empty App.unity scene that is included in the project (Assets/App.unity)

Note
App.unity in the main Assets folder is always the first scene loaded by any app build by Headjack.
Warning
Headjack builds all Unity scenes in a Template into the app. So if an app build by Headjack turns out very large, look into removing unused scenes and assets from the Template.

Create an empty asset in the scene that will reference our main script, by clicking GameObject/Create Empty, and name it PlayVideoAsset. Click on PlayVideoAsset in the Hierarchy and Click the large Add Component button in the Inspector. Add a New Script, C Sharp language, named PlayVideoScript. Double click the script to open an editor and start editing PlayVideoScript. under the existing "using" statements at the top of the script, add

using Headjack;

This will make it easier to call Headjack API functions.

Note
With the exception of the VideoPlayer object, all Headjack API function are static functions that can be called from any script (you don't need a reference to an initialized object)

Now we initialize the app by calling App.Initialize(OnEnd, bool, bool). This function downloads the app data from the Headjack servers, so the app is up to date when started. The function also optionally initializes the VR camera (recommended). We initialize the app in the script's Start function so it runs immediately when the app starts:

// Use this for initialization
void Start () {
// Download data from server, like video and thumbnail information for this app
// also creates the camera (autoCreateCamera = true) and a cardboard app starts in VR mode (cardboardStereoMode = true)
App.Initialize(Initialized, true, true);
}
void Initialized(bool success, string error)
{
}

We also added the Initialized function with two parameters and passed that function to App.Initialize(OnEnd, bool, bool) as the first parameter. This first parameter is an OnEnd event that is triggered when App.Initialize is finished. Now we implement the Initialized function and use the app data downloaded from the server to stream the first project in this app's project list.

// Called after app initialization
void Initialized(bool success, string error)
{
// Only start streaming a project if the app is successfully initialized
if (success)
{
// get the first project in the projects list (if there is a first project)
if (App.GetProjects().Length > 0)
{
// ID of the first project
string firstProjectID = App.GetProjects()[0];
// stream the first project (over wifi only)
App.Play(firstProjectID, true, true, delegate (bool vidSuc, string vidErr)
{
// this is an inline delegate that can be used instead of passing a function name as OnEnd
// this is called once when the video is finished playing
App.ShowMessage("You just watched the first project", 5);
});
}
else
{
// tell the user that this app contains no projects
App.ShowMessage("This app contains no videos to show", 5);
}
}
else
{
// show the error message (as 5 second popup), because App.Initialize failed
App.ShowMessage(error, 5);
}
}

Save the script and preview it in the editor, you should see a (low resolution) dummy video, that represents the video of the first project of any app build using this template.

Note
The Template project uses dummy projects, videos, thumbnails, etc., which can be customized by opening the Headjack Settings menu by clicking Headjack/Headjack Settings.
You can replace the dummy video with your own video for testing, by replacing Assets/Headjack/WillBeRemoved/Resources/PPVRexampleVid.ogv with a video file compatible with UnityEngine.MovieTexture.
Warning
The Assets/Headjack/WillBeRemoved folder is removed during building by the Headjack platform, so resources in that folder are not accessible in the final app.

You can also see a VideoPlayer asset and "DontDestroyOnLoad" item added to the scene Hierarchy, with the app data and VR Camera objects.

Note
Any asset in a Unity scene under the section "DontDestroyOnLoad" is not destroyed when changing Unity scenes at runtime

You should never have to directly interact with these objects, instead use the API functions in Headjack.App. When the short video is finished, you should see a popup message saying "You just watched the first project". You can look around in the editor (as this is a 360 degree video player after all), by holding Alt and moving the mouse.

If you wanted to use this Template to build an app with the Headjack platform, you can upload it to Headjack. First create a Template zip file by clicking the menu option Headjack/Export Project as Template On the Headjack platform, you can now upload this zip file as a template (either private or public) here: https://app.headjack.io/#/templates/my-templates/add and then select this Template for an app to build the app with this Template.

In the next section, we will expand this simple Template with a really simple menu and buttons to download and play a project.

Making a simple menu

The simplest Template you just created in the previous section is not very flexible and only supports a single project. Let's make use of Headjack's simple API to make the Template more flexible and interactive.

We begin this Template in much the same way as in the previous section: in an empty App.unity scene, create an Empty asset and add a C# script (called Startup) beginning with:

using System.Collections;
using Headjack;
public class Startup : MonoBehaviour {
// only instance of this startup script, usable by project menu items
public static Startup instance;
// Use this for initialization
void Start()
{
instance = this;
// Download data from server, like video and thumbnail information for this app
// also creates the camera (autoCreateCamera = true) and a cardboard app starts in VR mode (cardboardStereoMode = true)
App.Initialize(delegate(bool succ, string e)
{
if (succ)
{
// after downloading app info, download and load all project thumbnails to show in menu
App.DownloadAllTextures(Initialized);
}
else
{
App.ShowMessage("Internet Connection required when first launching app");
}
}, true, true);
}
void Initialized(bool success, string error)
{
}
// a static method to play a project, used by project menu items to start playing a project
// this static method disables the menu before playing the project
public void Play(string projectId, bool stream)
{
}
}

Besides some placeholder code, the main difference to notice here is that we (down)load the project thumbnails after initialization, so we can show those thumbnails in our menu. Now that we have loaded the project information and thumbnails, we have to initialize the menu, but before we can do that, we have to create the menu elements. Because we are going to use the same menu elements (buttons, text, thumbnail) for each project, we're going to create a Unity prefab that holds all these elements:

Create an Empty holding all elements and the script we are going to create, call it "Project".

Project.png

As a child of Project, add:

We need a Material for the thumbnail Quad, to show the thumbnail and another one for the ButtonBackground Quad, to distinguish the buttons from the background:

Now we're going to create the script that each menu item prefab will use to respond to the buttons and change the title and thumbnail according to the project. Create a C# script called "ProjectMenu", with this content:

using System.Collections;
using Headjack;
public class ProjectMenu : MonoBehaviour {
// public fields preset in prefab for manipulation of this menu item
public TextMesh titleText; // to change project title
public MeshRenderer thumbnailRenderer; // to change project thumbnail
public MeshCollider downloadButtonCollider; // to detect download button press
public MeshCollider playButtonCollider; // to detect play button press
public TextMesh downloadButtonText; // to change download button text based on download status
// Project ID corresponding to this menu item
string projectId;
// download state
enum DownloadState
{
Available, // Project has not been downloaded yet
Downloaded, // Project has been downloaded
Downloading, // Project is currently downloading
};
// this function is called to set project ID of this project menu item
public void SetProjectId(string pID)
{
projectId = pID;
// update project title
titleText.text = App.GetTitle(projectId);
// update project thumbnail
thumbnailRenderer.material.mainTexture = App.GetImage(projectId);
// update download button text based on local file/download state
UpdateDownloadState();
}
// sets text of download button according to download state and returns that state
DownloadState UpdateDownloadState()
{
// Local files exist already, change download button to delete
if (App.GotFiles(projectId))
{
downloadButtonText.text = "Delete";
return DownloadState.Downloaded;
}
// Project is currently downloading, change download button to cancel
if (App.ProjectIsDownloading(projectId))
{
downloadButtonText.text = "Cancel";
return DownloadState.Downloading;
}
// Project is not downloading or downloaded, so ready to download
downloadButtonText.text = "Download";
return DownloadState.Available;
}
// Update is called once per frame
void Update () {
// every frame, check whether a confirm input has been registered
if (VRInput.Confirm.Pressed && projectId != null)
{
// confirm has been pressed on any of the VR input devices
// check if one of the buttons of this menu item was selected
if (App.IsCrosshairHit(downloadButtonCollider))
{
// download button was pressed, take action based on download state
DownloadState dlState = UpdateDownloadState();
switch (dlState)
{
case DownloadState.Downloaded:
// delete downloaded project
App.Delete(projectId);
break;
case DownloadState.Downloading:
// cancel downloading project
App.Cancel(projectId);
break;
case DownloadState.Available:
default:
// start project download
App.Download(projectId, true, delegate(bool succ, string e)
{
// download finished, update download button
UpdateDownloadState();
// if download failed, show message to user (for 5 seconds)
if (!succ)
{
App.ShowMessage("Download failed: " + e, 5);
}
});
break;
}
// update download button text after the above action
UpdateDownloadState();
}
else if (App.IsCrosshairHit(playButtonCollider))
{
// play button was pressed, play the downloaded video if the project is downloaded,
// otherwise, stream the project
if (UpdateDownloadState() == DownloadState.Downloaded)
{
Startup.instance.Play(projectId, false);
}
else
{
Startup.instance.Play(projectId, true);
}
}
}
}
}

Some notes on the ProjectMenu script:

Note
When a project is streamed, the quality of the video depends on the quality of the internet connection. A downloaded project always displays at the highest resolution possible for the device.
When a project is streamed, spatial audio is not supported and the audio track in the video file is played

Now to finish the menu item prefab, attach the ProjectMenu script to the Project empty asset and assign the references in the script as follows, by dragging the appropriate asset into the fields of the Project Menu (Script):

Now to create the prefab, drag the Project asset into the Assets window and a Project.prefab asset should be created (and the assets in the Scene Hierarchy should turn blue).

FullTemplate.png
Structure of the Template before deleting Project prefab

We can now delete the Project asset and all its subassets from the Scene Hierarchy, as we will be creating the menu one item at a time using the Startup script and the Project prefab.

Note
Deleting the assets a prefab is based on from the Scene Hierarchy does not delete or change the prefab.

We should now be left with a Scene with only the Startup asset in it. We will create one more Empty asset, call it "Menu" and change its position (X = 0, Y = 0, Z = 1.5).

Menu.png

The Menu asset will be used as a parent for the menu items we are going to create for each project, so we can turn the menu off easily by disabling the Menu asset.

Lastly, we need to expand the Startup script to create the menu of projects when the app has finished initializing and create a custom Play function to disable this menu before playing a video. This is complete Startup script:

using System.Collections;
using Headjack;
public class Startup : MonoBehaviour {
// only instance of this startup script, usable by project menu items
public static Startup instance;
// Menu GameObject containing the entire menu, for convenient menu disabling
public GameObject menu;
// Prefab of a project menu item
public GameObject menuItemPrefab;
// true when currently playing a video
bool playing;
// Use this for initialization
void Start()
{
instance = this;
// Download data from server, like video and thumbnail information for this app
// also creates the camera (autoCreateCamera = true) and a cardboard app starts in VR mode (cardboardStereoMode = true)
App.Initialize(delegate(bool succ, string e)
{
if (succ)
{
// after downloading app info, download and load all project thumbnails to show in menu
App.DownloadAllTextures(Initialized);
}
else
{
App.ShowMessage("Internet Connection required when first launching app");
}
}, true, true);
}
void Initialized(bool success, string error)
{
// if (down)loading all project thumbnails failed, show a message to the user and don't load menu
if (!success)
{
App.ShowMessage("(Down)Loading project thumbnails failed: " + error, 5);
return;
}
// show crosshair in menu view so menu buttons can be selected by gaze
App.ShowCrosshair = true;
// all project metadata downloaded and loaded, so construct a menu item for each project
Vector3 currentMenuPos = menu.transform.position; // position of current menu item, to place menu items on a horizontal line
GameObject currentMenuItem; // holds current menu item
foreach(string currentProjectId in App.GetProjects())
{
// instantiate prefab (i.e. create a clone of) of project menu item
currentMenuItem = (GameObject)Instantiate(menuItemPrefab, menu.transform, false);
// set position of menu item
currentMenuItem.transform.position = currentMenuPos;
currentMenuPos.x += 0.65f; // move current menu position so next menu item is placed correctly
// set project id of this newly created menu item (also sets projet title and thumbnail)
currentMenuItem.GetComponent<ProjectMenu>().SetProjectId(currentProjectId);
}
}
// a static method to play a project, used by project menu items to start playing a project
// this static method disables the menu before playing the project
public void Play(string projectId, bool stream)
{
// disable menu before starting video
App.ShowCrosshair = false;
menu.SetActive(false);
playing = true;
// play video (over wifi only)
App.Play(projectId, stream, true, delegate (bool succ, string e)
{
// when video is finished playing, show menu
App.ShowCrosshair = true;
playing = false;
App.DestroyVideoPlayer();
menu.SetActive(true);
});
}
// Update is called once per frame
void Update ()
{
// when playing a video, pressing the universal back button returns the user to menu
if (playing && VRInput.Back.Pressed)
{
App.ShowCrosshair = true;
playing = false;
App.DestroyVideoPlayer();
menu.SetActive(true);
}
}
}

Some notes on the Startup script:

All that is left to do is to update the references to Menu and the Project prefab:

To test this finished Template, change the number of example projects by opening the menu Headjack/Headjack Settings and changing the Number of videos to 5 and clicking Apply. Now play the Scene in the Unity Editor and you'll see your menu in action. Look around by holding Alt and moving the mouse the click a menu button by clicking Left Mouse Button.

To build a Headjack app with this template, create a Template zip file with menu option Headjack/Export Project as Template. Upload it to Headjack here: https://app.headjack.io/#/templates/my-templates/add And select the uploaded Template when building a new Headjack app.