HoloLens (1.ª generación) y Azure 305: Funciones y almacenamiento


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.


final product -start

En este curso, aprenderá a crear y usar Azure Functions y almacenar datos con un recurso de Azure Storage, dentro de una aplicación de realidad mixta.

Azure Functions es un servicio de Microsoft, que permite a los desarrolladores ejecutar pequeños fragmentos de código, "funciones", en Azure. Esto proporciona una manera de delegar el trabajo en la nube, en lugar de la aplicación local, lo que puede tener muchas ventajas. Azure Functions admite varios lenguajes de desarrollo, como C#, F#, Node.js, Java y PHP. Para obtener más información, visite el artículo Azure Functions.

Azure Storage es un servicio en la nube de Microsoft, que permite a los desarrolladores almacenar datos, con el seguro de que estará altamente disponible, seguro, duradero, escalable y redundante. Esto significa que Microsoft controlará todo el mantenimiento y los problemas críticos para usted. Para más información, visite el artículo de Azure Storage.

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

  1. Permitir al usuario mirar alrededor de una escena.
  2. Desencadene la creación de objetos cuando el usuario mira en un "botón" 3D.
  3. Una función de Azure elegirá los objetos generados.
  4. A medida que se genera cada objeto, la aplicación almacenará el tipo de objeto en un archivo de Azure, ubicado en Azure Storage.
  5. Al cargar una segunda vez, se recuperarán los datos de Azure File y se usarán para reproducir las acciones de desove de la instancia anterior de la aplicación.

En la aplicación, dependerá de usted cómo 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 (305): Funciones y almacenamiento ✔️ ✔️

Nota

Aunque este curso se centra principalmente en Windows Mixed Reality cascos envolventes (VR), también puede aplicar lo que aprende en este curso para Microsoft HoloLens. A medida que sigas con el curso, verás notas sobre los cambios que puedas necesitar para admitir HoloLens.

Requisitos previos

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 dentro de este documento representan lo que se ha probado y comprobado en el momento de la escritura (mayo de 2018). Puede usar el software más reciente, como se muestra en el artículo sobre la 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

Para evitar encontrar 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 carpetas largas pueden causar problemas en tiempo de compilación).

Capítulo 1: Azure Portal

Para usar el servicio Azure Storage, deberá crear y configurar una cuenta de almacenamiento en el Azure Portal.

  1. Inicie sesión en Azure Portal.

    Nota

    Si aún no tiene una cuenta de Azure, deberá crear una. Si sigue este tutorial en una situación de clase o laboratorio, pida a su instructor o a uno de los proctores que le ayuden a configurar la nueva cuenta.

  2. Una vez que haya iniciado sesión, haga clic en Nuevo en la esquina superior izquierda y busque Cuenta de almacenamiento y haga clic en Entrar.

    Azure Storage Search

    Nota

    Es posible que la palabra New se haya reemplazado por Create a resource (Crear un recurso) en portales más recientes.

  3. La nueva página proporcionará una descripción del servicio de cuenta de Azure Storage . En la parte inferior izquierda de este mensaje, seleccione el botón Crear para crear una asociación con este servicio.

    crear servicio

  4. Una vez que haya hecho clic en Crear:

    1. Inserte un nombre para su cuenta, tenga en cuenta que este campo solo acepta números y letras minúsculas.

    2. En Modelo de implementación, seleccione Resource Manager.

    3. En Tipo de cuenta, seleccione Almacenamiento (uso general v1).

    4. Determine la ubicación del grupo de recursos (si va a crear un nuevo grupo de recursos). La ubicación ideal sería en la región donde se ejecutaría la aplicación. Algunos recursos de Azure solo están disponibles en determinadas regiones.

    5. En Replicación , seleccione Read-access-geo-redundant storage (RA-GRS) (Almacenamiento con redundancia geográfica con acceso de lectura [RA-GRS]).

    6. En Rendimiento, seleccione Estándar.

    7. Deje Transferencia segura necesaria como Deshabilitada.

    8. Seleccione una opción en Suscripción.

    9. Elija un grupo de recursos o cree uno nuevo. Un grupo de recursos proporciona una manera de supervisar, controlar el acceso, aprovisionar y administrar la facturación de una colección de recursos de Azure. Se recomienda mantener todos los servicios de Azure asociados a un único proyecto (por ejemplo, estos laboratorios) en un grupo de recursos común).

      Si desea obtener más información sobre los grupos de recursos de Azure, visite el artículo sobre el grupo de recursos.

    10. También deberá confirmar que ha comprendido los Términos y Condiciones aplicados a este Servicio.

    11. Seleccione Crear.

      información de servicio de entrada

  5. Una vez que haya hecho clic en Crear, tendrá que esperar a que se cree el servicio, esto puede tardar un minuto.

  6. Una notificación aparecerá en el portal una vez creada la instancia de servicio.

    nueva notificación en Azure Portal

  7. Haga clic en las notificaciones para explorar la nueva instancia de servicio.

    Ir al recurso

  8. Haga clic en el botón Ir al recurso en la notificación para explorar la nueva instancia de servicio. Se le llevará a la nueva instancia de servicio de la cuenta de almacenamiento .

    claves de acceso

  9. Haga clic en Claves de acceso para mostrar los puntos de conexión de este servicio en la nube. Use el Bloc de notas o similar para copiar una de las claves para usarlas más adelante. Además, tenga en cuenta el valor de Cadena de conexión , ya que se usará en la clase AzureServices , que creará más adelante.

    copiar cadena de conexión

