Beispiel #1
0
 /// <summary>
 /// Callback function when toggle button is selected.
 /// </summary>
 /// <param name="item">Caller item object.</param>
 /// <param name="value">Selected value of the toggle button.</param>
 private void _OnToggleChanged(AreaDescription item, bool value)
 {
     if (value)
     {
         m_curAreaDescriptionUUID = item.m_uuid;
     }
 }
    /// <summary>
    /// Actually do the Area Description save.
    /// </summary>
    /// <returns>Coroutine IEnumerator.</returns>
    private IEnumerator _DoSaveCurrentAreaDescription()
    {
        // m_guiTextInputContents = "Unnamed";

        if (m_saveThread != null)
        {
            yield break;
        }
        bool saveConfirmed = true;

        if (saveConfirmed)
        {
            // Disable interaction before saving.
            m_initialized = false;
            m_savingText.gameObject.SetActive(true);
            if (m_tangoApplication.m_areaDescriptionLearningMode)
            {
                // The keyboard is not readable if you are not in the Unity main thread. Cache the value here.
                string name;
                name = GetComponent <AreaLoadingStartup>().GetName();

                m_saveThread = new Thread(delegate()
                {
                    // Start saving process in another thread.
                    m_curAreaDescription = AreaDescription.SaveCurrent();
                    AreaDescription.Metadata metadata = m_curAreaDescription.GetMetadata();
                    metadata.m_name = name;
                    m_curAreaDescription.SaveMetadata(metadata);
                });
                m_saveThread.Start();
                //m_saveThread.Join();
            }
        }
    }
Beispiel #3
0
 /// <summary>
 /// Callback function when toggle button is selected.
 /// </summary>
 /// <param name="item">Caller item object.</param>
 /// <param name="value">Selected value of the toggle button.</param>
 private void _OnToggleChanged(AreaDescription item, bool value)
 {
     if (value)
     {
         Globals.m_curAreaDescription = item;
     }
 }
Beispiel #4
0
 // Retrieves the latest named ADF found in the disk.
 // Null if the permission is granted but the file is not found.
 // The behavior is undefined when a user permission has not been granted.
 public static AreaDescription FindAdfByName(string name)
 {
     return(AreaDescription.GetList()
            .Where(d => d.GetMetadata().m_name == name) // Matches name.
            .OrderBy(n => n.GetMetadata().m_dateTime)   // Sorts by date & time.
            .LastOrDefault());                          // Null if not found.
 }
Beispiel #5
0
    /// <summary>
    /// Join or host a game.
    /// </summary>
    /// <param name="isJoin">If the client is hosting or joining game.</param>
    public void JoinOrCreateGame(bool isJoin)
    {
        if (!PhotonNetwork.insideLobby)
        {
            AndroidHelper.ShowAndroidToastMessage("Please wait to join or create the room until you are in lobby.");
            Debug.Log("Please wait to join or create the room until you are in lobby." + Environment.StackTrace);
            return;
        }

        if (isJoin)
        {
            Globals.m_curAreaDescription = null;
            if (PhotonNetwork.GetRoomList().Length == 0)
            {
                AndroidHelper.ShowAndroidToastMessage("There's no room in the lobby.");
                Debug.Log("There's no room in the lobby." + Environment.StackTrace);
                return;
            }
        }
        else
        {
#if UNITY_EDITOR
            Globals.m_curAreaDescription = AreaDescription.ForUUID("abc");
#else
            if (Globals.m_curAreaDescription == null)
            {
                AndroidHelper.ShowAndroidToastMessage("No Area Description selected.");
                Debug.Log("No Area Description selected." + Environment.StackTrace);
                return;
            }
#endif
        }

        Application.LoadLevel("MultiplayerCubeStacker");
    }
    /// <summary>
    /// Actually do the Area Description save.
    /// </summary>
    /// <returns>Coroutine IEnumerator.</returns>
    private IEnumerator _DoSaveCurrentAreaDescription()
    {
        if (TouchScreenKeyboard.visible || m_saveThread != null)
        {
            yield break;
        }

        TouchScreenKeyboard kb = TouchScreenKeyboard.Open("Unnamed");

        while (!kb.done && !kb.wasCanceled)
        {
            yield return(null);
        }

        if (kb.done)
        {
            // Disable interaction before saving.
            m_initialized = false;
            m_savingText.gameObject.SetActive(true);
            m_saveThread = new Thread(delegate()
            {
                // Start saving process in another thread.
                m_curAreaDescription = AreaDescription.SaveCurrent();
                AreaDescription.Metadata metadata = m_curAreaDescription.GetMetadata();
                metadata.m_name = kb.text;
                m_curAreaDescription.SaveMetadata(metadata);
            });
            m_saveThread.Start();
        }
    }
