HoloLens (1.ª geração) e Azure 305: Funções e armazenamento


Nota

Os tutoriais Mixed Reality Academy foram concebidos com o HoloLens (1.ª geração) e Mixed Reality Headsets Envolventes em mente. Como tal, consideramos importante deixar estes tutoriais em vigor para os programadores que ainda estão à procura de orientação no desenvolvimento desses dispositivos. Estes tutoriais não serão atualizados com os conjuntos de ferramentas ou interações mais recentes que estão a ser utilizados para HoloLens 2. Serão mantidas para continuarem a trabalhar nos dispositivos suportados. Haverá uma nova série de tutoriais que serão publicados no futuro que demonstrarão como desenvolver para HoloLens 2. Este aviso será atualizado com uma ligação para esses tutoriais quando forem publicados.


produto final - iniciar

Neste curso, irá aprender a criar e utilizar Funções do Azure e armazenar dados com um recurso de Armazenamento do Azure, numa aplicação de realidade mista.

Funções do Azure é um serviço Microsoft, que permite aos programadores executar pequenos blocos de código, "funções", no Azure. Isto fornece uma forma de delegar trabalho para a cloud, em vez da sua aplicação local, que pode ter muitas vantagens. Funções do Azure suporta várias linguagens de desenvolvimento, incluindo C#, F#, Node.js, Java e PHP. Para obter mais informações, visite o artigo Funções do Azure.

O Armazenamento do Microsoft Azure é um serviço cloud da Microsoft, que permite aos programadores armazenar dados, com o seguro de que serão altamente disponíveis, seguros, duráveis, dimensionáveis e redundantes. Isto significa que a Microsoft irá lidar com todos os problemas de manutenção e críticos por si. Para obter mais informações, veja o artigo Armazenamento do Azure.

Depois de concluir este curso, terá uma aplicação de headset envolvente de realidade mista que poderá fazer o seguinte:

  1. Permitir que o utilizador olhe ao redor de uma cena.
  2. Acione a desova de objetos quando o utilizador olhar para um "botão" 3D.
  3. Os objetos gerados serão escolhidos por uma Função do Azure.
  4. À medida que cada objeto é gerado, a aplicação irá armazenar o tipo de objeto num Ficheiro do Azure, localizado no Armazenamento do Azure.
  5. Após carregar uma segunda vez, os dados do Ficheiro do Azure serão obtidos e utilizados para reproduzir as ações de desova da instância anterior da aplicação.

Na sua aplicação, cabe-lhe a si saber como irá integrar os resultados com a sua estrutura. Este curso foi concebido para lhe ensinar a integrar um Serviço do Azure no seu Projeto do Unity. A sua função é utilizar os conhecimentos obtidos neste curso para melhorar a sua Aplicação de realidade mista.

Suporte de dispositivos

Curso HoloLens Headsets envolventes
MR e Azure 305: Funções e armazenamento ✔️ ✔️

Nota

Embora este curso se centre principalmente em headsets envolventes (VR) Windows Mixed Reality, também pode aplicar o que aprendeu neste curso para Microsoft HoloLens. À medida que acompanha o curso, verá notas sobre quaisquer alterações que possa ter de utilizar para suportar o HoloLens.

Pré-requisitos

Nota

Este tutorial foi concebido para programadores com experiência básica com o Unity e C#. Tenha também em atenção que os pré-requisitos e as instruções escritas neste documento representam o que foi testado e verificado no momento da escrita (maio de 2018). Pode utilizar o software mais recente, conforme listado no artigo Instalar as ferramentas , embora não se deva presumir que as informações neste curso corresponderão perfeitamente ao que encontrará no software mais recente do que o que está listado abaixo.

Recomendamos o seguinte hardware e software para este curso:

Antes de começar

Para evitar problemas ao criar este projeto, é altamente sugerido que crie o projeto mencionado neste tutorial numa pasta raiz ou de raiz próxima (os caminhos de pasta longos podem causar problemas no tempo de compilação).

Capítulo 1 - Portal do Azure