Capítulo 2: Configuración de una función de Azure

Ahora escribirá una función de Azure en el servicio de Azure.

Puede usar una función de Azure para hacer casi todo lo que haría con una función clásica en el código, la diferencia es que cualquier aplicación que tenga credenciales para acceder a esta función.

Para crear una función de Azure:

  1. En Azure Portal, haga clic en Nuevo en la esquina superior izquierda y busque Function App y haga clic en Entrar.

    creación de una aplicación de funciones

    Nota

    Es posible que la palabra New se haya reemplazado por Create a resource (Crear un recurso) en portales más recientes.

  2. La nueva página proporcionará una descripción de Azure Function App Service. En la parte inferior izquierda de este símbolo del sistema, seleccione el botón Crear para crear una asociación con este servicio.

    información de la aplicación de funciones

  3. Una vez que haya hecho clic en Crear:

    1. Proporcione un nombre de aplicación. Aquí solo se pueden usar letras y números (se permite mayúsculas o minúsculas).

    2. Seleccione su suscripción preferida.

    3. Elija un grupo de recursos o cree uno nuevo. Un grupo de recursos proporciona una manera de supervisar, controlar el acceso, aprovisionar y administrar la facturación de una colección de recursos de Azure. Se recomienda mantener todos los servicios de Azure asociados a un único proyecto (por ejemplo, estos laboratorios) en un grupo de recursos común).

      Si desea obtener más información sobre los grupos de recursos de Azure, visite el artículo sobre el grupo de recursos.

    4. En este ejercicio, seleccione Windows como sistema operativo elegido.

    5. Seleccione Plan de consumo para el plan de hospedaje.

    6. Determine la ubicación del grupo de recursos (si va a crear un nuevo grupo de recursos). Idealmente, la ubicación sería en la región donde se ejecutaría la aplicación. Algunos recursos de Azure solo están disponibles en determinadas regiones. Para obtener un rendimiento óptimo, seleccione la misma región que la cuenta de almacenamiento.

    7. En Almacenamiento, seleccione Usar existente y, a continuación, use el menú desplegable y busque el almacenamiento creado anteriormente.

    8. Deje Application Insights desactivado para este ejercicio.

      detalles de la aplicación de funciones de entrada

  4. Haga clic en el botón Crear.

  5. Una vez que haya hecho clic en Crear, tendrá que esperar a que se cree el servicio, esto puede tardar un minuto.

  6. Una notificación aparecerá en el portal una vez creada la instancia de servicio.

    nueva notificación de Azure Portal

  7. Haga clic en las notificaciones para explorar la nueva instancia de servicio.

    Ir a la aplicación de funciones de recursos

  8. Haga clic en el botón Ir al recurso en la notificación para explorar la nueva instancia de servicio. Se le dirigirá a la nueva instancia de Function App Service.

  9. En el panel de Function App, mantenga el mouse sobre Functions, se encuentra en el panel de la izquierda y, a continuación, haga clic en el símbolo + (más).

    crear una nueva función

  10. En la página siguiente, asegúrese de que webhook + API está seleccionado y, en Elegir un lenguaje, seleccione CSharp, ya que este será el lenguaje que se usará para este tutorial. Por último, haga clic en el botón Crear esta función .

    seleccionar web hook csharp

  11. Si no es así, debe ir a la página de códigos (run.csx), si no es así, hacer clic en la función recién creada en la lista funciones del panel de la izquierda.

    abrir nueva función

  12. Copie el código siguiente en la función. Esta función simplemente devolverá un entero aleatorio entre 0 y 2 cuando se llame a . No se preocupe por el código existente, no dude en pegar sobre él.

        using System.Net;
        using System.Threading.Tasks;
    
        public static int Run(CustomObject req, TraceWriter log)
        {
            Random rnd = new Random();
            int randomInt = rnd.Next(0, 3);
            return randomInt;
        }
    
        public class CustomObject
        {
            public String name {get; set;}
        }
    
  13. Seleccione Guardar.

  14. El resultado debe ser similar a la imagen siguiente.

  15. Haga clic en Obtener la dirección URL de la función y anote el punto de conexión que se muestra. Tendrá que insertarlo en la clase AzureServices que creará más adelante en este curso.

    Obtención del punto de conexión de la función

    Insertar punto de conexión de función

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

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

Configure y pruebe los cascos envolventes de realidad mixta.

Nota