Beispiel #7
0
    /// <summary>
    /// Populate a scrolling list with Area Descriptions. Each element will check if there is any associated
    /// mesh data tied to the Area Description by UUID. The Area Description file and linked mesh data are
    /// loaded when starting the game.
    /// </summary>
    private void _PopulateAreaDescriptionUIList()
    {
        // Load Area Descriptions.
        foreach (Transform t in m_listContentParent.transform)
        {
            Destroy(t.gameObject);
        }

        // Update Tango space Area Description list.
        AreaDescription[] areaDescriptionList = AreaDescription.GetList();

        if (areaDescriptionList == null)
        {
            return;
        }

        foreach (AreaDescription areaDescription in areaDescriptionList)
        {
            GameObject newElement = Instantiate(m_listElement) as GameObject;
            MeshOcclusionAreaDescriptionListElement listElement = newElement.GetComponent <MeshOcclusionAreaDescriptionListElement>();
            listElement.m_toggle.group             = m_toggleGroup;
            listElement.m_areaDescriptionName.text = areaDescription.GetMetadata().m_name;
            listElement.m_areaDescriptionUUID.text = areaDescription.m_uuid;

            // Check if there is an associated Area Description mesh.
            bool hasMeshData = File.Exists(m_meshSavePath + "/" + areaDescription.m_uuid) ? true : false;
            listElement.m_hasMeshData.gameObject.SetActive(hasMeshData);

            // Ensure the lambda makes a copy of areaDescription.
            AreaDescription lambdaParam = areaDescription;
            listElement.m_toggle.onValueChanged.AddListener((value) => _OnToggleChanged(lambdaParam, value));
            newElement.transform.SetParent(m_listContentParent.transform, false);
        }
    }
Beispiel #8
0
 public void OnTangoPermissions(bool permissionsGranted)
 {
     if (permissionsGranted)
     {
         AreaDescription[]        list = AreaDescription.GetList();
         AreaDescription.Metadata mostRecentMetadata = null;
         if (list.Length > 0)
         {
             // Find and load the most recent Area Description
             area = list[0];
             mostRecentMetadata = area.GetMetadata();
             foreach (AreaDescription areaDescription in list)
             {
                 AreaDescription.Metadata metadata = areaDescription.GetMetadata();
                 if (metadata.m_name.Contains("nathanbrandon00"))
                 {
                     area = areaDescription;
                     break;
                 }
             }
             loadedMessage = "AREA LOADED" + Environment.NewLine;
             m_tangoApplication.Startup(area);
             loadSuccess = true;
         }
     }
 }
Beispiel #9
0
    /// <summary>
    /// Actually do the Area Description save.
    /// </summary>
    /// <returns>Coroutine IEnumerator.</returns>
    private IEnumerator _DoSaveAreaDescriptionAndMesh()
    {
        if (m_saveThread != null)
        {
            yield break;
        }

        // Disable interaction before saving.
        m_initialized     = false;
        m_savingText.text = "Saving Area Description...";

        if (string.IsNullOrEmpty(m_savedUUID))
        {
            m_saveThread = new Thread(delegate()
            {
                // Save the Area Description to file.
                m_curAreaDescription = AreaDescription.SaveCurrent();
                AreaDescription.Metadata metadata = m_curAreaDescription.GetMetadata();
                m_savedUUID     = m_curAreaDescription.m_uuid;
                metadata.m_name = metadata.m_dateTime.ToLongTimeString();
                m_curAreaDescription.SaveMetadata(metadata);

                // Save the tango dynamic mesh to file.
                StartCoroutine(_DoSaveTangoDynamicMesh());
            });
            m_saveThread.Start();
        }
        else
        {
            StartCoroutine(_DoSaveTangoDynamicMesh());
        }
    }
