Topic: Large tour setup - loading from JSON?

Hi, I have a few upcoming projects that require about 20 panorama scenes, each with 3-4 interactive hotspots. I'd like to try importing the scene and hotspot data from JSON in order to create the tour object, panos and connections procedurally.

Do you have any existing tools, or suggestions as to how to do this in a reasonable way? Thank you, my team is really appreciative of this package so far and it will save us a lot of time.

Re: Large tour setup - loading from JSON?


Please show me an example of the JSON you have and I'll make an example for you how to automate the creation of tours.

3 (edited by ryahes 2020-12-14 11:10:09)

Re: Large tour setup - loading from JSON?

Thanks - it seems that for format, we are actually using a Google Sheet exported as tab separated value though we could potentially export as JSON if necessary.

Here is the sheet: … sp=sharing

Basically the whole thing is a tree structure and we have elements labeled with an ID.

0 is the main pano hub.


Panos you can get to from hotspots in the main hub.

     -1                   -2

Panos you can get to from hotspots in previous level

  -1.1 -1.2 -1.3     -2.1 -2.2 -2.3

All the items at the end of the tree are image or video hotspots.

  -1.1.1, -1.1.2, x.x.x etc

Re: Large tour setup - loading from JSON?

Unfortunately I don't know how to convert Google Sheet to JSON.
Please attach JSON file.

5 (edited by ryahes 2020-12-14 00:48:35)

Re: Large tour setup - loading from JSON?

Sure thing - here:

Re: Large tour setup - loading from JSON?

Unfortunately I didn't understand the structure of your data and how it should work.
Here's an example of how to create a tour from your JSON.
I hope this helps you get started.

Place the script in the Editor folder, and select the uPano / Create Tour From JSON menu item.

using System.IO;
using System.Text;
using InfinityCode.uPano;
using InfinityCode.uPano.Actions;
using InfinityCode.uPano.Actions.HotSpots;
using InfinityCode.uPano.Controls;
using InfinityCode.uPano.Directions;
using InfinityCode.uPano.Editors.Utils;
using InfinityCode.uPano.HotSpots;
using InfinityCode.uPano.Json;
using InfinityCode.uPano.Plugins;
using InfinityCode.uPano.Tours;
using UnityEditor;
using UnityEditor.Events;
using UnityEngine;

public static class CreateTourFromJSON
    [MenuItem("uPano/Create Tour From JSON")]
    private static void Create()
        string filename = EditorUtility.OpenFilePanel("Select JSON", EditorApplication.applicationContentsPath, "json");
        if (string.IsNullOrEmpty(filename) || !File.Exists(filename)) return;

        string content = File.ReadAllText(filename, Encoding.UTF8);
        Record[] records;

            records = JSON.Deserialize<Record[]>(content);

        if (records == null || records.Length == 0) return;

        Tour tour = CreateTour();

        foreach (Record record in records)

            if (record.type == "Scene") CreatePanorama(tour, record);
            else if (record.type == "Video") CreateVideo(tour, record);
            else if (record.type == "Image") CreateImage(tour, record);

        if (tour.items.Count > 1)
            int r = 400;
            float d = 360f / tour.items.Count;

            for (int i = 0; i < tour.items.Count; i++)
                float a = i * d * Mathf.Deg2Rad;
                tour.items[i].position = new Vector2(Mathf.Cos(a) * r, Mathf.Sin(a) * r);

    private static void CreateImage(Tour tour, Record record)
        // Do something

    private static TourItem CreatePanorama(Tour tour, Record record)
        TourItem item = TourItem.Create(tour);
        if (tour.startItem == null) tour.startItem = item;
        return item;

    private static Tour CreateTour()
        GameObject go = new GameObject("uPano Tour");
        Tour tour = go.AddComponent<Tour>();
        GlobalSettings globalSettings = go.AddComponent<GlobalSettings>();
        globalSettings.defaultHotSpotPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(EditorUtils.assetPath + "\\Examples\\Prefabs\\Hot Spots\\Hot Spot Arrow.prefab");
        globalSettings.defaultDirectionPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(EditorUtils.assetPath + "\\Examples\\Prefabs\\Directions\\Direction Arrow.prefab");
        globalSettings.beforeTransitionPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(EditorUtils.assetPath + "\\Examples\\Transitions\\ExampleFadeOut.prefab");
        globalSettings.afterTransitionPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(EditorUtils.assetPath + "\\Examples\\Transitions\\ExampleFadeIn.prefab");
        HotSpotGlobalActions hsga = go.AddComponent<HotSpotGlobalActions>();
        DirectionGlobalActions dga = go.AddComponent<DirectionGlobalActions>();

        GameObject events = new GameObject("Events");
        events.transform.parent = go.transform;

        GameObject enterGO = new GameObject("Pointer Enter");
        enterGO.transform.parent = events.transform;
        SetScale enterScale = enterGO.AddComponent<SetScale>();
        enterScale.scale = new Vector3(1.2f, 1.2f, 1.2f);
        UnityEventTools.AddPersistentListener(hsga.OnPointerEnter, enterScale.Invoke);
        UnityEventTools.AddPersistentListener(dga.OnPointerEnter, enterScale.Invoke);

        GameObject exitGO = new GameObject("Pointer Exit");
        exitGO.transform.parent = events.transform;
        SetScale exitScale = exitGO.AddComponent<SetScale>();
        UnityEventTools.AddPersistentListener(hsga.OnPointerExit, exitScale.Invoke);
        UnityEventTools.AddPersistentListener(dga.OnPointerExit, exitScale.Invoke);

        GameObject tooltip = new GameObject("Show Tooltip");
        tooltip.transform.parent = events.transform;
        ShowTooltip showTooltip = tooltip.AddComponent<ShowTooltip>();
        showTooltip.tooltipPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(EditorUtils.assetPath + "\\Examples\\Prefabs\\Tooltips\\Tooltip.prefab");
        showTooltip.getTextFromElement = true;
        UnityEventTools.AddPersistentListener(hsga.OnPointerEnter, showTooltip.Invoke);
        UnityEventTools.AddPersistentListener(hsga.OnPointerExit, showTooltip.Hide);

        return tour;

    private static void CreateVideo(Tour tour, Record record)
        // Do something

    internal class Record
        [JSON.Alias("Hierarchy ID")]
        public string hierarchyID;

        public string type;

        [JSON.Alias("Hotspot Image URL")]
        public string hotSpotURL;

        public string title;

        [JSON.Alias("Content URL")]
        public string contentURL;

        public string description;

Re: Large tour setup - loading from JSON?

Thank you! This should definitely help us get started. I'll let you know how it goes, or if I have any additional questions.