Para utilizar o Serviço de Armazenamento do Azure, terá de criar e configurar uma Conta de Armazenamento no portal do Azure.

  1. Inicie sessão no Portal do Azure.

    Nota

    Se ainda não tiver uma conta do Azure, terá de criar uma. Se estiver a seguir este tutorial numa situação de sala de aula ou laboratório, peça ajuda ao seu instrutor ou a um dos tutores para configurar a sua nova conta.

  2. Assim que tiver sessão iniciada, clique em Novo no canto superior esquerdo, procure Conta de armazenamento e clique em Enter.

    pesquisa de armazenamento do azure

    Nota

    A palavra Novo pode ter sido substituída por Criar um recurso, em portais mais recentes.

  3. A nova página irá fornecer uma descrição do serviço conta de Armazenamento do Azure . Na parte inferior esquerda deste pedido, selecione o botão Criar para criar uma associação com este serviço.

    criar serviço

  4. Depois de clicar em Criar:

    1. Insira um Nome para a sua conta, tenha em atenção que este campo só aceita números e letras minúsculas.

    2. Em Modelo de implementação, selecione Gestor de recursos.

    3. Em Tipo de conta, selecione Armazenamento (fins gerais v1).

    4. Determine a Localização do grupo de recursos (se estiver a criar um novo Grupo de Recursos). Idealmente, a localização seria na região onde a aplicação seria executada. Alguns recursos do Azure só estão disponíveis em determinadas regiões.

    5. Para Replicação , selecione Armazenamento georredundante com acesso de leitura (RA-GRS).

    6. Em Desempenho, selecione Standard.

    7. Deixe Transferência segura necessária como Desativado.

    8. Selecione uma Subscrição.

    9. Escolha um Grupo de Recursos ou crie um novo. Um grupo de recursos fornece uma forma de monitorizar, controlar o acesso, aprovisionar e gerir a faturação de uma coleção de recursos do Azure. Recomenda-se manter todos os serviços do Azure associados a um único projeto (por exemplo, estes laboratórios) num grupo de recursos comum.

      Se quiser ler mais sobre os Grupos de Recursos do Azure, veja o artigo do grupo de recursos.

    10. Também terá de confirmar que compreendeu os Termos e Condições aplicados a este Serviço.

    11. Selecione Criar.

      informações do serviço de entrada

  5. Depois de clicar em Criar, terá de aguardar que o serviço seja criado, o que poderá demorar um minuto.

  6. Será apresentada uma notificação no portal assim que a instância do Serviço for criada.

    nova notificação no portal do Azure

  7. Clique nas notificações para explorar a nova instância do Serviço.

    ir para recurso

  8. Clique no botão Ir para recurso na notificação para explorar a nova instância do Serviço. Será levado para a nova instância de serviço da Conta de armazenamento .

    chaves de acesso

  9. Clique em Chaves de acesso para revelar os pontos finais deste serviço em nuvem. Utilize o Bloco de Notas ou semelhante para copiar uma das suas chaves para utilizar mais tarde. Além disso, tenha em atenção o valor cadeia de ligação , uma vez que será utilizado na classe AzureServices , que irá criar mais tarde.

    copiar cadeia de ligação

Capítulo 2 - Configurar uma Função do Azure

Agora, vai escrever uma Função do Azure no Serviço do Azure.

Pode utilizar uma Função do Azure para fazer praticamente tudo o que faria com uma função clássica no seu código. A diferença é que esta função pode ser acedida por qualquer aplicação que tenha credenciais para aceder à sua Conta do Azure.