Beispiel #10
0
    /// <summary>
    /// Refresh the scrolling list's content for both list.
    ///
    /// This function will query from the Tango API for the Tango space Area Description. Also, when it populates
    /// the scrolling list content, it will connect the delegate for each button in the list. The delegate is
    /// responsible for the actual import/export  through the Tango API.
    /// </summary>
    private void _PopulateList()
    {
        foreach (Transform t in m_listContentParent.transform)
        {
            Destroy(t.gameObject);
        }

        // Update Tango space Area Description list.
        AreaDescription[] areaDescriptionList = AreaDescription.GetList();

        if (areaDescriptionList == null)
        {
            return;
        }

        foreach (AreaDescription areaDescription in areaDescriptionList)
        {
            GameObject newElement = Instantiate(m_listElement) as GameObject;
            AreaDescriptionListElement listElement = newElement.GetComponent <AreaDescriptionListElement>();
            listElement.m_toggle.group             = m_toggleGroup;
            listElement.m_areaDescriptionName.text = areaDescription.GetMetadata().m_name;
            listElement.m_areaDescriptionUUID.text = areaDescription.m_uuid;

            // Ensure the lambda makes a copy of areaDescription.
            AreaDescription lambdaParam = areaDescription;
            listElement.m_toggle.onValueChanged.AddListener((value) => _OnToggleChanged(lambdaParam, value));
            newElement.transform.SetParent(m_listContentParent.transform, false);
        }
    }
Beispiel #11
0
    public void InitTangoScene()
    {
        string     prefabPath = "Prefabs/UI/SceneUI";
        GameObject content    = transform.FindChild("Top/SceneBtn/Scroll View/Viewport/Content").gameObject;

        AreaDescription[] areaDescriptionList = AreaDescription.GetList();
        if (areaDescriptionList == null)
        {
            return;
        }
        for (int i = 0; i < areaDescriptionList.Length; i++)
        {
            AreaDescription desc = areaDescriptionList [i];
            _AreaDescription.Add(desc);             //保存所有的描述

            GameObject data = Instantiate(Resources.Load(prefabPath, typeof(GameObject))) as GameObject;
            data.transform.SetParent(content.transform, false);

            Text name = data.transform.FindChild("desc").GetComponent <Text> ();
            name.text = desc.GetMetadata().m_name;

            int    pos    = i;
            Button button = data.gameObject.GetComponent <Button> ();
            button.onClick.AddListener(delegate() {
                this.PressLoad(pos);
            });
        }
    }
Beispiel #12
0
    IEnumerator Download(string guid)
    {
        var form   = new WWWForm();
        var server = "https://finchserver.azurewebsites.net/uploads/";

        using (var www = UnityWebRequest.Get(server + guid + ".bin"))
        {
            yield return(www.Send());

            if (www.isError)
            {
                Debug.Log("ajax error: " + www.error);
            }
            else
            {
                var bytes = www.downloadHandler.data;
                Debug.Log("ajax downloadbytes: " + bytes.Length);

                File.WriteAllBytes("/sdcard/" + guid, bytes);

                var res = AreaDescription.ImportFromFile("/sdcard/" + guid);
                Debug.Log("ajax result importfromfile: " + res);
            }
        }
    }
    /// <summary>
    /// Loads the Area Description.
    /// </summary>
    public void LoadAreaDescription()
    {
        // Check that Area Description has been found or do nothing
        if (string.IsNullOrEmpty(m_areaDescriptionUUID))
        {
            AndroidHelper.ShowAndroidToastMessage("Please select a scan");
            return;
        }
        else
        {
            m_areaDescription = AreaDescription.ForUUID(m_areaDescriptionUUID);
            m_tangoApplication.m_areaDescriptionLearningMode = false;

            m_tangoApplication.Startup(m_areaDescription);
            m_poseController.gameObject.SetActive(true);
        }

        m_panelAreaDescriptionPicker.SetActive(false);

        if (m_seeOnly)
        {
            m_panelSeeMenuTop.SetActive(true);
        }
        else
        {
            m_panelPlaceMenuSide.SetActive(true);
        }
    }
    public void OnTangoPermissions(bool permissionsGranted)
    {
        if (permissionsGranted)
        {
            AreaDescription[]        list               = AreaDescription.GetList();
            AreaDescription          mostRecent         = null;
            AreaDescription.Metadata mostRecentMetadata = null;
            if (list.Length > 0)
            {
                // Find and load the most recent Area Description
                mostRecent         = list[0];
                mostRecentMetadata = mostRecent.GetMetadata();
                foreach (AreaDescription areaDescription in list)
                {
                    AreaDescription.Metadata metadata = areaDescription.GetMetadata();
                    if (metadata.m_dateTime > mostRecentMetadata.m_dateTime)
                    {
                        mostRecent         = areaDescription;
                        mostRecentMetadata = metadata;
                    }
                }

                m_tangoApplication.Startup(mostRecent);
            }
            else
            {
                // No Area Descriptions available.
                Debug.Log("No area descriptions available.");
            }
        }
    }
    /// <summary>
    /// Actually do the Area Description save.
    /// </summary>
    /// <returns>Coroutine IEnumerator.</returns>
    private IEnumerator _DoSaveCurrentAreaDescription()
    {
        if (TouchScreenKeyboard.visible || m_saveThread != null)
        {
            yield break;
        }

        TouchScreenKeyboard kb = TouchScreenKeyboard.Open("Unnamed");

        while (!kb.done && !kb.wasCanceled)
        {
            yield return(null);
        }

        // Save the text in a background thread.
        m_savingTextParent.gameObject.SetActive(true);
        m_saveThread = new Thread(delegate()
        {
            // Save the name put in with the Area Description.
            AreaDescription areaDescription   = AreaDescription.SaveCurrent();
            AreaDescription.Metadata metadata = areaDescription.GetMetadata();
            metadata.m_name = kb.text;
            areaDescription.SaveMetadata(metadata);
        });
        m_saveThread.Start();
    }