No necesitará controladores de movimiento para este curso. Si necesita compatibilidad con la configuración del casco envolvente, visite el artículo configuración de realidad mixta.

  1. Abra Unity y haga clic en Nuevo.

    Creación de un nuevo proyecto de Unity

  2. Ahora tendrá que proporcionar un nombre de proyecto de Unity. Inserte MR_Azure_Functions. Asegúrese de que el tipo de proyecto está establecido 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.

    Asigne un nombre al nuevo proyecto de Unity.

  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.

    establecer Visual Studio como editor de scripts

  4. A continuación, vaya aConfiguración de compilación de archivos> y cambie la plataforma a Plataforma universal de Windows, haciendo clic en el botón Cambiar plataforma.

    cambiar la plataforma a uwp

  5. Vaya aConfiguración de compilaciónde archivos> y asegúrese de que:

    1. El dispositivo de destino se establece en Cualquier dispositivo.

      Para Microsoft HoloLens, establezca Dispositivo de destino 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.

        agregar escenas abiertas

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

        crear carpeta de escenas

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

        Guardar escena de funciones

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

    Dejar la configuración de compilación predeterminada

  7. 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 .

    configuración del reproductor en el inspector

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

    1. En la pestaña Otros valores:

      1. La versión del entorno de 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
    2. En la pestaña Configuración de publicación , en Funcionalidades, active:

      • InternetClient

        establecer funcionalidades

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

      establecer la configuración de XR

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

    tick proyectos de c#

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

  11. Guarde la escena y el proyecto (ARCHIVO>SAVE SCENE/FILE>SAVE PROJECT).

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

Importante

Si desea omitir los componentes de Configuración de Unity de este curso y continuar directamente en el código, no dude en descargar este archivo .unitypackage e importarlo en el proyecto como un paquete personalizado. Esto también contendrá los archivos DLL del capítulo siguiente. Después de la importación, continúe desde el capítulo 7.

  1. En el Panel de jerarquía, encontrará un objeto denominado Cámara principal, este objeto representa el punto de vista "head" una vez que esté "dentro" de la aplicación.

  2. Con el panel de Unity delante de usted, seleccione Main Camera GameObject. Observará que el Panel inspector (que se encuentra a la derecha, dentro del panel) mostrará los distintos componentes de ese GameObject, con Transformar en la parte superior, seguido de Cámara y otros componentes. Tendrá que restablecer la transformación de la cámara principal, por lo que se coloca correctamente.

  3. Para ello, seleccione el icono engranaje situado junto al componente Transformar de la cámara y seleccione Restablecer.

    restablecimiento de transformación

  4. A continuación, actualice el componente Transformar para que tenga el siguiente aspecto:

Transformar: posición

X S Z
0 1 0

Transformación: rotación

X S Z
0 0 0

Transformación: escalado

X S Z
1 1 1

establecer transformación de cámara

Capítulo 5: Configuración de la escena de Unity

  1. Haga clic con el botón derecho en un área vacía del Panel de jerarquía, en Objeto 3D, agregue un plano.

    crear un nuevo plano

  2. Con el objeto Plane seleccionado, cambie los parámetros siguientes en el Panel inspector:

Transformar: posición

X S Z
0 0 4

Transformación: escalado

X S Z
10 1 10

establecer posición y escala del plano

vista de escena del plano

  1. Haga clic con el botón derecho en un área vacía del Panel de jerarquía, en Objeto 3D, agregue un cubo.

    1. Cambie el nombre del cubo a GazeButton (con el cubo seleccionado, presione "F2").

    2. Cambie los parámetros siguientes para Transformar posición en el Panel inspector:

      X S Z
      0 3 5

      establecer transformación del botón de mirada

      vista de escena del botón de mirada

    3. Haga clic en el botón desplegable Etiqueta y haga clic en Agregar etiqueta para abrir el panel Etiquetas & capas.

      agregar nueva etiqueta

      seleccionar más

    4. Seleccione el botón + (más) y, en el campo Nuevo nombre de etiqueta , escriba GazeButton y presione Guardar.

      nombre de nueva etiqueta

    5. Haga clic en el objeto GazeButton en el Panel de jerarquía y, en el Panel inspector, asigne la etiqueta GazeButton recién creada.

      asignar el botón de mirada a la nueva etiqueta

  2. Haga clic con el botón derecho en el objeto GazeButton , en el Panel de jerarquía y agregue un GameObject vacío (que se agregará como un objeto secundario ).

  3. Seleccione el nuevo objeto y cámbielo ShapeSpawnPoint.

    1. Cambie los parámetros siguientes para Transformar posición en el Panel inspector:

      X S Z
      0 -1 0

      actualizar transformación de punto de creación de forma

      vista de la escena del punto de generar forma

  4. A continuación, creará un objeto Text 3D para proporcionar comentarios sobre el estado del servicio de Azure.

    Haga clic con el botón derecho en gazeButton en el Panel de jerarquía y agregue un objetode texto 3D 3D>como secundario.

    crear un nuevo objeto de texto 3D

  5. Cambie el nombre del objeto Text 3D a AzureStatusText.

  6. Cambie la posición de transformación del objeto AzureStatusText de la siguiente manera:

    X S Z
    0 0 -0,6
  7. Cambie el objeto AzureStatusTextTransform Scale de la siguiente manera: | X | Y | Z | | :---: | :---: | :---: | | 0.1 | 0.1 | 0.1 |

    Nota

    No se preocupe si parece estar fuera del centro, ya que esto se corregirá cuando se actualice el siguiente componente Text Mesh.

  8. Cambie el componente Text Mesh para que coincida con lo siguiente:

    establecer el componente de malla de texto

    Sugerencia

    El color seleccionado aquí es El color hexadecimal: 000000FF, aunque no dude en elegir el suyo propio, simplemente asegúrese de que es legible.

  9. La estructura del Panel de jerarquía ahora debería tener este aspecto:

    Malla de texto en la jerarquía

  10. La escena debería tener ahora un aspecto similar al siguiente:

    Malla de texto en la vista de escena