Para criar uma Função do Azure:

  1. No portal do Azure, clique em Novo no canto superior esquerdo e procure Function App e clique em Enter.

    criar aplicação de funções

    Nota

    A palavra Novo pode ter sido substituída por Criar um recurso, em portais mais recentes.

  2. A nova página irá fornecer uma descrição do serviço Aplicação de Funções do Azure . Na parte inferior esquerda deste pedido, selecione o botão Criar para criar uma associação com este serviço.

    informações da aplicação de funções

  3. Depois de clicar em Criar:

    1. Indique um Nome da aplicação. Só podem ser utilizadas letras e números aqui (são permitidas maiúsculas ou minúsculas).

    2. Selecione a sua Subscrição preferencial.

    3. Escolha um Grupo de Recursos ou crie um novo. Um grupo de recursos fornece uma forma de monitorizar, controlar o acesso, aprovisionar e gerir a faturação de uma coleção de recursos do Azure. Recomenda-se manter todos os serviços do Azure associados a um único projeto (por exemplo, estes laboratórios) num grupo de recursos comum.

      Se quiser ler mais sobre os Grupos de Recursos do Azure, veja o artigo do grupo de recursos.

    4. Para este exercício, selecione Windows como o SO escolhido.

    5. Selecione Plano de Consumo para o Plano de Alojamento.

    6. Determine a Localização do grupo de recursos (se estiver a criar um novo Grupo de Recursos). Idealmente, a localização seria na região onde a aplicação seria executada. Alguns recursos do Azure só estão disponíveis em determinadas regiões. Para um desempenho ideal, selecione a mesma região que a conta de armazenamento.

    7. Em Armazenamento, selecione Utilizar existente e, em seguida, utilize o menu pendente, localize o armazenamento criado anteriormente.

    8. Deixe o Application Insights desativado para este exercício.

      detalhes da aplicação de funções de entrada

  4. Clique no botão Criar.

  5. Depois de clicar em Criar, terá de aguardar que o serviço seja criado, o que poderá demorar um minuto.

  6. Será apresentada uma notificação no portal assim que a instância do Serviço for criada.

    nova notificação do portal do Azure

  7. Clique nas notificações para explorar a nova instância do Serviço.

    ir para a aplicação de funções de recurso

  8. Clique no botão Ir para recurso na notificação para explorar a nova instância do Serviço. Será levado para a nova instância do Serviço de Aplicações de Funções .

  9. No dashboard da Aplicação de Funções , paire o rato sobre As Funções, que se encontra no painel à esquerda e, em seguida, clique no símbolo + (mais ).

    criar nova função

  10. Na página seguinte, certifique-se de que Webhook + API está selecionado e, para Escolher um idioma, selecione CSharp, uma vez que este será o idioma utilizado neste tutorial. Por fim, clique no botão Criar esta função .

    selecionar web hook csharp

  11. Deve ser levado para a página de código (run.csx), caso contrário, clique na Função recém-criada na lista Funções no painel à esquerda.

    abrir nova função

  12. Copie o seguinte código para a sua função. Esta função irá simplesmente devolver um número inteiro aleatório entre 0 e 2 quando for chamada. Não se preocupe com o código existente, não hesite em colar por cima do mesmo.

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

  14. O resultado deverá ser semelhante à imagem abaixo.

  15. Clique em Obter URL da função e tome nota do ponto final apresentado. Terá de inseri-la na classe AzureServices que irá criar mais tarde neste curso.

    Obter ponto final de função

    Inserir ponto final de função

Capítulo 3 – Configurar o projeto do Unity

Segue-se uma configuração típica para programar com Mixed Reality e, como tal, é um bom modelo para outros projetos.

Configure e teste os auscultadores envolventes de realidade mista.

Nota