Beispiel #16
0
        public SwaggerDocument GetSwagger(string rootUrl, string apiVersion, AreaDescription area, IList <AreaDescription> allAreas)
        {
            var areaName = area?.Name ?? string.Empty;
            var cacheKey = $"{rootUrl}_{apiVersion}_{areaName}";

            return(_cache.GetOrAdd(cacheKey, key => _swaggerProvider.GetSwagger(rootUrl, apiVersion, area, allAreas)));
        }
    /// <summary>
    /// Select a specific Area Description to show details.
    /// </summary>
    /// <param name="areaDescription">Area Description to show details for, or <c>null</c> if details should be hidden.</param>
    private void _SelectAreaDescription(AreaDescription areaDescription)
    {
        m_selectedAreaDescription = areaDescription;

        if (areaDescription != null)
        {
            m_selectedMetadata = areaDescription.GetMetadata();
            m_detailsParent.gameObject.SetActive(true);

            m_detailsDate.text = m_selectedMetadata.m_dateTime.ToLongDateString() + ", " + m_selectedMetadata.m_dateTime.ToLongTimeString();

            m_detailsEditableName.text  = m_selectedMetadata.m_name;
            m_detailsEditablePosX.text  = m_selectedMetadata.m_transformationPosition[0].ToString();
            m_detailsEditablePosY.text  = m_selectedMetadata.m_transformationPosition[1].ToString();
            m_detailsEditablePosZ.text  = m_selectedMetadata.m_transformationPosition[2].ToString();
            m_detailsEditableRotQX.text = m_selectedMetadata.m_transformationRotation[0].ToString();
            m_detailsEditableRotQY.text = m_selectedMetadata.m_transformationRotation[1].ToString();
            m_detailsEditableRotQZ.text = m_selectedMetadata.m_transformationRotation[2].ToString();
            m_detailsEditableRotQW.text = m_selectedMetadata.m_transformationRotation[3].ToString();
        }
        else
        {
            m_selectedMetadata = null;
            m_detailsParent.gameObject.SetActive(false);
        }
    }
Beispiel #18
0
        public static void SaveNameAs(this AreaDescription self, string name)
        {
            var metadata = self.GetMetadata();

            metadata.m_name = name;
            self.SaveMetadata(metadata);
        }
