HoloLens de primera generación y Azure (311): Microsoft Graph

Nota

Los tutoriales de Mixed Reality Academy se han diseñado teniendo en cuenta HoloLens (1.ª generación) y los cascos envolventes de realidad mixta. Por lo tanto, creemos que es importante conservar estos tutoriales para los desarrolladores que sigan buscando instrucciones sobre el desarrollo para esos dispositivos. Estos tutoriales no se actualizarán con los conjuntos de herramientas o las interacciones más recientes que se usan para HoloLens 2. Se mantendrán para que sigan funcionando en los dispositivos compatibles. Habrá una nueva serie de tutoriales que se publicarán en el futuro que demostrarán cómo desarrollar para HoloLens 2. Este aviso se actualizará con un vínculo a esos tutoriales cuando se publiquen.

En este curso, aprenderá a usar Microsoft Graph para iniciar sesión en su cuenta de Microsoft mediante la autenticación segura dentro de una aplicación de realidad mixta. A continuación, recuperará y mostrará las reuniones programadas en la interfaz de la aplicación.

Captura de pantalla que muestra las reuniones programadas en la interfaz de la aplicación.

Microsoft Graph es un conjunto de API diseñadas para permitir el acceso a muchos de los servicios de Microsoft. Microsoft describe Microsoft Graph como una matriz de recursos conectados por relaciones, lo que significa que una aplicación puede acceder a todo tipo de datos de usuario conectados. Para obtener más información, visite la página de Microsoft Graph.

El desarrollo incluirá la creación de una aplicación en la que se le indicará al usuario que mire y, a continuación, pulse una esfera, lo que pedirá al usuario que inicie sesión de forma segura en una cuenta microsoft. Una vez que haya iniciado sesión en su cuenta, el usuario podrá ver una lista de reuniones programadas para el día.

Después de completar este curso, tendrá una aplicación holoLens de realidad mixta, que podrá hacer lo siguiente:

  1. Con el gesto Pulsar, pulse en un objeto , que pedirá al usuario que inicie sesión en una cuenta Microsoft (saliendo de la aplicación para iniciar sesión y vuelva a la aplicación).
  2. Vea una lista de reuniones programadas para el día.

En la aplicación, le corresponde la forma en que integrará los resultados con el diseño. Este curso está diseñado para enseñar a integrar un servicio de Azure con el proyecto de Unity. Es su trabajo usar los conocimientos que obtiene de este curso para mejorar la aplicación de realidad mixta.

Compatibilidad con dispositivos

Curso HoloLens Cascos envolventes
Realidad mixta y Azure (311): Microsoft Graph ✔️

Prerrequisitos

Nota:

Este tutorial está diseñado para desarrolladores que tienen experiencia básica con Unity y C#. Tenga en cuenta también que los requisitos previos y las instrucciones escritas de este documento representan lo que se ha probado y comprobado en el momento de escribirlo (julio de 2018). Puede usar el software más reciente, como se muestra en el artículo de instalación de las herramientas , aunque no debe asumirse que la información de este curso coincidirá perfectamente con lo que encontrará en el software más reciente que lo que se muestra a continuación.

Se recomienda el siguiente hardware y software para este curso:

Antes de empezar

  1. Para evitar problemas al compilar este proyecto, se recomienda encarecidamente crear el proyecto mencionado en este tutorial en una carpeta raíz o casi raíz (las rutas de acceso de carpeta largas pueden causar problemas en tiempo de compilación).
  2. Configure y pruebe holoLens. Si necesita compatibilidad con la configuración de HoloLens, asegúrese de visitar el artículo de configuración de HoloLens.
  3. Es una buena idea realizar la calibración y la optimización del sensor al empezar a desarrollar una nueva aplicación de HoloLens (a veces puede ayudar a realizar esas tareas para cada usuario).

Para obtener ayuda sobre calibración, siga este vínculo al artículo Calibración de HoloLens.

Para obtener ayuda sobre la optimización de sensores, siga este vínculo al artículo Optimización del sensor de HoloLens.

Capítulo 1: Creación de la aplicación en el Portal de registro de aplicaciones

Para empezar, deberá crear y registrar la aplicación en el Portal de registro de aplicaciones.