Capítulo 6: Importación de Azure Storage para Unity

Usará Azure Storage para Unity (que aprovecha el SDK de .Net para Azure). Puede obtener más información sobre esto en el artículo azure Storage para Unity.

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 el SDK en su propio proyecto, asegúrese de que ha descargado la versión ".unitypackage" más reciente de GitHub. A continuación, haga lo siguiente:

  1. Agregue el archivo .unitypackage a Unity mediante la opción de menú Importar>paquete>personalizado de recursos .

  2. En el cuadro Importar paquete de Unity que aparece, puede seleccionar todo en Almacenamiento de complementos>. Desactive todo lo demás, ya que no es necesario para este curso.

    importar en el paquete

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

  4. Vaya a la carpeta Almacenamiento en Complementos, en la vista Proyecto y seleccione solo los siguientes complementos:

    • Microsoft.Data.Edm

    • Microsoft.Data.OData

    • Microsoft.WindowsAzure.Storage

    • Newtonsoft.Json

    • System.Spatial

      desactive Cualquier plataforma

  5. Con estos complementos específicos seleccionados, desactiveCualquier plataforma y desactiveWSAPlayer y haga clic en Aplicar.

    aplicación de dlls de plataforma

    Nota

    Estamos marcando estos complementos concretos para que solo se usen en el Editor de Unity. Esto se debe a que hay diferentes versiones de los mismos complementos en la carpeta WSA que se usarán después de exportar el proyecto desde Unity.

  6. En la carpeta Complemento de almacenamiento , seleccione solo:

    • Microsoft.Data.Services.Client

      set don't process for dlls (establecer no procesar para dlls)

  7. Active la casilla No procesar en Configuración de la plataforma y haga clic en Aplicar.

    no se aplica ningún procesamiento

    Nota

    Estamos marcando este complemento "No procesar" porque el parcheador de ensamblados de Unity tiene dificultades para procesar este complemento. El complemento seguirá funcionando aunque no se procese.

Capítulo 7: Creación de la clase AzureServices

La primera clase que va a crear es la clase AzureServices .

La clase AzureServices será responsable de:

  • Almacenamiento de credenciales de cuenta de Azure.

  • Llamar a la función App de Azure.

  • Carga y descarga del archivo de datos en Azure Cloud Storage.