Beispiel #19
0
    /// <summary>
    /// From button press: start creating a mesh for occlusion.
    ///
    /// If an area description has been selected, use it and link it to the dynamic mesh.
    /// If no area description selected, create one while meshing.
    /// </summary>
    public void Button_CreateAreaDescriptionMesh()
    {
        m_3dReconstruction = true;

        // Enable the pose controller, but disable the AR screen.
        m_arPoseController.gameObject.SetActive(true);
        m_arPoseController.gameObject.GetComponent <TangoARScreen>().enabled = false;
        m_arPoseController.gameObject.GetComponent <MeshRenderer>().enabled  = false;

        // Need to enable depth to build the mesh.
        m_tangoApplication.m_enableDepth = true;

        // Set UI panel to the mesh construction panel.
        m_areaDescriptionLoaderPanel.SetActive(false);
        m_meshBuildPanel.SetActive(true);
        m_meshInteractionPanel.SetActive(false);

        // Initialize tango application and pose controller depending on whether area description has been selected.
        if (string.IsNullOrEmpty(m_savedUUID))
        {
            m_curAreaDescription = null;
            m_tangoApplication.m_areaDescriptionLearningMode = true;
            m_arPoseController.m_useAreaDescriptionPose      = false;
            m_relocalizeImage.gameObject.SetActive(false);
        }
        else
        {
            m_curAreaDescription = AreaDescription.ForUUID(m_savedUUID);
            m_tangoApplication.m_areaDescriptionLearningMode = false;
            m_arPoseController.m_useAreaDescriptionPose      = true;
            m_relocalizeImage.gameObject.SetActive(true);
        }

        m_tangoApplication.Startup(m_curAreaDescription);
    }
    /// <summary>
    /// Actually do the Area Description import.
    /// 
    /// This runs over multiple frames, so a Unity coroutine is used.
    /// </summary>
    /// <returns>Coroutine IEnumerator.</returns>
    private IEnumerator _DoImportAreaDescription()
	{#if UNITY_ANDROID 
        if (TouchScreenKeyboard.visible)
        {
            yield break;
        }
        
        TouchScreenKeyboard kb = TouchScreenKeyboard.Open("/sdcard/", TouchScreenKeyboardType.Default, false);
	
		while (!kb.done && !kb.wasCanceled)
        {
            yield return null;
        }
        
        if (kb.done)
        {
            AreaDescription.ImportFromFile(kb.text);
        }
		#else 
		yield return null;

		#endif


    }
    /// <summary>
    /// Actually do the Area Description save.
    /// </summary>
    /// <returns>Coroutine IEnumerator.</returns>
    private IEnumerator _DoSaveCurrentAreaDescription()
    {
#if UNITY_EDITOR
        // Work around lack of on-screen keyboard in editor:
        if (m_displayGuiTextInput || m_saveThread != null)
        {
            yield break;
        }

        m_displayGuiTextInput  = true;
        m_guiTextInputContents = "Unnamed";
        while (m_displayGuiTextInput)
        {
            yield return(null);
        }

        bool saveConfirmed = m_guiTextInputResult;
#else
        if (TouchScreenKeyboard.visible || m_saveThread != null)
        {
            yield break;
        }

        TouchScreenKeyboard kb = TouchScreenKeyboard.Open("Unnamed");
        while (!kb.done && !kb.wasCanceled)
        {
            yield return(null);
        }

        bool saveConfirmed = kb.done;
#endif
        if (saveConfirmed)
        {
            // Disable interaction before saving.
            m_initialized = false;
            m_savingText.gameObject.SetActive(true);
            if (m_tangoApplication.m_areaDescriptionLearningMode)
            {
                m_saveThread = new Thread(delegate()
                {
                    // Start saving process in another thread.
                    m_curAreaDescription = AreaDescription.SaveCurrent();
                    AreaDescription.Metadata metadata = m_curAreaDescription.GetMetadata();
#if UNITY_EDITOR
                    metadata.m_name = m_guiTextInputContents;
#else
                    metadata.m_name = kb.text;
#endif
                    m_curAreaDescription.SaveMetadata(metadata);
                });
                m_saveThread.Start();
            }
            else
            {
                _SaveMarkerToDisk();
                SceneManager.LoadScene(SceneManager.GetActiveScene().name);
            }
        }
    }
Beispiel #22
0
 // Fetch the list of ADFs from the device sorted by their name.
 IEnumerable <AreaDescription> GetAllSortedADFs()
 {
     // The list is null when permissions haven't been granted,
     // so in that case, proceed with an empty list.
     // The service procudes a warning, so we don't warn it here.
     return((AreaDescription.GetList() ?? new AreaDescription[0])
            .OrderByDescending(adf => adf.GetMetadata().m_dateTime));
 }