En este capítulo también encontrará la clave de servicio que le permitirá realizar llamadas a Microsoft Graph para acceder al contenido de la cuenta.

  1. Vaya al Portal de registro de aplicaciones de Microsoft e inicie sesión con su cuenta Microsoft. Una vez que haya iniciado sesión, se le redirigirá al Portal de registro de aplicaciones.

  2. En la sección Mis aplicaciones , haga clic en el botón Agregar una aplicación.

    Captura de pantalla que muestra dónde seleccionar Agregar una aplicación.

    Importante

    El Portal de registro de aplicaciones puede ser diferente, en función de si ha trabajado anteriormente con Microsoft Graph. En las capturas de pantalla siguientes se muestran estas diferentes versiones.

  3. Agregue un nombre para la aplicación y haga clic en Crear.

    Captura de pantalla que muestra dónde agregar un nombre para la aplicación.

  4. Una vez creada la aplicación, se le redirigirá a la página principal de la aplicación. Copie el identificador de la aplicación y asegúrese de anotar este valor en algún lugar seguro, lo usará pronto en el código.

    Captura de pantalla que muestra dónde ver el identificador de la aplicación.

  5. En la sección Plataformas , asegúrese de que se muestra la aplicación nativa . Si no hace clic en Agregar plataforma y selecciona Aplicación nativa.

    Captura de pantalla que resalta la sección Aplicación nativa.

  6. Desplácese hacia abajo en la misma página y, en la sección denominada Permisos de Microsoft Graph , deberá agregar permisos adicionales para la aplicación. Haga clic en Agregar junto a Permisos delegados.

    Captura de pantalla que muestra dónde seleccionar Agregar junto a Permisos delegados.

  7. Puesto que quiere que la aplicación acceda al calendario del usuario, active la casilla calendars.Read y haga clic en Aceptar.

    Captura de pantalla que muestra la casilla Calendars.Read.

  8. Desplácese hasta la parte inferior y haga clic en el botón Guardar .

    Captura de pantalla que muestra dónde seleccionar Guardar.

  9. El guardado se confirmará y podrá cerrar sesión en el Portal de registro de aplicaciones.

Capítulo 2: Configuración del proyecto de Unity

A continuación se muestra una configuración típica para desarrollar con realidad mixta y, como tal, es una buena plantilla para otros proyectos.

  1. Abra Unity y haga clic en Nuevo.

    Captura de pantalla que muestra la interfaz de Unity.

  2. Debe proporcionar un nombre de proyecto de Unity. Inserte MSGraphMR. Asegúrese de que la plantilla de proyecto está establecida en 3D. Establezca la ubicación en algún lugar adecuado para usted (recuerde que más cerca de los directorios raíz es mejor). A continuación, haga clic en Crear proyecto.

    Captura de pantalla que muestra dónde seleccionar Crear proyecto.

  3. Con Unity abierto, vale la pena comprobar que el Editor de scripts predeterminado está establecido en Visual Studio. Vaya a Editar>preferencias y, a continuación, en la nueva ventana, vaya a Herramientas externas. Cambie el Editor de scripts externos a Visual Studio 2017. Cierre la ventana Preferencias.

    Captura de pantalla que muestra dónde establecer el Editor de scripts externos en Visual Studio 2017.

  4. Vaya a Configuración de compilación de archivos> y seleccione Plataforma universal de Windows y haga clic en el botón Cambiar plataforma para aplicar la selección.

    Captura de pantalla que muestra dónde seleccionar Cambiar plataforma.

  5. Mientras sigue enConfiguración de compilación de archivos>, asegúrese de que:

    1. El dispositivo de destino está establecido en HoloLens.

    2. El tipo de compilación se establece en D3D.

    3. El SDK se establece en Latest installed (Versión más reciente instalada)

    4. La versión de Visual Studio se establece en Latest installed (Versión más reciente instalada)

    5. Compilar y ejecutar está establecido en Equipo local

    6. Guarde la escena y agréguela a la compilación.

      1. Para ello, seleccione Agregar escenas abiertas. Aparecerá una ventana de guardado.

        Captura de pantalla que muestra dónde seleccionar Agregar escenas abiertas.

      2. Cree una carpeta para esto y cualquier escena futura. Seleccione el botón Nueva carpeta para crear una nueva carpeta, asígnela el nombre Scenes.

        Captura de pantalla que muestra dónde asignar un nombre a la nueva carpeta.

      3. Abra la carpeta Escenas recién creada y, a continuación, en el campo Nombre de archivo: texto, escriba MR_ComputerVisionScene y, a continuación, haga clic en Guardar.

        Captura de pantalla que muestra dónde escribir el nombre de archivo.

        Importante

        Tenga en cuenta que debe guardar las escenas de Unity en la carpeta Activos , ya que deben estar asociadas con el proyecto de Unity. La creación de la carpeta de escenas (y otras carpetas similares) es una manera típica de estructurar un proyecto de Unity.

    7. La configuración restante, en Configuración de compilación, debe dejarse como predeterminada por ahora.

  6. En la ventana Configuración de compilación, haga clic en el botón Configuración del reproductor ; se abrirá el panel relacionado en el espacio donde se encuentra el Inspector .

    Captura de pantalla que muestra el cuadro de diálogo Configuración del reproductor.

  7. En este panel, es necesario comprobar algunas opciones de configuración:

    1. En la pestaña Otros valores:

      1. La versión del entornode ejecución de scripting debe ser Experimental (equivalente a.NET 4.6), lo que desencadenará una necesidad de reiniciar el editor.

      2. El back-end de scripting debe ser .NET

      3. El nivel de compatibilidad de API debe ser .NET 4.6

        Captura de pantalla que muestra dónde comprobar el nivel de compatibilidad de la API.

    2. En la pestaña Configuración de publicación , en Funcionalidades, active:

      • InternetClient

        Captura de pantalla que muestra dónde seleccionar la opción InternetClient.

    3. Más abajo en el panel, en Configuración de XR (que se encuentra a continuación de Configuración de publicación), active Virtual Reality Supported (Compatible con Realidad virtual), asegúrese de que se agrega el SDK de Windows Mixed Reality.

      Captura de pantalla que muestra dónde agregar Windows Mixed Reality SDK.

  8. De nuevo en Configuración de compilación, los proyectos de C# de Unity ya no están atenuados; active la casilla situada junto a esta.

  9. Cierre la ventana Build Settings (Configuración de compilación).

  10. Guarde la escena y el proyecto (FILE>SAVE SCENES/FILE>SAVE PROJECT).