Para crear esta clase:

  1. Haga clic con el botón derecho en la carpeta de recursos , ubicada en el Panel del proyecto, Crear>carpeta. Asigne un nombre a la carpeta Scripts.

    crear nueva carpeta

    carpeta de llamadas: scripts

  2. Haga doble clic en la carpeta que acaba de crear para abrirla.

  3. Haga clic con el botón derecho dentro de la carpeta Crear>script de C#. Llame al script AzureServices.

  4. Haga doble clic en la nueva clase AzureServices para abrirla con Visual Studio.

  5. Agregue los siguientes espacios de nombres a la parte superior de AzureServices:

        using System;
        using System.Threading.Tasks;
        using UnityEngine;
        using Microsoft.WindowsAzure.Storage;
        using Microsoft.WindowsAzure.Storage.File;
        using System.IO;
        using System.Net;
    
  6. Agregue los siguientes campos inspectores dentro de la clase AzureServices :

        /// <summary>
        /// Provides Singleton-like behavior to this class.
        /// </summary>
        public static AzureServices instance;
    
        /// <summary>
        /// Reference Target for AzureStatusText Text Mesh object
        /// </summary>
        public TextMesh azureStatusText;
    
  7. A continuación, agregue las siguientes variables miembro dentro de la clase AzureServices :

        /// <summary>
        /// Holds the Azure Function endpoint - Insert your Azure Function
        /// Connection String here.
        /// </summary>
    
        private readonly string azureFunctionEndpoint = "--Insert here you AzureFunction Endpoint--";
    
        /// <summary>
        /// Holds the Storage Connection String - Insert your Azure Storage
        /// Connection String here.
        /// </summary>
        private readonly string storageConnectionString = "--Insert here you AzureStorage Connection String--";
    
        /// <summary>
        /// Name of the Cloud Share - Hosts directories.
        /// </summary>
        private const string fileShare = "fileshare";
    
        /// <summary>
        /// Name of a Directory within the Share
        /// </summary>
        private const string storageDirectory = "storagedirectory";
    
        /// <summary>
        /// The Cloud File
        /// </summary>
        private CloudFile shapeIndexCloudFile;
    
        /// <summary>
        /// The Linked Storage Account
        /// </summary>
        private CloudStorageAccount storageAccount;
    
        /// <summary>
        /// The Cloud Client
        /// </summary>
        private CloudFileClient fileClient;
    
        /// <summary>
        /// The Cloud Share - Hosts Directories
        /// </summary>
        private CloudFileShare share;
    
        /// <summary>
        /// The Directory in the share that will host the Cloud file
        /// </summary>
        private CloudFileDirectory dir;
    

    Importante

    Asegúrese de reemplazar el punto de conexión y cadena de conexión valores por los valores de Azure Storage, que se encuentran en Azure Portal.

  8. Ahora es necesario agregar código para los métodos Awake() y Start(). Se llamará a estos métodos cuando se inicialice la clase :

        private void Awake()
        {
            instance = this;
        }
    
        // Use this for initialization
        private void Start()
        {
            // Set the Status text to loading, whilst attempting connection to Azure.
            azureStatusText.text = "Loading...";
        }
    
        /// <summary>
        /// Call to the Azure Function App to request a Shape.
        /// </summary>
        public async void CallAzureFunctionForNextShape()
        {
    
        }
    

    Importante

    Rellenaremos el código de CallAzureFunctionForNextShape() en un capítulo futuro.

  9. Elimine el método Update(), ya que esta clase no la usará.

  10. Guarde los cambios en Visual Studio y vuelva a Unity.

  11. Haga clic y arrastre la clase AzureServices desde la carpeta Scripts al objeto Cámara principal en el Panel de jerarquía.

  12. Seleccione la Cámara principal y, a continuación, tome el objeto secundario AzureStatusText debajo del objeto GazeButton y colóquelo en el campo de destino de referencia azureStatusText , en el Inspector, para proporcionar la referencia al script AzureServices .

    asignar destino de referencia de texto de estado de Azure

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

El siguiente script que se va a crear es la clase ShapeFactory . El rol de esta clase es crear una nueva forma, cuando se solicite, y mantener un historial de las formas creadas en una lista de historial de formas. Cada vez que se crea una forma, la lista Historial de formas se actualiza en la clase AzureService y, a continuación, se almacena en Azure Storage. Cuando se inicia la aplicación, si se encuentra un archivo almacenado en Azure Storage, la lista Historial de formas se recupera y se reproduce, con el objeto Text 3D que proporciona si la forma generada procede del almacenamiento o la nueva.

Para crear esta clase:

  1. Vaya a la carpeta Scripts que creó anteriormente.

  2. Haga clic con el botón derecho dentro de la carpeta Crear>script de C#. Llame al script ShapeFactory.

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

  4. Asegúrese de que la clase ShapeFactory incluye los siguientes espacios de nombres:

        using System.Collections.Generic;
        using UnityEngine;
    
  5. Agregue las variables que se muestran a continuación a la clase ShapeFactory y reemplace las funciones Start() y Awake() por las siguientes:

        /// <summary>
        /// Provide this class Singleton-like behaviour
        /// </summary>
        [HideInInspector]
        public static ShapeFactory instance;
    
        /// <summary>
        /// Provides an Inspector exposed reference to ShapeSpawnPoint
        /// </summary>
        [SerializeField]
        public Transform spawnPoint;
    
        /// <summary>
        /// Shape History Index
        /// </summary>
        [HideInInspector]
        public List<int> shapeHistoryList;
    
        /// <summary>
        /// Shapes Enum for selecting required shape
        /// </summary>
        private enum Shapes { Cube, Sphere, Cylinder }
    
        private void Awake()
        {
            instance = this;
        }
    
        private void Start()
        {
            shapeHistoryList = new List<int>();
        }
    
  6. El método CreateShape() genera las formas primitivas, en función del parámetro entero proporcionado. El parámetro booleano se usa para especificar si la forma creada actualmente procede del almacenamiento o la nueva. Coloque el código siguiente en la clase ShapeFactory , debajo de los métodos anteriores:

        /// <summary>
        /// Use the Shape Enum to spawn a new Primitive object in the scene
        /// </summary>
        /// <param name="shape">Enumerator Number for Shape</param>
        /// <param name="storageShape">Provides whether this is new or old</param>
        internal void CreateShape(int shape, bool storageSpace)
        {
            Shapes primitive = (Shapes)shape;
            GameObject newObject = null;
            string shapeText = storageSpace == true ? "Storage: " : "New: ";
    
            AzureServices.instance.azureStatusText.text = string.Format("{0}{1}", shapeText, primitive.ToString());
    
            switch (primitive)
            {
                case Shapes.Cube:
                newObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
                break;
    
                case Shapes.Sphere:
                newObject = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                break;
    
                case Shapes.Cylinder:
                newObject = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
                break;
            }
    
            if (newObject != null)
            {
                newObject.transform.position = spawnPoint.position;
    
                newObject.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
    
                newObject.AddComponent<Rigidbody>().useGravity = true;
    
                newObject.GetComponent<Renderer>().material.color = UnityEngine.Random.ColorHSV(0f, 1f, 1f, 1f, 0.5f, 1f);
            }
        }
    
  7. Asegúrese de guardar los cambios en Visual Studio antes de volver a Unity.

  8. De nuevo en el Editor de Unity, haga clic y arrastre la clase ShapeFactory desde la carpeta Scripts al objeto Cámara principal en el Panel de jerarquía.

  9. Con la cámara principal seleccionada, observará que falta el componente de script ShapeFactory la referencia Desove punto . Para corregirlo, arrastre el objeto ShapeSpawnPoint desde el Panel de jerarquía al destino de referencia Desovar punto .

    establecer el destino de referencia del generador de formas