Beispiel #23
0
    /// <summary>
    /// From button press: start the game by loading the mesh and the Area Description.
    ///
    /// Generate a new mesh from the saved area definition mesh data linked to the selected Area Description.
    /// </summary>
    public void Button_StartAreaDescriptionMesh()
    {
        if (string.IsNullOrEmpty(m_savedUUID))
        {
            AndroidHelper.ShowAndroidToastMessage("Please choose an Area Description.");
            return;
        }

        if (!File.Exists(m_meshSavePath + "/" + m_savedUUID))
        {
            AndroidHelper.ShowAndroidToastMessage("Please choose an Area Description with mesh data.");
            return;
        }

        m_3dReconstruction = false;
        m_menuOpen         = false;

        // Enable objects needed to use Area Description and mesh for occlusion.
        m_arPoseController.gameObject.SetActive(true);
        m_arPoseController.m_useAreaDescriptionPose = true;

        // Disable unused components in tango application.
        m_tangoApplication.m_areaDescriptionLearningMode = false;
        m_tangoApplication.m_enableDepth = false;

        m_relocalizeImage.gameObject.SetActive(true);

        // Set UI panel to the mesh interaction panel.
        m_areaDescriptionLoaderPanel.SetActive(false);
        m_meshBuildPanel.SetActive(false);
        m_meshInteractionPanel.SetActive(true);

        // Load mesh.
        AreaDescriptionMesh mesh = _DeserializeAreaDescriptionMesh(m_savedUUID);

        if (mesh == null)
        {
            return;
        }

        // Create GameObject container with mesh components for the loaded mesh.
        m_meshFromFile = new GameObject();

        MeshFilter mf = m_meshFromFile.AddComponent <MeshFilter>();

        mf.mesh = _AreaDescriptionMeshToUnityMesh(mesh);

        MeshRenderer mr = m_meshFromFile.AddComponent <MeshRenderer>();

        mr.material = m_depthMaskMat;

        m_meshFromFile.AddComponent <MeshCollider>();

        // Load Area Description file.
        m_curAreaDescription = AreaDescription.ForUUID(m_savedUUID);

        m_tangoApplication.Startup(m_curAreaDescription);
    }
 /// <summary>
 /// Called every time an Area Description toggle changes state.
 /// </summary>
 /// <param name="areaDescription">Area Description the toggle is for.</param>
 /// <param name="value">The new state of the toggle.</param>
 private void _OnAreaDescriptionToggleChanged(AreaDescription areaDescription, bool value)
 {
     if (value)
     {
         _SelectAreaDescription(areaDescription);
     }
     else
     {
         _SelectAreaDescription(null);
     }
 }
Beispiel #25
0
    public void PressLoad(int pos)
    {
        SetScene();
        PressSceneMenu();
        AreaDescription desc = _AreaDescription [pos];

        TangoService.Instance.m_curAreaDescriptionUUID = desc.m_uuid;
        GameScene.Instance.StartTango(false);
        UIManager.Instance.Close(gameObject);
        UIManager.Instance.Open(UIID.Main);
        //desc.GetMetadata().
    }
    /// <summary>
    /// Thread method to save an Area Description and marker data.  Make this the ThreadFunc.
    /// </summary>
    /// <param name="xmlDataList">The marker's data list.</param>
    private void _AsyncDataSaving(List <MarkerData> xmlDataList)
    {
        if (xmlDataList == null)
        {
            Debug.Log("AndroidInGameController._AsyncDataSaving(): xmlDataList is null");
        }

        if (m_tangoApplication.m_enableAreaLearning)
        {
            m_curAreaDescription = AreaDescription.SaveCurrent();
        }
        _WriteToXml(m_curAreaDescription.m_uuid + ".xml", xmlDataList);
    }