Não irá precisar de Comandos de Movimento para este curso. Se precisar de suporte para configurar os auscultadores envolventes, visite o artigo de configuração da realidade mista.

  1. Abra o Unity e clique em Novo.

    Criar novo projeto do Unity

  2. Agora, terá de fornecer um nome para o Projeto do Unity. Inserir MR_Azure_Functions. Certifique-se de que o tipo de projeto está definido como 3D. Defina a Localização para um local adequado para si (lembre-se de que é melhor aproximar-se dos diretórios de raiz). Em seguida, clique em Criar projeto.

    Atribuir um nome a um novo projeto do Unity

  3. Com o Unity aberto, vale a pena verificar se o Editor de Scripts predefinido está definido como Visual Studio. Aceda a Editar>Preferências e, em seguida, a partir da nova janela, navegue para Ferramentas Externas. Altere o Editor de Scripts Externos para o Visual Studio 2017. Feche a janela Preferências .

    definir o Visual Studio como editor de scripts

  4. Em seguida, aceda aDefinições de Compilação de Ficheiros> e mude a plataforma para Plataforma Universal do Windows, clicando no botão Mudar de Plataforma.

    mudar de plataforma para uwp

  5. Aceda aDefinições de Compilação de Ficheiros> e certifique-se de que:

    1. O Dispositivo de Destino está definido como Qualquer Dispositivo.

      Para Microsoft HoloLens, defina Dispositivo de Destino como HoloLens.

    2. O Tipo de Compilação está definido como D3D

    3. O SDK está definido como Instalado mais recentemente

    4. A Versão do Visual Studio está definida como Mais Recente instalada

    5. Compilar e Executar está definido como Computador Local

    6. Guarde a cena e adicione-a à compilação.

      1. Para tal, selecione Adicionar Cenas Abertas. Será apresentada uma janela guardar.

        adicionar cenas abertas

      2. Crie uma nova pasta para esta e qualquer cenário futuro e, em seguida, selecione o botão Nova pasta , para criar uma nova pasta, atribua-lhe o nome Cenas.

        criar pasta de cenas

      3. Abra a pasta Cenas recém-criada e, em seguida, no campo Nome do ficheiro : texto, escreva FunctionsScene e, em seguida, prima Guardar.

        Cenário Guardar funções

  6. As restantes definições, em Definições de Compilação, devem ser deixadas como predefinição por agora.

    Mantenha as predefinições de compilação

  7. Na janela Definições de Compilação, clique no botão Definições do Leitor . Esta ação abrirá o painel relacionado no espaço onde se encontra o Inspetor .

    definições do leitor no inspetor

  8. Neste painel, é necessário verificar algumas definições:

    1. No separador Outras Definições :

      1. A Versão do Runtime de Scripting deve ser Experimental (.NET 4.6 Equivalente), o que irá acionar a necessidade de reiniciar o Editor.
      2. O Back-end de Scripting deve ser .NET
      3. O Nível de Compatibilidade de API deve ser .NET 4.6
    2. No separador Definições de Publicação , em Capacidades, verifique:

      • InternetClient

        definir capacidades

    3. Mais abaixo no painel, em Definições XR (encontradas abaixo das Definições de Publicação), assinale Realidade Virtual Suportada, certifique-se de que o SDK Windows Mixed Reality é adicionado.

      definir definições XR

  9. Novamente em Definições de Compilação, o Unity C# Projects já não está a cinzento; marque a caixa de verificação junto a esta.

    marcar projetos c#

  10. Feche a janela Definições de Compilação.

  11. Guarde a cena e o projeto (CENA DE GUARDAR FICHEIRO>/PROJETO GUARDARFICHEIRO>).

Capítulo 4 - Configurar a Câmara Principal

Importante

Se quiser ignorar os componentes da Configuração do Unity deste curso e continuar diretamente para o código, pode transferir este .unitypackage e importá-lo para o seu projeto como um Pacote Personalizado. Isto também conterá as DLLs do próximo Capítulo. Após a importação, continue a partir do Capítulo 7.

  1. No Painel hierarquia, encontrará um objeto chamado Câmara Principal, este objeto representa o seu ponto de vista "cabeça" assim que estiver "dentro" da sua aplicação.

  2. Com o Dashboard do Unity à sua frente, selecione o GameObject da Câmara Principal. Irá reparar que o Painel de Inspetores (geralmente encontrado à direita, no Dashboard) irá mostrar os vários componentes desse GameObject, com Transform na parte superior, seguido da Câmara e de outros componentes. Terá de repor a Transformação da Câmara Principal, para que esteja corretamente posicionada.

  3. Para tal, selecione o ícone Engrenagem junto ao componente Transformar da Câmara e selecione Repor.

    repor transformação

  4. Em seguida, atualize o componente Transformar para ter o seguinte aspeto:

Transformar - Posição

X Y Z
0 1 0

Transformar - Rotação

X Y Z
0 0 0

Transformar - Dimensionar

X Y Z
1 1 1

definir transformação da câmara