Capítulo 9: Creación de la clase Gaze

El último script que necesita crear es la clase Gaze .

Esta clase es responsable de crear un Raycast que se proyectará hacia delante desde la cámara principal, para detectar qué objeto está mirando el usuario. En este caso, Raycast tendrá que identificar si el usuario está mirando el objeto GazeButton en la escena y desencadena un comportamiento.

Para crear esta clase:

  1. Vaya a la carpeta Scripts que creó anteriormente.

  2. Haga clic con el botón derecho en el Panel del proyecto, Crear>script de C#. Llame al script Gaze.

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

  4. Asegúrese de que el siguiente espacio de nombres se incluye en la parte superior del script:

        using UnityEngine;
    
  5. A continuación, agregue las siguientes variables dentro de la clase Gaze :

        /// <summary>
        /// Provides Singleton-like behavior to this class.
        /// </summary>
        public static Gaze instance;
    
        /// <summary>
        /// The Tag which the Gaze will use to interact with objects. Can also be set in editor.
        /// </summary>
        public string InteractibleTag = "GazeButton";
    
        /// <summary>
        /// The layer which will be detected by the Gaze ('~0' equals everything).
        /// </summary>
        public LayerMask LayerMask = ~0;
    
        /// <summary>
        /// The Max Distance the gaze should travel, if it has not hit anything.
        /// </summary>
        public float GazeMaxDistance = 300;
    
        /// <summary>
        /// The size of the cursor, which will be created.
        /// </summary>
        public Vector3 CursorSize = new Vector3(0.05f, 0.05f, 0.05f);
    
        /// <summary>
        /// The color of the cursor - can be set in editor.
        /// </summary>
        public Color CursorColour = Color.HSVToRGB(0.0223f, 0.7922f, 1.000f);
    
        /// <summary>
        /// Provides when the gaze is ready to start working (based upon whether
        /// Azure connects successfully).
        /// </summary>
        internal bool GazeEnabled = false;
    
        /// <summary>
        /// The currently focused object.
        /// </summary>
        internal GameObject FocusedObject { get; private set; }
    
        /// <summary>
        /// The object which was last focused on.
        /// </summary>
        internal GameObject _oldFocusedObject { get; private set; }
    
        /// <summary>
        /// The info taken from the last hit.
        /// </summary>
        internal RaycastHit HitInfo { get; private set; }
    
        /// <summary>
        /// The cursor object.
        /// </summary>
        internal GameObject Cursor { get; private set; }
    
        /// <summary>
        /// Provides whether the raycast has hit something.
        /// </summary>
        internal bool Hit { get; private set; }
    
        /// <summary>
        /// This will store the position which the ray last hit.
        /// </summary>
        internal Vector3 Position { get; private set; }
    
        /// <summary>
        /// This will store the normal, of the ray from its last hit.
        /// </summary>
        internal Vector3 Normal { get; private set; }
    
        /// <summary>
        /// The start point of the gaze ray cast.
        /// </summary>
        private Vector3 _gazeOrigin;
    
        /// <summary>
        /// The direction in which the gaze should be.
        /// </summary>
        private Vector3 _gazeDirection;
    

Importante