Beispiel #27
0
    /// <summary>
    /// 保存区域描述.
    /// </summary>
    /// <returns>Coroutine IEnumerator.</returns>
    private IEnumerator _DoSaveCurrentAreaDescription()
    {
                #if UNITY_EDITOR
        // Work around lack of on-screen keyboard in editor:
        if (m_displayGuiTextInput || m_saveThread != null)
        {
            yield break;
        }

        m_displayGuiTextInput  = true;
        m_guiTextInputContents = "Unnamed";
        while (m_displayGuiTextInput)
        {
            yield return(null);
        }

        bool saveConfirmed = m_guiTextInputResult;
                #else
        if (TouchScreenKeyboard.visible || m_saveThread != null)
        {
            yield break;
        }

        TouchScreenKeyboard kb = TouchScreenKeyboard.Open("Unnamed");
        while (!kb.done && !kb.wasCanceled)
        {
            yield return(null);
        }

        bool saveConfirmed = kb.done;
                #endif
        // 保存.
        if (saveConfirmed)
        {
            // 保存前禁用交互.
            m_tangoReady = false;
            UIManager.Instance.ShowMessage("Saving...");
            if (m_tangoApplication.m_areaDescriptionLearningMode)             // 学习模式, 保存当前区域描述.
            {
                m_curAreaDescription = AreaDescription.SaveCurrent();
                AreaDescription.Metadata metadata = m_curAreaDescription.GetMetadata();
                                #if UNITY_EDITOR
                metadata.m_name = m_guiTextInputContents;
                                #else
                metadata.m_name = kb.text;
                                #endif
                m_curAreaDescription.SaveMetadata(metadata);
            }
            _SaveUnitToDisk();
        }
    }
    /// <summary>
    /// Actually do the Area Description save.
    /// </summary>
    /// <returns>Coroutine IEnumerator.</returns>
    private IEnumerator _DoSaveCurrentAreaDescription()
    {
#if UNITY_EDITOR
        // Work around lack of on-screen keyboard in editor:
        if (m_displayGuiTextInput || m_saveThread != null)
        {
            yield break;
        }

        m_displayGuiTextInput  = true;
        m_guiTextInputContents = "Unnamed";
        while (m_displayGuiTextInput)
        {
            yield return(null);
        }
#elif UNITY_ANDROID
        if (TouchScreenKeyboard.visible || m_saveThread != null)
        {
            yield break;
        }

        TouchScreenKeyboard kb = TouchScreenKeyboard.Open("Unnamed");
        while (!kb.done && !kb.wasCanceled)
        {
            yield return(null);
        }

        // Store name so it is available when we use it from thread delegate.
        var fileNameFromKeyboard = kb.text;
#endif
#if UNITY_ANDROID || UNITY_EDITOR
        // Save the text in a background thread.
        m_savingTextParent.gameObject.SetActive(true);
        m_saveThread = new Thread(delegate()
        {
            // Save the name put in with the Area Description.
            AreaDescription areaDescription   = AreaDescription.SaveCurrent();
            AreaDescription.Metadata metadata = areaDescription.GetMetadata();
#if UNITY_EDITOR
            metadata.m_name = m_guiTextInputContents;
#else
            metadata.m_name = fileNameFromKeyboard;
#endif
            areaDescription.SaveMetadata(metadata);
        });

        m_saveThread.Start();
#else
        yield return(null);
#endif
    }
    /// <summary>
    /// Called when entering a room (by creating or joining it). Called on all clients (including the Master Client).
    /// </summary>
    /// <remarks>This method is commonly used to instantiate player characters.
    /// If a match has to be started "actively", you can call an [PunRPC](@ref PhotonView.RPC) triggered by a user's
    /// button-press or a timer.
    ///
    /// When this is called, you can usually already access the existing players in the room via
    /// PhotonNetwork.playerList. Also, all custom properties should be already available as Room.customProperties.
    /// Check Room.playerCount to find out if enough players are in the room to start playing.</remarks>
    public override void OnJoinedRoom()
    {
#if UNITY_EDITOR
        m_tangoApplication.Startup(null);
        _StartPlayer();
#else
        if (Globals.m_curAreaDescription != null)
        {
            Directory.CreateDirectory(TEMP_FILE_PATH);
            Globals.m_curAreaDescription.ExportToFile(TEMP_FILE_PATH);
        }
#endif
        m_loadedAreaDescription = Globals.m_curAreaDescription;
    }
    /// <summary>
    /// This is called when the Area Description import operation completes.
    ///
    /// Please note that the Tango Service can only load Area Description file from internal storage.
    /// </summary>
    /// <param name="isSuccessful">If the import operation completed successfully.</param>
    /// <param name="areaDescription">The imported Area Description.</param>
    public void OnAreaDescriptionImported(bool isSuccessful, AreaDescription areaDescription)
    {
        if (!isSuccessful)
        {
            AndroidHelper.ShowAndroidToastMessage("Area Description import failed, unable to join game.");
            _QuitGame();
            return;
        }

        // Only non-master client will run this part of code.
        m_loadedAreaDescription = areaDescription;
        m_tangoApplication.Startup(m_loadedAreaDescription);
        _StartPlayer();
    }