Capítulo 5 - Configurar a cena do Unity

  1. Clique com o botão direito do rato numa área vazia do Painel de Hierarquia, em Objeto 3D, adicione um Plano.

    criar novo plano

  2. Com o objeto Plano selecionado, altere os seguintes parâmetros no Painel de Inspetores:

Transformar - Posição

X Y Z
0 0 4

Transformar - Dimensionar

X Y Z
10 1 10

definir a posição e o dimensionamento do plano

vista de cena do plano

  1. Clique com o botão direito do rato numa área vazia do Painel de Hierarquia, em Objeto 3D, adicione um Cubo.

    1. Mude o nome do Cubo para GazeButton (com o Cubo selecionado, prima "F2").

    2. Altere os seguintes parâmetros para Transformar Posição no Painel de Inspetores:

      X Y Z
      0 3 5

      definir a transformação do botão de olhar

      vista de cena do botão de olhar

    3. Clique no botão pendente Etiqueta e clique em Adicionar Etiqueta para abrir o Painel Etiquetas & Camadas.

      adicionar nova etiqueta

      selecionar mais

    4. Selecione o botão + (mais) e, no campo Novo Nome da Etiqueta , introduza GazeButton e prima Guardar.

      nome de nova etiqueta

    5. Clique no objeto GazeButton no Painel de Hierarquia e, no Painel de Inspetores, atribua a etiqueta GazeButton recentemente criada.

      botão atribuir olhar a nova etiqueta

  2. Clique com o botão direito do rato no objeto GazeButton , no Painel hierarquia, e adicione um GameObject Vazio (que será adicionado como um objeto subordinado ).

  3. Selecione o novo objeto e mude-lhe o nome ShapeSpawnPoint.

    1. Altere os seguintes parâmetros para Transformar Posição no Painel de Inspetores:

      X Y Z
      0 -1 0

      atualizar a transformação do ponto de desova da forma

      vista de cena do ponto de desova da forma

  4. Em seguida, irá criar um objeto de Texto 3D para fornecer feedback sobre o estado do serviço do Azure.

    Clique com o botão direito do rato no GazeButton no Painel de Hierarquia novamente e adicione um objetode Texto 3D objeto >3Dem criança.

    criar novo objeto de texto 3D

  5. Mude o nome do objeto texto 3D para AzureStatusText.

  6. Altere o objeto AzureStatusTextTransform Position da seguinte forma:

    X Y Z
    0 0 -0,6
  7. Altere o objeto AzureStatusTextTransform Scale da seguinte forma: | X | Y | Z | | :---: | :---: | :---: | | 0.1 | 0.1 | 0.1 |

    Nota

    Não se preocupe se parecer estar fora do centro, uma vez que será corrigido quando o componente do Text Mesh abaixo for atualizado.

  8. Altere o componente malha de texto para corresponder ao seguinte:

    definir componente de malha de texto

    Dica

    A cor selecionada aqui é a cor Hexadecim: 000000FF, embora sinta-se à vontade para escolher a sua, apenas certifique-se de que é legível.

  9. A estrutura do Painel de Hierarquia deverá ter agora o seguinte aspeto:

    Malha de texto na hierarquia

  10. A sua cena deverá agora ter o seguinte aspeto:

    Malha de texto na vista de cena

Capítulo 6 – Importar o Armazenamento do Azure para o Unity

Irá utilizar o Armazenamento do Azure para o Unity (que tira partido do SDK .Net para o Azure). Pode ler mais sobre isto no artigo Armazenamento do Azure para Unity.

Atualmente, existe um problema conhecido no Unity que requer que os plug-ins sejam reconfigurados após a importação. Estes passos (4 a 7 nesta secção) deixarão de ser necessários depois de o erro ter sido resolvido.