Algunas de estas variables se podrán editar en el Editor.

  1. Ahora es necesario agregar código para los métodos Awake() y Start().

        /// <summary>
        /// The method used after initialization of the scene, though before Start().
        /// </summary>
        private void Awake()
        {
            // Set this class to behave similar to singleton
            instance = this;
        }
    
        /// <summary>
        /// Start method used upon initialization.
        /// </summary>
        private void Start()
        {
            FocusedObject = null;
            Cursor = CreateCursor();
        }
    
  2. Agregue el código siguiente, que creará un objeto de cursor al principio, junto con el método Update(), que ejecutará el método Raycast, junto con el lugar donde se alterna el booleano GazeEnabled:

        /// <summary>
        /// Method to create a cursor object.
        /// </summary>
        /// <returns></returns>
        private 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 = CursorSize;
    
            newCursor.GetComponent<MeshRenderer>().material = new Material(Shader.Find("Diffuse"))
            {
                color = CursorColour
            };
    
            newCursor.name = "Cursor";
    
            newCursor.SetActive(true);
    
            return newCursor;
        }
    
        /// <summary>
        /// Called every frame
        /// </summary>
        private void Update()
        {
            if(GazeEnabled == true)
            {
                _gazeOrigin = Camera.main.transform.position;
    
                _gazeDirection = Camera.main.transform.forward;
    
                UpdateRaycast();
            }
        }
    
  3. A continuación, agregue el método UpdateRaycast(), que proyectará un Raycast y detectará el destino de posicionamiento.

        private void UpdateRaycast()
        {
            // Set the old focused gameobject.
            _oldFocusedObject = FocusedObject;
    
            RaycastHit hitInfo;
    
            // Initialise Raycasting.
            Hit = Physics.Raycast(_gazeOrigin,
                _gazeDirection,
                out hitInfo,
                GazeMaxDistance, LayerMask);
    
            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 
            //    object. If so, reset the focused object.
            if (FocusedObject != _oldFocusedObject)
            {
                ResetFocusedObject();
    
                if (FocusedObject != null)
                {
                if (FocusedObject.CompareTag(InteractibleTag.ToString()))
                {
                        // Set the Focused object to green - success!
                        FocusedObject.GetComponent<Renderer>().material.color = Color.green;
    
                        // Start the Azure Function, to provide the next shape!
                        AzureServices.instance.CallAzureFunctionForNextShape();
                    }
                }
            }
        }
    
  4. Por último, agregue el método ResetFocusedObject(), que alternará el color actual de los objetos GazeButton, lo que indica si está creando una nueva forma o no.

        /// <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.ToString()))
                {
                    // Set the old focused object to red - its original state.
                    _oldFocusedObject.GetComponent<Renderer>().material.color = Color.red;
                }
            }
        }
    
  5. Guarde los cambios en Visual Studio antes de volver a Unity.

  6. Haga clic y arrastre la clase Gaze desde la carpeta Scripts al objeto Cámara principal en el Panel de jerarquía.

Capítulo 10: Finalización de la clase AzureServices

Con los otros scripts implementados, ahora es posible completar la clase AzureServices . Esto se logrará a través de:

  1. Agregar un nuevo método denominado CreateCloudIdentityAsync() para configurar las variables de autenticación necesarias para comunicarse con Azure.

    Este método también comprobará la existencia de un archivo almacenado previamente que contiene la lista de formas.

    Si se encuentra el archivo, deshabilitará la mirada del usuario y desencadenará la creación de formas, según el patrón de formas, tal como se almacena en el archivo de Azure Storage. El usuario puede ver esto, ya que la malla de texto proporcionará la pantalla "Almacenamiento" o "Nuevo", en función del origen de las formas.

    Si no se encuentra ningún archivo, habilitará la mirada, lo que permitirá al usuario crear formas al examinar el objeto GazeButton en la escena.

        /// <summary>
        /// Create the references necessary to log into Azure
        /// </summary>
        private async void CreateCloudIdentityAsync()
        {
            // Retrieve storage account information from connection string
            storageAccount = CloudStorageAccount.Parse(storageConnectionString);
    
            // Create a file client for interacting with the file service.
            fileClient = storageAccount.CreateCloudFileClient();
    
            // Create a share for organizing files and directories within the storage account.
            share = fileClient.GetShareReference(fileShare);
    
            await share.CreateIfNotExistsAsync();
    
            // Get a reference to the root directory of the share.
            CloudFileDirectory root = share.GetRootDirectoryReference();
    
            // Create a directory under the root directory
            dir = root.GetDirectoryReference(storageDirectory);
    
            await dir.CreateIfNotExistsAsync();
    
            //Check if the there is a stored text file containing the list
            shapeIndexCloudFile = dir.GetFileReference("TextShapeFile");
    
            if (!await shapeIndexCloudFile.ExistsAsync())
            {
                // File not found, enable gaze for shapes creation
                Gaze.instance.GazeEnabled = true;
    
                azureStatusText.text = "No Shape\nFile!";
            }
            else
            {
                // The file has been found, disable gaze and get the list from the file
                Gaze.instance.GazeEnabled = false;
    
                azureStatusText.text = "Shape File\nFound!";
    
                await ReplicateListFromAzureAsync();
            }
        }
    
  2. El siguiente fragmento de código procede del método Start(); donde se realizará una llamada al método CreateCloudIdentityAsync(). No dude en copiar sobre el método Start() actual, con lo siguiente:

        private void Start()
        {
            // Disable TLS cert checks only while in Unity Editor (until Unity adds support for TLS)
    #if UNITY_EDITOR
            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
    #endif
    
            // Set the Status text to loading, whilst attempting connection to Azure.
            azureStatusText.text = "Loading...";
    
            //Creating the references necessary to log into Azure and check if the Storage Directory is empty
            CreateCloudIdentityAsync();
        }
    
  3. Rellene el código del método CallAzureFunctionForNextShape(). Usará la aplicación de funciones de Azure creada anteriormente para solicitar un índice de formas. Una vez recibida la nueva forma, este método enviará la forma a la clase ShapeFactory para crear la nueva forma en la escena. Use el código siguiente para completar el cuerpo de CallAzureFunctionForNextShape().

        /// <summary>
        /// Call to the Azure Function App to request a Shape.
        /// </summary>
        public async void CallAzureFunctionForNextShape()
        {
            int azureRandomInt = 0;
    
            // Call Azure function
            HttpWebRequest webRequest = WebRequest.CreateHttp(azureFunctionEndpoint);
    
            WebResponse response = await webRequest.GetResponseAsync();
    
            // Read response as string
            using (Stream stream = response.GetResponseStream())
            {
                StreamReader reader = new StreamReader(stream);
    
                String responseString = reader.ReadToEnd();
    
                //parse result as integer
                Int32.TryParse(responseString, out azureRandomInt);
            }
    
            //add random int from Azure to the ShapeIndexList
            ShapeFactory.instance.shapeHistoryList.Add(azureRandomInt);
    
            ShapeFactory.instance.CreateShape(azureRandomInt, false);
    
            //Save to Azure storage
            await UploadListToAzureAsync();
        }
    
  4. Agregue un método para crear una cadena mediante la concatenación de los enteros almacenados en la lista de historial de formas y guárdelo en el archivo de Azure Storage.

        /// <summary>
        /// Upload the locally stored List to Azure
        /// </summary>
        private async Task UploadListToAzureAsync()
        {
            // Uploading a local file to the directory created above
            string listToString = string.Join(",", ShapeFactory.instance.shapeHistoryList.ToArray());
    
            await shapeIndexCloudFile.UploadTextAsync(listToString);
        }
    
  5. Agregue un método para recuperar el texto almacenado en el archivo ubicado en el archivo de Azure Storage y deserializarlo en una lista.

  6. Una vez completado este proceso, el método vuelve a habilitar la mirada para que el usuario pueda agregar más formas a la escena.

        ///<summary>
        /// Get the List stored in Azure and use the data retrieved to replicate 
        /// a Shape creation pattern
        ///</summary>
        private async Task ReplicateListFromAzureAsync()
        {
            string azureTextFileContent = await shapeIndexCloudFile.DownloadTextAsync();
    
            string[] shapes = azureTextFileContent.Split(new char[] { ',' });
    
            foreach (string shape in shapes)
            {
                int i;
    
                Int32.TryParse(shape.ToString(), out i);
    
                ShapeFactory.instance.shapeHistoryList.Add(i);
    
                ShapeFactory.instance.CreateShape(i, true);
    
                await Task.Delay(500);
            }
    
            Gaze.instance.GazeEnabled = true;
    
            azureStatusText.text = "Load Complete!";
        }
    
  7. Guarde los cambios en Visual Studio antes de volver a Unity.