Capítulo 3: Importación de bibliotecas en Unity

Importante

Si desea omitir el componente De configuración de Unity de este curso y continuar directamente en el código, no dude en descargar este paquete azure-MR-311.unitypackage, importarlo en el proyecto como un paquete personalizado y, a continuación, continuar desde el capítulo 5.

Para usar Microsoft Graph en Unity, debe usar el archivo DLL Microsoft.Identity.Client . Sin embargo, es posible usar el SDK de Microsoft Graph; sin embargo, requerirá la adición de un paquete NuGet después de compilar el proyecto de Unity (lo que significa editar el proyecto después de la compilación). Se considera más sencillo importar los archivos DLL necesarios directamente en Unity.

Nota

Actualmente hay un problema conocido en Unity que requiere que se vuelvan a configurar los complementos después de la importación. Estos pasos (4 - 7 en esta sección) ya no serán necesarios después de que se haya resuelto el error.

Para importar Microsoft Graph en su propio proyecto, descargue el archivo MSGraph_LabPlugins.zip. Este paquete se ha creado con versiones de las bibliotecas que se han probado.

Si desea obtener más información sobre cómo agregar archivos DLL personalizados al proyecto de Unity, siga este vínculo.

Para importar el paquete:

  1. Agregue el paquete de Unity a Unity mediante la opciónde > menúImportar paquete>personalizado de paquete de recursos. Seleccione el paquete que acaba de descargar.

  2. En el cuadro Importar paquete de Unity que aparece, asegúrese de que está seleccionado todo en Complementos (e incluidos).

    Captura de pantalla que muestra los parámetros de configuración seleccionados en Complementos.

  3. Haga clic en el botón Importar para agregar los elementos al proyecto.

  4. Vaya a la carpeta MSGraph en Complementos en el Panel de proyectos y seleccione el complemento llamado Microsoft.Identity.Client.

    Captura de pantalla que muestra el complemento Microsoft.Identity.Client.

  5. Con el complemento seleccionado, asegúrese de que Cualquier plataforma está desactivada y, a continuación, asegúrese de que WSAPlayer también esté desactivada y, a continuación, haga clic en Aplicar. Esto es solo para confirmar que los archivos están configurados correctamente.

    Captura de pantalla en la que se muestra dónde confirmar que no se comprueban Any Platform y WSAPlayer.

    Nota

    Marcar estos complementos los configura para que solo se usen en el Editor de Unity. Hay un conjunto diferente de archivos DLL en la carpeta WSA que se usará después de exportar el proyecto desde Unity como una aplicación universal de Windows.

  6. A continuación, debe abrir la carpeta WSA , dentro de la carpeta MSGraph . Verá una copia del mismo archivo que acaba de configurar. Seleccione el archivo y, a continuación, en el inspector:

    • asegúrese de que Cualquier plataforma está desactivada y que solo se activaWSAPlayer.

    • Asegúrese de que el SDK está establecido en UWP y el back-end de scripting está establecido en Dot Net.

    • Asegúrese de que No procesar está activado.

      Captura de pantalla que muestra que No procesar está seleccionado.

  7. Haga clic en Aplicar.

Capítulo 4: Configuración de la cámara