Para importar o SDK para o seu próprio projeto, certifique-se de que transferiu o ".unitypackage" mais recente a partir do GitHub. Em seguida, faça o seguinte:

  1. Adicione o ficheiro .unitypackage ao Unity com a opção de menuPacote Personalizadode Importaçãode Recursos>>.

  2. Na caixa Importar Pacote do Unity apresentada, pode selecionar tudo em Armazenamento de Plug-ins>. Desmarque tudo o resto, uma vez que não é necessário para este curso.

    importar para o pacote

  3. Clique no botão Importar para adicionar os itens ao projeto.

  4. Aceda à pasta Armazenamentoem Plug-ins, na vista Projeto e selecione apenas os seguintes plug-ins:

    • Microsoft.Data.Edm

    • Microsoft.Data.OData

    • Microsoft.WindowsAzure.Storage

    • Newtonsoft.Json

    • System.Spatial

      desmarque Qualquer plataforma

  5. Com estes plug-ins específicos selecionados,desmarqueQualquer Plataforma e desmarqueWSAPlayer e, em seguida, clique em Aplicar.

    aplicar dlls de plataforma

    Nota

    Estamos a marcar estes plug-ins específicos para serem utilizados apenas no Editor do Unity. Isto acontece porque existem versões diferentes dos mesmos plug-ins na pasta WSA que serão utilizadas depois de o projeto ser exportado do Unity.

  6. Na pasta Plug-in de armazenamento , selecione apenas:

    • Microsoft.Data.Services.Client

      definir não processar para dlls

  7. Selecione a caixa Não Processar em Definições da Plataforma e clique em Aplicar.

    não aplicar processamento

    Nota

    Estamos a marcar este plug-in "Não processe" porque o patcher de assemblagem do Unity tem dificuldade em processar este plug-in. O plug-in continuará a funcionar mesmo que não seja processado.

Capítulo 7 - Criar a classe AzureServices

A primeira classe que vai criar é a classe AzureServices .

A classe AzureServices será responsável por:

  • Armazenar credenciais da Conta do Azure.

  • Chamar a função Aplicação Azure AD.

  • O carregamento e a transferência do ficheiro de dados no Armazenamento na Cloud do Azure.

Para criar esta Classe:

  1. Clique com o botão direito do rato na Pasta de Recursos, localizada no Painel de Projeto, Criar>Pasta. Atribua um nome aos Scripts da pasta.

    criar nova pasta

    pasta de chamadas - scripts

  2. Faça duplo clique na pasta que acabou de criar para abri-la.

  3. Clique com o botão direito do rato dentro da pasta Criar>Script C#. Chame o script AzureServices.

  4. Faça duplo clique na nova classe AzureServices para abri-la com o Visual Studio.

  5. Adicione os seguintes espaços de nomes à parte superior do 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. Adicione os seguintes Campos de Inspetor na classe 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. Em seguida, adicione as seguintes variáveis de membro na classe 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

    Confirme que substitui o ponto final e cadeia de ligação valores pelos valores do armazenamento do Azure, que se encontram no Portal do Azure

  8. O código para métodos Awake() e Start() tem agora de ser adicionado. Estes métodos serão chamados quando a classe inicializar:

        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

    Iremos preencher o código para CallAzureFunctionForNextShape() num capítulo futuro.

  9. Elimine o método Update(), uma vez que esta classe não irá utilizá-lo.

  10. Guarde as alterações no Visual Studio e, em seguida, regresse ao Unity.

  11. Clique e arraste a classe AzureServices da pasta Scripts para o objeto Câmara Principal no Painel de Hierarquia.

  12. Selecione a Câmara Principal e, em seguida, agarre o objeto subordinado AzureStatusText abaixo do objeto GazeButton e coloque-o no campo de destino de referência AzureStatusText , no Inspetor, para fornecer a referência ao script AzureServices .

    atribuir destino de referência de texto de estado do azure

Capítulo 8 - Criar a classe ShapeFactory

O script seguinte a criar é a classe ShapeFactory . A função desta classe é criar uma nova forma, quando solicitado, e manter um histórico das formas criadas numa Lista do Histórico de Formas. Sempre que uma forma é criada, a lista Histórico de Formas é atualizada na classe AzureService e, em seguida, armazenada no Armazenamento do Azure. Quando a aplicação é iniciada, se for encontrado um ficheiro armazenado no Armazenamento do Azure, a lista Histórico de Formas é obtida e reproduzida, com o objeto Texto 3D a indicar se a forma gerada é de armazenamento ou nova.

