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.
En este curso, aprenderá a crear y usar Azure Functions y a 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 más información, visite el artículo sobre 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:
- Permitir al usuario mirar alrededor de una escena.
- Desencadene la creación de objetos cuando el usuario mira en un "botón" 3D.
- Una función de Azure elegirá los objetos generados.
- A medida que se genera cada objeto, la aplicación almacenará el tipo de objeto en un archivo de Azure, ubicado en Azure Storage.
- Al cargar una segunda vez, se recuperarán los datos de Azure File y se usarán para reproducir las acciones de creación de la instancia anterior de la aplicación.
En la aplicación, es el momento de 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 el conocimiento que obtiene de este curso para mejorar la aplicación de realidad mixta.
Compatibilidad con dispositivos
Curso | HoloLens | Cascos envolventes |
---|---|---|
MR y Azure 305: Funciones y almacenamiento | ✔️ | ✔️ |
Nota:
Aunque este curso se centra principalmente en cascos envolventes de Windows Mixed Reality (VR), también puede aplicar lo que aprende en este curso a Microsoft HoloLens. A medida que siga el curso, verá notas sobre los cambios que podría 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 de este documento representan lo que se ha probado y comprobado en el momento de redactarlo (mayo 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:
- Un equipo de desarrollo, compatible con Windows Mixed Reality para el desarrollo de cascos envolventes (VR)
- Windows 10 Fall Creators Update (o posterior) con el modo desarrollador habilitado
- El SDK de Windows 10 más reciente
- Unity 2017.4
- Visual Studio 2017
- Casco envolvente (VR) de Windows Mixed Reality o Microsoft HoloLens con el modo desarrollador habilitado
- Una suscripción a una cuenta de Azure para crear recursos de Azure
- Acceso a Internet para la configuración y recuperación de datos de Azure
Antes de comenzar
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 carpeta 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 Azure Portal.
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.
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.
Nota:
Es posible que la palabra New se haya reemplazado por Create a resource (Crear un recurso) en portales más recientes.
La nueva página proporcionará una descripción del servicio de cuenta de Azure Storage. En la parte inferior izquierda de este símbolo del sistema, seleccione el botón Crear para crear una asociación con este servicio.
Una vez que haya hecho clic en Crear:
Inserte un nombre para su cuenta, tenga en cuenta que este campo solo acepta números y letras minúsculas.
En Modelo de implementación, seleccione Resource Manager.
En Tipo de cuenta, seleccione Almacenamiento (uso general v1) .
Determine la ubicación del grupo de recursos (si va a crear un nuevo grupo de recursos). Idealmente, la ubicación estaría en la región donde se ejecutaría la aplicación. Algunos recursos de Azure solo están disponibles en determinadas regiones.
En Replicación, seleccione Almacenamiento con redundancia geográfica con acceso de lectura (RA-GRS).
En Rendimiento, seleccione Estándar.
Deje Transferencia segura necesaria como Deshabilitada.
Seleccione una opción en Suscripción.
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.
También deberá confirmar que ha comprendido los Términos y Condiciones aplicados a este Servicio.
Seleccione Crear.
Una vez que haya hecho clic en Crear, tendrá que esperar a que se cree el servicio, esto puede tardar un minuto.
Aparecerá una notificación en el portal una vez creada la instancia de servicio.
Haga clic en las notificaciones para explorar la nueva instancia de servicio.
Haga clic en el botón Ir al recurso de la notificación para explorar la nueva instancia de servicio. Se le llevará a la nueva instancia de servicio de la cuenta de almacenamiento.
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.
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 es posible acceder a la cuenta de Azure.
Para crear una función de Azure:
En Azure Portal, haga clic en Nuevo en la esquina superior izquierda y busque Function App y haga clic en Entrar.
Nota:
Es posible que la palabra New se haya reemplazado por Create a resource (Crear un recurso) en portales más recientes.
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.
Una vez que haya hecho clic en Crear:
Proporcione un nombre de aplicación. Solo se pueden usar letras y números aquí (se permite mayúsculas o minúsculas).
Seleccione su suscripción preferida.
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.
Para este ejercicio, seleccione Windows como sistema operativo elegido.
Seleccione Plan de consumo para el Plan de hospedaje.
Determine la ubicación del grupo de recursos (si va a crear un nuevo grupo de recursos). Idealmente, la ubicación estarí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.
En Almacenamiento, seleccione Usar existente y, a continuación, use el menú desplegable y busque el almacenamiento creado anteriormente.
Deje Application Insights desactivado para este ejercicio.
Haga clic en el botón Crear.
Una vez que haya hecho clic en Crear, tendrá que esperar a que se cree el servicio, esto puede tardar un minuto.
Aparecerá una notificación en el portal una vez creada la instancia de servicio.
Haga clic en las notificaciones para explorar la nueva instancia de servicio.
Haga clic en el botón Ir al recurso de la notificación para explorar la nueva instancia de servicio. Se le llevará a la nueva instancia de Function App Service.
En el panel de Function App, mantenga el mouse sobre Functions, que se encuentra en el panel de la izquierda y, a continuación, haga clic en el símbolo + (más).
En la página siguiente, asegúrese de que webhook + API está seleccionado y, para 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 .
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.
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 pegarlo sobre la parte superior.
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;} }
Seleccione Guardar.
El resultado debe ser similar a la imagen siguiente.
Haga clic en Obtener 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.
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 soporte técnico para configurar los cascos envolventes, visite el artículo configuración de realidad mixta.
Abra Unity y haga clic en Nuevo.
Ahora deberá proporcionar un nombre de proyecto de Unity. Insertar 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.
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.
A continuación, vaya a Configuración> de compilación de archivos y cambie la plataforma a Plataforma universal de Windows haciendo clic en el botón Cambiar plataforma.
Vaya a Configuración de compilación de archivos>y asegúrese de que:
El dispositivo de destino se establece en Cualquier dispositivo.
Para Microsoft HoloLens, establezca Dispositivo de destino en HoloLens.
Tipo de compilación se establece en D3D
El SDK se establece en Latest installed (Versión más reciente instalada)
La versión de Visual Studio se establece en Latest installed (Versión más reciente instalada)
Build and Run (Compilar y ejecutar ) está establecido en Equipo local
Guarde la escena y agréguela a la compilación.
Para ello, seleccione Agregar escenas abiertas. Aparecerá una ventana de guardado.
Cree una nueva carpeta para esto y cualquier escena futura y, a continuación, seleccione el botón Nueva carpeta para crear una nueva carpeta, asígnela el nombre Scenes.
Abra la carpeta Escenas recién creada y, a continuación, en el campo Nombre de archivo: texto, escriba FunctionsScene y presione Guardar.
La configuración restante, en Configuración de compilación, debe dejarse como predeterminada por ahora.
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.
En este panel, es necesario comprobar algunos valores:
En la pestaña Otros valores :
- La versión del entorno de ejecución de scripting debe ser experimental (equivalente a.NET 4.6), lo que desencadenará la necesidad de reiniciar el editor.
- El back-end de scripting debe ser .NET
- El nivel de compatibilidad de API debe ser .NET 4.6
En la pestaña Configuración de publicación, en Funcionalidades, active:
InternetClient
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 realidad virtual), asegúrese de que se agrega el SDK de Windows Mixed Reality.
De nuevo en Configuración de compilación, los proyectos de Unity de C# ya no están atenuados; marque la casilla situada junto a esto.
Cierre la ventana Build Settings (Configuración de compilación).
Guarde la escena y el proyecto (FILE>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 paquete .unitypackage e importarlo en el proyecto como un paquete personalizado. Esto también contendrá los archivos DLL del siguiente capítulo. Después de la importación, continúe desde el capítulo 7.
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.
Con el panel de Unity delante de usted, seleccione el GameObject de cámara principal. Observarás que el Panel inspector (que se encuentra a la derecha, dentro del Panel) mostrará los distintos componentes de ese GameObject, con Transformación 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.
Para ello, seleccione el icono engranaje situado junto al componente Transformar de la cámara y seleccione Restablecer.
A continuación, actualice el componente Transformar para que tenga el siguiente aspecto:
Transformación: posición
X | Y | Z |
---|---|---|
0 | 1 | 0 |
Transformación: rotación
X | Y | Z |
---|---|---|
0 | 0 | 0 |
Transformación: escalado
X | Y | Z |
---|---|---|
1 | 1 | 1 |
Capítulo 5: Configuración de la escena de Unity
Haga clic con el botón derecho en un área vacía del Panel jerarquía, en Objeto 3D, agregue un plano.
Con el objeto Plane seleccionado, cambie los parámetros siguientes en el Panel inspector:
Transformación: posición
X | Y | Z |
---|---|---|
0 | 0 | 4 |
Transformación: escalado
X | Y | Z |
---|---|---|
10 | 1 | 10 |
Haga clic con el botón derecho en un área vacía del Panel jerarquía, en Objeto 3D, agregue un cubo.
Cambie el nombre del cubo a GazeButton (con el cubo seleccionado, presione "F2").
Cambie los parámetros siguientes para Transformar posición en el Panel de inspectores:
X Y Z 0 3 5 Haga clic en el botón desplegable Etiqueta y haga clic en Agregar etiqueta para abrir el panel Etiquetas y capas.
Seleccione el botón + (más) y, en el campo Nuevo nombre de etiqueta, escriba GazeButton y presione Guardar.
Haga clic en el objeto GazeButton en el Panel de jerarquía y, en el Panel de inspectores, asigne la etiqueta GazeButton recién creada.
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 ).
Seleccione el nuevo objeto y cámbielo ShapeSpawnPoint.
Cambie los parámetros siguientes para Transformar posición en el Panel de inspectores:
X Y Z 0 -1 0
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 el GazeButton en el Panel de jerarquía y agregue un objeto 3D Object>3D Text como elemento secundario.
Cambie el nombre del objeto Text 3D a AzureStatusText.
Cambie la posición de transformación del objeto AzureStatusText de la siguiente manera:
X Y Z 0 0 -0,6 Cambie la escala de transformación del objeto AzureStatusText de la siguiente manera: | X | Y | Z | | :---: | :---: | :---: | | 0.1 | 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 componente Text Mesh siguiente.
Cambie el componente Text Mesh para que coincida con lo siguiente:
Sugerencia
El color seleccionado aquí es Color hexadecimal: 000000FF, aunque no dude en elegir su propio, solo asegúrese de que es legible.
La estructura del Panel de jerarquía ahora debería tener este aspecto:
Ahora la escena debería tener este aspecto:
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 los complementos se vuelvan a configurar 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:
Agregue el archivo .unitypackage a Unity mediante la opción de> menú Importar paquete personalizado de paquetes>de activos.
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.
Haga clic en el botón Importar para agregar los elementos al proyecto.
Vaya a la carpeta Storage en Complementos, en la vista Proyecto y seleccione solo los complementos siguientes:
Microsoft.Data.Edm
Microsoft.Data.OData
Microsoft.WindowsAzure.Storage
Newtonsoft.Json
System.Spatial
Con estos complementos específicos seleccionados, desactive Cualquier plataforma y desactive WSAPlayer y haga clic en Aplicar.
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.
En la carpeta Complemento de almacenamiento , seleccione solo:
Microsoft.Data.Services.Client
Active la casilla No procesar en Configuración de la plataforma y haga clic en Aplicar.
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 la 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:
Haga clic con el botón derecho en la carpeta de recursos, que se encuentra en el Panel de proyectos, Crear>carpeta. Asigne un nombre a la carpeta Scripts.
Haga doble clic en la carpeta que acaba de crear para abrirla.
Haga clic con el botón derecho en la carpeta Create C# Script (Crear>script de C#). Llame al script AzureServices.
Haga doble clic en la nueva clase AzureServices para abrirla con Visual Studio.
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;
Agregue los siguientes campos de inspector 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;
A continuación, agregue las siguientes variables de 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 los valores de cadena de conexión por los valores de Azure Storage, que se encuentran en Azure Portal.
Ahora es necesario agregar código para los métodos Awake() e 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.
Elimine el método Update(), ya que esta clase no la usará.
Guarde los cambios en Visual Studio y vuelva a Unity.
Haga clic y arrastre la clase AzureServices de la carpeta Scripts al objeto Main Camera en el Panel jerarquía.
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 de AzureStatusText , en el Inspector, para proporcionar la referencia al script AzureServices .
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 solicita 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 reproduce, con el objeto Text 3D que proporciona si la forma generada procede del almacenamiento o la nueva.
Para crear esta clase:
Vaya a la carpeta Scripts que creó anteriormente.
Haga clic con el botón derecho en la carpeta Create C# Script (Crear>script de C#). Llame al script ShapeFactory.
Haga doble clic en el nuevo script ShapeFactory para abrirlo con Visual Studio.
Asegúrese de que la clase ShapeFactory incluye los siguientes espacios de nombres:
using System.Collections.Generic; using UnityEngine;
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>(); }
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 de 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); } }
Asegúrese de guardar los cambios en Visual Studio antes de volver a Unity.
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 jerarquía.
Con la cámara principal seleccionada, observará que falta el componente de script ShapeFactory la referencia de punto de generar . Para corregirlo, arrastre el objeto ShapeSpawnPoint desde el Panel jerarquía al destino de referencia de punto de generar.
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 el objeto que el usuario está mirando. En este caso, Raycast tendrá que identificar si el usuario está viendo el objeto GazeButton en la escena y desencadenar un comportamiento.
Para crear esta clase:
Vaya a la carpeta Scripts que creó anteriormente.
Haga clic con el botón derecho en el Panel del proyecto, Crear>script de C#. Llame al script Gaze.
Haga doble clic en el nuevo script De mirada para abrirlo con Visual Studio.
Asegúrese de que el siguiente espacio de nombres se incluye en la parte superior del script:
using UnityEngine;
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.
Ahora es necesario agregar código para los métodos Awake() e 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(); }
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 estar 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(); } }
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(); } } } }
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; } } }
Guarde los cambios en Visual Studio antes de volver a Unity.
Haga clic y arrastre la clase Gaze desde la carpeta Scripts hasta el objeto Main Camera en el Panel jerarquía.
Capítulo 10: Finalización de la clase AzureServices
Con los demás scripts implementados, ahora es posible completar la clase AzureServices . Esto se logrará a través de:
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 Text Mesh proporcionará "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 mirar 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(); } }
El siguiente fragmento de código es desde el 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(); }
Rellene el código del método CallAzureFunctionForNextShape(). Usará la aplicación de funciones de Azure creada anteriormente para solicitar un índice de forma. 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(); }
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); }
Agregue un método para recuperar el texto almacenado en el archivo ubicado en el archivo de Azure Storage y deserializarlo en una lista.
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!"; }
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:
Vaya a Configuración de compilación de archivos>.
Haga clic en Generar. Unity iniciará una ventana de Explorador de archivos, donde debe crear y, a continuación, seleccionará una carpeta para 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.
Unity comenzará a compilar el proyecto en la carpeta Aplicación .
Una vez que Unity haya terminado de compilar (puede tardar algún tiempo), se 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:
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.
En la Plataforma de soluciones, seleccione x86, Máquina local.
En Configuración de la solución, seleccione Depurar.
Para Microsoft HoloLens, es posible que le resulte más fácil establecerlo en Máquina remota, de modo que no esté anclado a su equipo. Sin embargo, también tendrá que hacer lo siguiente:
- Conoce la dirección IP de tu HoloLens, que se puede encontrar en las opciones avanzadas de Red de configuración>e Internet>Wi-Fi>; IPv4 es la dirección que debes usar.
- Asegúrese de que el modo de desarrollador está activado; se encuentra en Configuración>actualización y seguridad>para desarrolladores.
Vaya al menú Compilar y haga clic en Implementar solución para transferir localmente la aplicación a la máquina.
La aplicación debería aparecer ahora 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.
Ejercicios extra
Ejercicio 1
Cree un segundo punto de creación y 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 para empezar. 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.