Durante este capítulo configurará la cámara principal de la escena:

  1. En el Panel jerarquía, seleccione la Cámara principal.

  2. Una vez seleccionado, podrá ver todos los componentes de la cámara principal en el panel Inspector .

    1. El objeto Camera debe denominarse Cámara principal (tenga en cuenta la ortografía).

    2. La etiqueta de cámara principal debe establecerse en MainCamera (tenga en cuenta la ortografía).

    3. Asegúrese de que la posición de transformación está establecida en 0, 0, 0

    4. Establecer marcas de borrar en color sólido

    5. Establezca el color de fondo del componente de cámara en Negro, Alfa 0(Código hexadecimal: #000000000)

      Captura de pantalla que resalta dónde establecer el color de fondo.

  3. La estructura final del objeto en el Panel de jerarquía debe ser similar a la que se muestra en la imagen siguiente:

    Captura de pantalla que muestra la estructura de objetos final en el Panel de jerarquía.

Capítulo 5: Crear clase MeetingsUI

El primer script que necesita crear es MeetingsUI, que es responsable de hospedar y rellenar la interfaz de usuario de la aplicación (mensaje de bienvenida, instrucciones y los detalles de las reuniones).

Para crear esta clase:

  1. Haga clic con el botón derecho en la carpeta Activos en el Panel de proyectos y seleccione Crear>carpeta. Asigne un nombre a la carpeta Scripts.

    Captura de pantalla que muestra dónde buscar la carpeta Assets.Captura de pantalla que muestra dónde crear la carpeta Scripts.

  2. Abra la carpeta Scripts y, después, en esa carpeta, haga clic con el botón derecho en Crear>script de C#. Asigne al script el nombre MeetingsUI.

    Captura de pantalla que muestra dónde crear la carpeta MeetingsUI.

  3. Haga doble clic en el nuevo script MeetingsUI para abrirlo con Visual Studio.

  4. Inserte los siguientes espacios de nombres:

    using System;
    using UnityEngine;
    
  5. Dentro de la clase, inserte las siguientes variables:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static MeetingsUI Instance;
    
        /// <summary>
        /// The 3D text of the scene
        /// </summary>
        private TextMesh _meetingDisplayTextMesh;
    
  6. A continuación, reemplace el método Start() y agregue un método Awake(). Se llamará cuando se inicialice la clase:

        /// <summary>
        /// Called on initialization
        /// </summary>
        void Awake()
        {
            Instance = this;
        }
    
        /// <summary>
        /// Called on initialization, after Awake
        /// </summary>
        void Start ()
        {
            // Creating the text mesh within the scene
            _meetingDisplayTextMesh = CreateMeetingsDisplay();
        }
    
  7. Agregue los métodos responsables de crear la interfaz de usuario de reuniones y rellenarlo con las reuniones actuales cuando se solicite:

        /// <summary>
        /// Set the welcome message for the user
        /// </summary>
        internal void WelcomeUser(string userName)
        {
            if(!string.IsNullOrEmpty(userName))
            {
                _meetingDisplayTextMesh.text = $"Welcome {userName}";
            }
            else 
            {
                _meetingDisplayTextMesh.text = "Welcome";
            }
        }
    
        /// <summary>
        /// Set up the parameters for the UI text
        /// </summary>
        /// <returns>Returns the 3D text in the scene</returns>
        private TextMesh CreateMeetingsDisplay()
        {
            GameObject display = new GameObject();
            display.transform.localScale = new Vector3(0.03f, 0.03f, 0.03f);
            display.transform.position = new Vector3(-3.5f, 2f, 9f);
            TextMesh textMesh = display.AddComponent<TextMesh>();
            textMesh.anchor = TextAnchor.MiddleLeft;
            textMesh.alignment = TextAlignment.Left;
            textMesh.fontSize = 80;
            textMesh.text = "Welcome! \nPlease gaze at the button" +
                "\nand use the Tap Gesture to display your meetings";
    
            return textMesh;
        }
    
        /// <summary>
        /// Adds a new Meeting in the UI by chaining the existing UI text
        /// </summary>
        internal void AddMeeting(string subject, DateTime dateTime, string location)
        {
            string newText = $"\n{_meetingDisplayTextMesh.text}\n\n Meeting,\nSubject: {subject},\nToday at {dateTime},\nLocation: {location}";
    
            _meetingDisplayTextMesh.text = newText;
        }
    
  8. Elimine el método Update() y guarde los cambios en Visual Studio antes de volver a Unity.

Capítulo 6: Creación de la clase Graph

El siguiente script que se va a crear es el script graph . Este script es responsable de realizar las llamadas para autenticar al usuario y recuperar las reuniones programadas para el día actual del calendario del usuario.

Para crear esta clase:

  1. Haga doble clic en la carpeta Scripts para abrirla.

  2. Haga clic con el botón derecho en la carpeta Scripts y haga clic en Crear>script de C#. Asigne al script el nombre Graph.

  3. Haga doble clic en el script para abrirlo con Visual Studio.

  4. Inserte los siguientes espacios de nombres:

    using System.Collections.Generic;
    using UnityEngine;
    using Microsoft.Identity.Client;
    using System;
    using System.Threading.Tasks;
    
    #if !UNITY_EDITOR && UNITY_WSA
    using System.Net.Http;
    using System.Net.Http.Headers;
    using Windows.Storage;
    #endif
    

    Importante

    Observará que las partes del código de este script se encapsulan en torno a directivas de precompilación; esto es para evitar problemas con las bibliotecas al compilar la solución de Visual Studio.

  5. Elimine los métodos Start() y Update(), ya que no se usarán.

  6. Fuera de la clase Graph , inserte los siguientes objetos, que son necesarios para deserializar el objeto JSON que representa las reuniones programadas diarias:

    /// <summary>
    /// The object hosting the scheduled meetings
    /// </summary>
    [Serializable]
    public class Rootobject
    {
        public List<Value> value;
    }
    
    [Serializable]
    public class Value
    {
        public string subject { get; set; }
        public StartTime start { get; set; }
        public Location location { get; set; }
    }
    
    [Serializable]
    public class StartTime
    {
        public string dateTime;
    
        private DateTime? _startDateTime;
        public DateTime StartDateTime
        {
            get
            {
                if (_startDateTime != null)
                    return _startDateTime.Value;
                DateTime dt;
                DateTime.TryParse(dateTime, out dt);
                _startDateTime = dt;
                return _startDateTime.Value;
            }
        }
    }
    
    [Serializable]
    public class Location
    {
        public string displayName { get; set; }
    }
    
  7. Dentro de la clase Graph , agregue las siguientes variables:

        /// <summary>
        /// Insert your Application Id here
        /// </summary>
        private string _appId = "-- Insert your Application Id here --";
    
        /// <summary>
        /// Application scopes, determine Microsoft Graph accessibility level to user account
        /// </summary>
        private IEnumerable<string> _scopes = new List<string>() { "User.Read", "Calendars.Read" };
    
        /// <summary>
        /// Microsoft Graph API, user reference
        /// </summary>
        private PublicClientApplication _client;
    
        /// <summary>
        /// Microsoft Graph API, authentication
        /// </summary>
        private AuthenticationResult _authResult;
    
    

    Nota

    Cambie el valor de appId para que sea el identificador de aplicación que anotó en el capítulo 1, paso 4. Este valor debe ser el mismo que se muestra en el Portal de registro de aplicaciones, en la página de registro de la aplicación.

  8. En la clase Graph , agregue los métodos SignInAsync() y AquireTokenAsync() que pedirán al usuario que inserte las credenciales de inicio de sesión.

        /// <summary>
        /// Begin the Sign In process using Microsoft Graph Library
        /// </summary>
        internal async void SignInAsync()
        {
    #if !UNITY_EDITOR && UNITY_WSA
            // Set up Grap user settings, determine if needs auth
            ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
            string userId = localSettings.Values["UserId"] as string;
            _client = new PublicClientApplication(_appId);
    
            // Attempt authentication
            _authResult = await AcquireTokenAsync(_client, _scopes, userId);
    
            // If authentication is successful, retrieve the meetings
            if (!string.IsNullOrEmpty(_authResult.AccessToken))
            {
                // Once Auth as been completed, find the meetings for the day
                await ListMeetingsAsync(_authResult.AccessToken);
            }
    #endif
        }
    
        /// <summary>
        /// Attempt to retrieve the Access Token by either retrieving
        /// previously stored credentials or by prompting user to Login
        /// </summary>
        private async Task<AuthenticationResult> AcquireTokenAsync(
            IPublicClientApplication app, IEnumerable<string> scopes, string userId)
        {
            IUser user = !string.IsNullOrEmpty(userId) ? app.GetUser(userId) : null;
            string userName = user != null ? user.Name : "null";
    
            // Once the User name is found, display it as a welcome message
            MeetingsUI.Instance.WelcomeUser(userName);
    
            // Attempt to Log In the user with a pre-stored token. Only happens
            // in case the user Logged In with this app on this device previously
            try
            {
                _authResult = await app.AcquireTokenSilentAsync(scopes, user);
            }
            catch (MsalUiRequiredException)
            {
                // Pre-stored token not found, prompt the user to log-in 
                try
                {
                    _authResult = await app.AcquireTokenAsync(scopes);
                }
                catch (MsalException msalex)
                {
                    Debug.Log($"Error Acquiring Token: {msalex.Message}");
                    return _authResult;
                }
            }
    
            MeetingsUI.Instance.WelcomeUser(_authResult.User.Name);
    
    #if !UNITY_EDITOR && UNITY_WSA
            ApplicationData.Current.LocalSettings.Values["UserId"] = 
            _authResult.User.Identifier;
    #endif
            return _authResult;
        }
    
  9. Agregue los dos métodos siguientes:

    1. BuildTodayCalendarEndpoint(), que compila el URI que especifica el día y el intervalo de tiempo, en el que se recuperan las reuniones programadas.

    2. ListMeetingsAsync(), que solicita las reuniones programadas de Microsoft Graph.

        /// <summary>
        /// Build the endpoint to retrieve the meetings for the current day.
        /// </summary>
        /// <returns>Returns the Calendar Endpoint</returns>
        public string BuildTodayCalendarEndpoint()
        {
            DateTime startOfTheDay = DateTime.Today.AddDays(0);
            DateTime endOfTheDay = DateTime.Today.AddDays(1);
            DateTime startOfTheDayUTC = startOfTheDay.ToUniversalTime();
            DateTime endOfTheDayUTC = endOfTheDay.ToUniversalTime();
    
            string todayDate = startOfTheDayUTC.ToString("o");
            string tomorrowDate = endOfTheDayUTC.ToString("o");
            string todayCalendarEndpoint = string.Format(
                "https://graph.microsoft.com/v1.0/me/calendarview?startdatetime={0}&enddatetime={1}",
                todayDate,
                tomorrowDate);
    
            return todayCalendarEndpoint;
        }
    
        /// <summary>
        /// Request all the scheduled meetings for the current day.
        /// </summary>
        private async Task ListMeetingsAsync(string accessToken)
        {
    #if !UNITY_EDITOR && UNITY_WSA
            var http = new HttpClient();
    
            http.DefaultRequestHeaders.Authorization = 
            new AuthenticationHeaderValue("Bearer", accessToken);
            var response = await http.GetAsync(BuildTodayCalendarEndpoint());
    
            var jsonResponse = await response.Content.ReadAsStringAsync();
    
            Rootobject rootObject = new Rootobject();
            try
            {
                // Parse the JSON response.
                rootObject = JsonUtility.FromJson<Rootobject>(jsonResponse);
    
                // Sort the meeting list by starting time.
                rootObject.value.Sort((x, y) => DateTime.Compare(x.start.StartDateTime, y.start.StartDateTime));
    
                // Populate the UI with the meetings.
                for (int i = 0; i < rootObject.value.Count; i++)
                {
                    MeetingsUI.Instance.AddMeeting(rootObject.value[i].subject,
                                                rootObject.value[i].start.StartDateTime.ToLocalTime(),
                                                rootObject.value[i].location.displayName);
                }
            }
            catch (Exception ex)
            {
                Debug.Log($"Error = {ex.Message}");
                return;
            }
    #endif
        }
    
  10. Ya ha completado el script de Graph . Guarde los cambios en Visual Studio antes de volver a Unity.

Capítulo 7: Creación del script GazeInput

Ahora creará gazeInput. Esta clase controla y realiza un seguimiento de la mirada del usuario, usando un Raycast procedente de la cámara principal, proyectando hacia delante.

Para crear el script:

  1. Haga doble clic en la carpeta Scripts para abrirla.

  2. Haga clic con el botón derecho en la carpeta Scripts y haga clic en Crear>script de C#. Asigne al script el nombre GazeInput.

  3. Haga doble clic en el script para abrirlo con Visual Studio.

  4. Cambie el código de los espacios de nombres para que coincida con el siguiente, junto con la adición de la etiqueta '[System.Serializable]' encima de la clase GazeInput , para que se pueda serializar:

    using UnityEngine;
    
    /// <summary>
    /// Class responsible for the User's Gaze interactions
    /// </summary>
    [System.Serializable]
    public class GazeInput : MonoBehaviour
    {
    
  5. Dentro de la clase GazeInput , agregue las siguientes variables:

        [Tooltip("Used to compare whether an object is to be interacted with.")]
        internal string InteractibleTag = "SignInButton";
    
        /// <summary>
        /// Length of the gaze
        /// </summary>
        internal float GazeMaxDistance = 300;
    
        /// <summary>
        /// Object currently gazed
        /// </summary>
        internal GameObject FocusedObject { get; private set; }
    
        internal GameObject oldFocusedObject { get; private set; }
    
        internal RaycastHit HitInfo { get; private set; }
    
        /// <summary>
        /// Cursor object visible in the scene
        /// </summary>
        internal GameObject Cursor { get; private set; }
    
        internal bool Hit { get; private set; }
    
        internal Vector3 Position { get; private set; }
    
        internal Vector3 Normal { get; private set; }
    
        private Vector3 _gazeOrigin;
    
        private Vector3 _gazeDirection;
    
  6. Agregue el método CreateCursor() para crear el cursor de HoloLens en la escena y llame al método desde el método Start():

        /// <summary>
        /// Start method used upon initialisation.
        /// </summary>
        internal virtual void Start()
        {
            FocusedObject = null;
            Cursor = CreateCursor();
        }
    
        /// <summary>
        /// Method to create a cursor object.
        /// </summary>
        internal GameObject CreateCursor()
        {
            GameObject newCursor = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            newCursor.SetActive(false);
            // Remove the collider, so it doesn't block raycast.
            Destroy(newCursor.GetComponent<SphereCollider>());
            newCursor.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
            Material mat = new Material(Shader.Find("Diffuse"));
            newCursor.GetComponent<MeshRenderer>().material = mat;
            mat.color = Color.HSVToRGB(0.0223f, 0.7922f, 1.000f);
            newCursor.SetActive(true);
    
            return newCursor;
        }
    
  7. Los métodos siguientes habilitan la mirada Raycast y realizan un seguimiento de los objetos centrados.

    /// <summary>
    /// Called every frame
    /// </summary>
    internal virtual void Update()
    {
        _gazeOrigin = Camera.main.transform.position;
    
        _gazeDirection = Camera.main.transform.forward;
    
        UpdateRaycast();
    }
    /// <summary>
    /// Reset the old focused object, stop the gaze timer, and send data if it
    /// is greater than one.
    /// </summary>
    private void ResetFocusedObject()
    {
        // Ensure the old focused object is not null.
        if (oldFocusedObject != null)
        {
            if (oldFocusedObject.CompareTag(InteractibleTag))
            {
                // Provide the 'Gaze Exited' event.
                oldFocusedObject.SendMessage("OnGazeExited", SendMessageOptions.DontRequireReceiver);
            }
        }
    }
    
        private void UpdateRaycast()
        {
            // Set the old focused gameobject.
            oldFocusedObject = FocusedObject;
            RaycastHit hitInfo;
    
            // Initialise Raycasting.
            Hit = Physics.Raycast(_gazeOrigin,
                _gazeDirection,
                out hitInfo,
                GazeMaxDistance);
                HitInfo = hitInfo;
    
            // Check whether raycast has hit.
            if (Hit == true)
            {
                Position = hitInfo.point;
                Normal = hitInfo.normal;
    
                // Check whether the hit has a collider.
                if (hitInfo.collider != null)
                {
                    // Set the focused object with what the user just looked at.
                    FocusedObject = hitInfo.collider.gameObject;
                }
                else
                {
                    // Object looked on is not valid, set focused gameobject to null.
                    FocusedObject = null;
                }
            }
            else
            {
                // No object looked upon, set focused gameobject to null.
                FocusedObject = null;
    
                // Provide default position for cursor.
                Position = _gazeOrigin + (_gazeDirection * GazeMaxDistance);
    
                // Provide a default normal.
                Normal = _gazeDirection;
            }
    
            // Lerp the cursor to the given position, which helps to stabilize the gaze.
            Cursor.transform.position = Vector3.Lerp(Cursor.transform.position, Position, 0.6f);
    
            // Check whether the previous focused object is this same. If so, reset the focused object.
            if (FocusedObject != oldFocusedObject)
            {
                ResetFocusedObject();
                if (FocusedObject != null)
                {
                    if (FocusedObject.CompareTag(InteractibleTag))
                    {
                        // Provide the 'Gaze Entered' event.
                        FocusedObject.SendMessage("OnGazeEntered", 
                            SendMessageOptions.DontRequireReceiver);
                    }
                }
            }
        }
    
  8. Guarde los cambios en Visual Studio antes de volver a Unity.

Capítulo 8: Creación de la clase Interactions

Ahora tendrá que crear el script interacciones , que es responsable de:

  • Controlar la interacción pulsar y la mirada de la cámara, lo que permite al usuario interactuar con el "botón" de inicio de sesión en la escena.

  • Crear el objeto "botón" de inicio de sesión en la escena con el que el usuario interactúe.

Para crear el script:

  1. Haga doble clic en la carpeta Scripts para abrirla.

  2. Haga clic con el botón derecho en la carpeta Scripts y haga clic en Crear>script de C#. Asigne un nombre a las interacciones del script.

  3. Haga doble clic en el script para abrirlo con Visual Studio.

  4. Inserte los siguientes espacios de nombres:

    using UnityEngine;
    using UnityEngine.XR.WSA.Input;
    
  5. Cambie la herencia de la clase Interaction de MonoBehaviour a GazeInput.

    interacciones de clase pública: MonoBehaviour

    public class Interactions : GazeInput
    
  6. Dentro de la clase Interaction , inserte la variable siguiente:

        /// <summary>
        /// Allows input recognition with the HoloLens
        /// </summary>
        private GestureRecognizer _gestureRecognizer;
    
  7. Reemplace el método Start ; observe que es un método de invalidación, que llama al método de clase Gaze "base". Se llamará a Start() cuando la clase se inicialice, registrando para el reconocimiento de entrada y creando el botón de inicio de sesión en la escena:

        /// <summary>
        /// Called on initialization, after Awake
        /// </summary>
        internal override void Start()
        {
            base.Start();
    
            // Register the application to recognize HoloLens user inputs
            _gestureRecognizer = new GestureRecognizer();
            _gestureRecognizer.SetRecognizableGestures(GestureSettings.Tap);
            _gestureRecognizer.Tapped += GestureRecognizer_Tapped;
            _gestureRecognizer.StartCapturingGestures();
    
            // Add the Graph script to this object
            gameObject.AddComponent<MeetingsUI>();
            CreateSignInButton();
        }
    
  8. Agregue el método CreateSignInButton(), que creará una instancia del botón de inicio de sesión en la escena y establecerá sus propiedades:

        /// <summary>
        /// Create the sign in button object in the scene
        /// and sets its properties
        /// </summary>
        void CreateSignInButton()
        {
            GameObject signInButton = GameObject.CreatePrimitive(PrimitiveType.Sphere);
    
            Material mat = new Material(Shader.Find("Diffuse"));
            signInButton.GetComponent<Renderer>().material = mat;
            mat.color = Color.blue;
    
            signInButton.transform.position = new Vector3(3.5f, 2f, 9f);
            signInButton.tag = "SignInButton";
            signInButton.AddComponent<Graph>();
        }
    
  9. Agregue el método GestureRecognizer_Tapped(), que se responderá para el evento Tap user.

        /// <summary>
        /// Detects the User Tap Input
        /// </summary>
        private void GestureRecognizer_Tapped(TappedEventArgs obj)
        {
            if(base.FocusedObject != null)
            {
                Debug.Log($"TAP on {base.FocusedObject.name}");
                base.FocusedObject.SendMessage("SignInAsync", SendMessageOptions.RequireReceiver);
            }
        }
    
  10. Elimine el método Update() y guarde los cambios en Visual Studio antes de volver a Unity.

Capítulo 9: Configuración de las referencias de script

En este capítulo, debe colocar el script interacciones en la cámara principal. Después, ese script controlará la colocación de los otros scripts donde deben estar.

  • En la carpeta Scripts del Panel de proyectos, arrastre las interacciones del script al objeto Cámara principal , como se muestra a continuación.

    Captura de pantalla que muestra dónde arrastrar el script interacciones.

Capítulo 10: Configuración de la etiqueta

El código que controla la mirada usará tag SignInButton para identificar con qué objeto interactuará el usuario para iniciar sesión en Microsoft Graph.

Para crear la etiqueta :

  1. En el Editor de Unity, haga clic en la cámara principal en el Panel de jerarquía.

  2. En el Panel inspector, haga clic en la etiquetaMainCamera para abrir una lista desplegable. Haga clic en Agregar etiqueta...

    Captura de pantalla que resalta la opción Agregar etiqueta... Opción.

  3. Haga clic en el botón +.

    Captura de pantalla que muestra el botón + .

  4. Escriba el nombre de etiqueta como SignInButton y haga clic en Guardar.

    Captura de pantalla que muestra dónde agregar el nombre de la etiqueta SignInButton.

Capítulo 11: Compilación del proyecto de Unity en UWP

Todo lo necesario para la sección unity de este proyecto ya se ha completado, por lo que es el momento de compilarlo desde Unity.

  1. Vaya a Configuración de compilación (Configuración de compilaciónde archivos>).

    Captura de pantalla que muestra el cuadro de diálogo Configuración de compilación.

  2. Si aún no es así, marque Proyectos de C# de Unity.

  3. Haga clic en Generar. Unity iniciará una ventana de Explorador de archivos, donde debe crear y, a continuación, seleccionará una carpeta en la que compilará la aplicación. Cree esa carpeta ahora y asígnela el nombre App. A continuación, con la carpeta Aplicación seleccionada, haga clic en Seleccionar carpeta.

  4. Unity comenzará a compilar el proyecto en la carpeta Aplicación .

  5. Una vez que Unity haya terminado de compilar (es posible que tarde algún tiempo), abrirá una ventana de Explorador de archivos en la ubicación de la compilación (compruebe la barra de tareas, ya que puede que no siempre aparezca encima de las ventanas, pero le notificará la adición de una nueva ventana).

Capítulo 12: Implementación en HoloLens

Para implementar en HoloLens:

  1. Necesitará la dirección IP de HoloLens (para implementación remota) y para asegurarse de que HoloLens está en modo de desarrollador. Para ello:

    1. Mientras llevas tu HoloLens, abre la configuración.

    2. Vaya a Red &Opciones avanzadasde Wi-Fi para> Internet >

    3. Anote la dirección IPv4 .

    4. A continuación, vuelva a Configuración y, a continuación, a Actualizar & Seguridad>para desarrolladores.

    5. Establezca el modo de desarrollador activado.

  2. Vaya a la nueva compilación de Unity (la carpeta Aplicación ) y abra el archivo de solución con Visual Studio.

  3. En Configuración de la solución , seleccione Depurar.

  4. En la Plataforma de soluciones, seleccione x86, Máquina remota. Se le pedirá que inserte la dirección IP de un dispositivo remoto (holoLens, en este caso, que anotó).

    Captura de pantalla que muestra dónde seleccionar x86 y Equipo remoto.

  5. Vaya al menú Compilar y haga clic en Implementar solución para transferir localmente la aplicación a HoloLens.

  6. La aplicación ahora debería aparecer en la lista de aplicaciones instaladas en HoloLens, listos para iniciarse.

La aplicación HoloLens de Microsoft Graph

Enhorabuena, ha creado una aplicación de realidad mixta que aprovecha Microsoft Graph para leer y mostrar los datos del calendario de usuario.

Captura de pantalla que muestra la aplicación de realidad mixta completada.

Ejercicios extra

Ejercicio 1

Usar Microsoft Graph para mostrar otra información sobre el usuario

  • Correo electrónico de usuario/ número de teléfono/imagen de perfil

Ejercicio 1

Implemente el control de voz para navegar por la interfaz de usuario de Microsoft Graph.