Para criar esta classe:

  1. Aceda à pasta Scripts que criou anteriormente.

  2. Clique com o botão direito do rato dentro da pasta Criar>Script C#. Chame o script ShapeFactory.

  3. Faça duplo clique no novo script ShapeFactory para o abrir com o Visual Studio.

  4. Certifique-se de que a classe ShapeFactory inclui os seguintes espaços de nomes:

        using System.Collections.Generic;
        using UnityEngine;
    
  5. Adicione as variáveis apresentadas abaixo à classe ShapeFactory e substitua as funções Start() e Awake() pelas seguintes:

        /// <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. O método CreateShape() gera as formas primitivas, com base no parâmetro inteiro fornecido. O parâmetro Booleano é utilizado para especificar se a forma atualmente criada é de armazenamento ou nova. Coloque o seguinte código na classe ShapeFactory , abaixo dos 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. Certifique-se de que guarda as alterações no Visual Studio antes de regressar ao Unity.

  8. No Editor do Unity, clique e arraste a classe ShapeFactory da pasta Scripts para o objeto Câmara Principal no Painel de Hierarquia.

  9. Com a Câmara Principal selecionada, irá reparar que o componente de script ShapeFactory não tem a referência de Ponto de Desova. Para o corrigir, arraste o objeto ShapeSpawnPoint do Painel de Hierarquia para o destino de referência ponto de desova.

    definir destino de referência de fábrica de formas

Capítulo 9 - Criar a classe Gaze

O último script que precisa de criar é a classe Gaze .

Esta classe é responsável por criar um Raycast que será projetado para a frente a partir da Câmara Principal, para detetar o objeto que o utilizador está a ver. Neste caso, o Raycast terá de identificar se o utilizador está a olhar para o objeto GazeButton na cena e acionar um comportamento.

Para criar esta Classe:

  1. Aceda à pasta Scripts que criou anteriormente.

  2. Clique com o botão direito do rato no Painel de Projeto, Criar>Script C#. Chame o script Gaze.

  3. Faça duplo clique no novo script Gaze para o abrir com o Visual Studio.

  4. Certifique-se de que o seguinte espaço de nomes está incluído na parte superior do script:

        using UnityEngine;
    
  5. Em seguida, adicione as seguintes variáveis dentro da classe 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

Algumas destas variáveis poderão ser editadas no Editor.

  1. O código para os métodos Awake() e Start() tem agora de ser adicionado.

        /// <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. Adicione o seguinte código, que irá criar um objeto de cursor no início, juntamente com o método Update(), que irá executar o método Raycast, juntamente com o local onde o booleano GazeEnabled está ativado:

        /// <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. Em seguida, adicione o método UpdateRaycast(), que projetará um Raycast e detetará o destino atingido.

        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 fim, adicione o método ResetFocusedObject(), que irá alternar a cor atual dos objetos GazeButton, indicando se está ou não a criar uma nova forma.

        /// <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 as alterações no Visual Studio antes de regressar ao Unity.

  6. Clique e arraste a classe Gaze da pasta Scripts para o objeto Câmara Principal no Painel de Hierarquia.

Capítulo 10 - Concluir a classe AzureServices