Capítulo 11: Compilación de la solución para UWP

Para comenzar el proceso de compilación:

  1. Vaya a Configuración de compilación de archivos>.

    compilación de la aplicación

  2. 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, presione Seleccionar carpeta.

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

  4. 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 de la aplicación

Para implementar la aplicación:

  1. Vaya a la carpeta Aplicación que se creó en el último capítulo. Verá un archivo con el nombre de las aplicaciones, con la extensión ".sln", que debe hacer doble clic, para abrirlo en Visual Studio.

  2. En plataforma de soluciones, seleccione x86, Equipo local.

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

    Para la Microsoft HoloLens, es posible que le resulte más fácil establecer esto en Máquina remota, de modo que no esté anclado a su equipo. Sin embargo, también tendrá que hacer lo siguiente:

    • Conozca la dirección IP de holoLens, que se puede encontrar en la red de configuración> &Opciones avanzadasde Wi-Fi> deInternet>; IPv4 es la dirección que debe usar.
    • Asegúrese de que el modo de desarrollador está activado; se encuentra en Configuración>Update & Security>Para desarrolladores.

    implementación de la solución

  4. Vaya al menú Compilar y haga clic en Implementar solución para transferir localmente la aplicación a la máquina.

  5. La aplicación ahora debería aparecer en la lista de aplicaciones instaladas, lista para iniciarse y probarse.

La aplicación de almacenamiento y Azure Functions finalizada

Enhorabuena, ha creado una aplicación de realidad mixta que aprovecha los servicios de Azure Functions y Azure Storage. La aplicación podrá dibujar datos almacenados y proporcionar una acción basada en esos datos.

producto final -end

Ejercicios extra

Ejercicio 1

Cree un segundo punto de generar y un registro desde el que se creó un objeto. Al cargar el archivo de datos, reproduzca las formas que se generan desde la ubicación en la que se crearon originalmente.

Ejercicio 2

Cree una manera de reiniciar la aplicación, en lugar de tener que volver a abrirla cada vez. Cargar escenas es un buen punto de partida. Después de hacerlo, cree una manera de borrar la lista almacenada en Azure Storage para que se pueda restablecer fácilmente desde la aplicación.