Com os outros scripts implementados, agora é possível concluir a classe AzureServices . Isto será conseguido através de:

  1. Adicionar um novo método denominado CreateCloudIdentityAsync(), para configurar as variáveis de autenticação necessárias para comunicar com o Azure.

    Este método também verificará a existência de um Ficheiro armazenado anteriormente que contenha a Lista de Formas.

    Se o ficheiro for encontrado, desativará o utilizador Gaze e acionará a criação da Forma, de acordo com o padrão das formas, conforme armazenado no ficheiro de Armazenamento do Azure. O utilizador pode ver isto, uma vez que a Malha de Texto fornecerá o ecrã "Armazenamento" ou "Novo", dependendo da origem das formas.

    Se não for encontrado nenhum ficheiro, irá ativar o Gaze, permitindo que o utilizador crie formas ao observar o objeto GazeButton na cena.

        /// <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. O fragmento de código seguinte é proveniente do método Start(). em que será feita uma chamada para o método CreateCloudIdentityAsync( ). Não hesite em copiar o método Start() atual, com o seguinte:

        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. Preencha o código do método CallAzureFunctionForNextShape(). Irá utilizar a Aplicação de Funções do Azure criada anteriormente para pedir um índice de formas. Assim que a nova forma for recebida, este método enviará a forma para a classe ShapeFactory para criar a nova forma na cena. Utilize o código abaixo para concluir o corpo 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. Adicione um método para criar uma cadeia, concatenando os números inteiros armazenados na lista do histórico de formas e guardando-o no Ficheiro de Armazenamento do Azure.

        /// <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. Adicione um método para obter o texto armazenado no ficheiro localizado no Ficheiro de Armazenamento do Azure e anular a serialização numa lista.

  6. Assim que este processo estiver concluído, o método reativa o olhar atento para que o utilizador possa adicionar mais formas à cena.

        ///<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 as alterações no Visual Studio antes de regressar ao Unity.

Capítulo 11 - Criar a Solução UWP

Para iniciar o processo de Compilação:

  1. Aceda aDefinições de Compilação de Ficheiros>.

    criar a aplicação

  2. Clique em Compilar. O Unity irá iniciar uma janela de Explorador de Ficheiros, onde tem de criar e, em seguida, selecionar uma pasta para compilar a aplicação. Crie essa pasta agora e dê-lhe o nome Aplicação. Em seguida, com a pasta Aplicação selecionada, prima Selecionar Pasta.

  3. O Unity começará a criar o seu projeto para a pasta Aplicação .

  4. Assim que o Unity terminar a criação (poderá demorar algum tempo), abrirá uma janela de Explorador de Ficheiros na localização da compilação (verifique a barra de tarefas, uma vez que pode nem sempre aparecer acima das janelas, mas irá notificá-lo sobre a adição de uma nova janela).

Capítulo 12 - Implementar a sua aplicação

Para implementar a sua aplicação:

  1. Navegue para a pasta Aplicação que foi criada no último Capítulo. Verá um ficheiro com o nome das suas aplicações, com a extensão ".sln", na qual deve fazer duplo clique, para o abrir no Visual Studio.

  2. Na Plataforma de Soluções, selecione x86, Máquina Local.

  3. Na Configuração da Solução , selecione Depurar.

    Para o Microsoft HoloLens, poderá ser mais fácil defini-lo como Máquina Remota, para que não esteja amarrado ao seu computador. No entanto, também terá de fazer o seguinte:

    • Conheça o Endereço IP do HoloLens, que pode ser encontrado nas Definiçõesde Rede &Opções Avançadas>de Wi-Fi> da Internet>; o IPv4 é o endereço que deve utilizar.
    • Certifique-se de que o Modo de Programador está Ativado; encontrado em Definições>Atualização & Segurança>para programadores.

    implementar solução

  4. Aceda ao menu Compilar e clique em Implementar Solução para fazer sideload da aplicação para o seu computador.

  5. A sua Aplicação deverá agora aparecer na lista de aplicações instaladas, prontas para serem iniciadas e testadas!

A Funções do Azure e a Aplicação de Armazenamento concluídas

Parabéns, criou uma aplicação de realidade mista que tira partido do Funções do Azure e dos serviços de Armazenamento do Azure. A sua aplicação poderá desenhar em dados armazenados e fornecer uma ação com base nesses dados.

final product -end

Exercícios de bónus

Exercício 1

Crie um segundo ponto de desova e registe o ponto de desova a partir do qual um objeto foi criado. Quando carrega o ficheiro de dados, reproduza as formas geradas a partir da localização em que foram criadas originalmente.

Exercício 2

Crie uma forma de reiniciar a aplicação, em vez de a voltar a abrir de cada vez. Carregar Cenas é um bom ponto de partida. Depois de o fazer, crie uma forma de limpar a lista armazenada no Armazenamento do Azure, para que possa ser facilmente reposta a partir da sua aplicação.