Partilhar via


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


Nota

Os tutoriais da Academia de Realidade Mista foram projetados com HoloLens (1ª geração) e Headsets Imersivos de Realidade Mista em mente. Como tal, sentimos que é importante deixar estes tutoriais no lugar para desenvolvedores que ainda estão procurando orientação no desenvolvimento para esses dispositivos. Esses tutoriais não serão atualizados com os conjuntos de ferramentas ou interações mais recentes que estão sendo usados para o HoloLens 2. Eles serão mantidos para continuar trabalhando nos dispositivos suportados. Haverá uma nova série de tutoriais que serão publicados no futuro que demonstrarão como desenvolver para o HoloLens 2. Este aviso será atualizado com um link para esses tutoriais quando eles forem publicados.


produto final -start

Neste curso, você aprenderá como criar e usar o Azure Functions e armazenar dados com um recurso de Armazenamento do Azure, dentro de um aplicativo de realidade mista.

O Azure Functions é um serviço da Microsoft, que permite que os desenvolvedores executem pequenos pedaços de código, 'funções', no Azure. Isso fornece uma maneira de delegar o trabalho para a nuvem, em vez de seu aplicativo local, que pode ter muitos benefícios. O Azure Functions suporta várias linguagens de desenvolvimento, incluindo C#, F#, Node.js, Java e PHP. Para obter mais informações, visite o artigo Azure Functions.

O Armazenamento do Azure é um serviço de nuvem da Microsoft, que permite aos desenvolvedores armazenar dados, com a garantia de que eles serão altamente disponíveis, seguros, duráveis, escaláveis e redundantes. Isso significa que a Microsoft lidará com toda a manutenção e problemas críticos para você. Para obter mais informações, visite o artigo Armazenamento do Azure.

Tendo concluído este curso, você terá um aplicativo de fone de ouvido imersivo de realidade mista que será capaz de fazer o seguinte:

  1. Permita que o usuário olhe em torno de uma cena.
  2. Acione a desova de objetos quando o usuário olha para um 'botão' 3D.
  3. Os objetos gerados serão escolhidos por uma Função do Azure.
  4. À medida que cada objeto é gerado, o aplicativo armazenará o tipo de objeto em um Arquivo do Azure, localizado no Armazenamento do Azure.
  5. Ao carregar uma segunda vez, os dados do Arquivo do Azure serão recuperados e usados para reproduzir as ações de desova da instância anterior do aplicativo.

Na sua aplicação, cabe-lhe a si decidir como irá integrar os resultados com o seu design. Este curso foi criado para ensiná-lo a integrar um Serviço do Azure ao seu Projeto Unity. É seu trabalho usar o conhecimento que você ganha com este curso para melhorar sua aplicação de realidade mista.

Suporte de dispositivos

Curso HoloLens Auriculares imersivos
MR e Azure 305: Funções e armazenamento ✔️ ✔️

Nota

Embora este curso se concentre principalmente em fones de ouvido imersivos (VR) do Windows Mixed Reality, você também pode aplicar o que aprendeu neste curso ao Microsoft HoloLens. À medida que você acompanha o curso, você verá anotações sobre quaisquer alterações que você possa precisar empregar para dar suporte ao HoloLens.

Pré-requisitos

Nota

Este tutorial foi projetado para desenvolvedores que têm experiência básica com Unity e C#. Por favor, esteja ciente de que os pré-requisitos e instruções escritas neste documento representam o que foi testado e verificado no momento da redação (maio de 2018). Você é livre para usar 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 você encontrará em software mais recente do que o listado abaixo.

Recomendamos o seguinte hardware e software para este curso:

Antes de começar

Para evitar encontrar problemas ao criar este projeto, é altamente recomendável que você crie o projeto mencionado neste tutorial em uma pasta raiz ou quase raiz (caminhos de pasta longos podem causar problemas em tempo de compilação).

Capítulo 1 - O Portal do Azure

Para usar o Serviço de Armazenamento do Azure, você precisará 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 você estiver seguindo este tutorial em uma situação de sala de aula ou laboratório, peça ajuda ao seu instrutor ou a um dos proctors para configurar sua nova conta.

  2. Depois de iniciar sessão, clique em Novo no canto superior esquerdo, procure por 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 fornecerá uma descrição do serviço de conta de Armazenamento do Azure. No canto inferior esquerdo deste prompt, 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, esteja ciente de que este campo só aceita números e letras minúsculas.

    2. Em Modelo de implantação, selecione Gerenciador de recursos.

    3. Para Tipo de conta, selecione Armazenamento (finalidade geral v1).

    4. Determine a Localização do seu grupo de recursos (se estiver a criar um novo Grupo de Recursos). O ideal seria que o local fosse na região onde o aplicativo seria executado. Alguns ativos do Azure só estão disponíveis em determinadas regiões.

    5. Em Replicação, selecione Read-access-geo-redundant storage (RA-GRS).

    6. Em Desempenho, selecione Standard.

    7. Deixe a 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 maneira de monitorar, controlar o acesso, provisionar e gerenciar a cobrança de uma coleção de ativos do Azure. É recomendável manter todos os serviços do Azure associados a um único projeto (por exemplo, como esses laboratórios) em um grupo de recursos comum).

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

    10. Você também precisará 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, você terá que esperar que o serviço seja criado, isso pode levar um minuto.

  6. Uma notificação aparecerá no portal assim que a instância de serviço for criada.

    Nova notificação no portal do Azure

  7. Clique nas notificações para explorar sua nova instância de serviço.

    ir para o recurso

  8. Clique no botão Ir para recurso na notificação para explorar sua nova instância de serviço. Você será levado para sua nova instância de serviço de conta de armazenamento.

    chaves de acesso

  9. Clique em Chaves de acesso para revelar os pontos de extremidade para este serviço de nuvem. Use o Bloco de Notas ou similar, para copiar uma das suas teclas para usar mais tarde. Além disso, observe o valor da cadeia de conexão , pois ele será usado na classe AzureServices , que você criará posteriormente.

    Copiar cadeia de conexão

Capítulo 2 - Configurando uma função do Azure

Agora você escreverá uma Função do Azure no Serviço do Azure.

Você pode usar uma Função do Azure para fazer quase tudo o que faria com uma função clássica em seu código, a diferença é que essa função pode ser acessada por qualquer aplicativo que tenha credenciais para acessar sua Conta do Azure.

Para criar uma Função do Azure:

  1. No Portal do Azure, clique em Novo no canto superior esquerdo, procure Aplicativo de Função e clique em Enter.

    criar aplicativo de função

    Nota

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

  2. A nova página fornecerá uma descrição do serviço Aplicativo de Função do Azure. No canto inferior esquerdo deste prompt, selecione o botão Criar para criar uma associação com este serviço.

    informações do aplicativo de função

  3. Depois de clicar em Criar:

    1. Forneça um nome de aplicativo. Apenas letras e números podem ser usados aqui (maiúsculas ou minúsculas são permitidas).

    2. Selecione a sua Subscrição preferida.

    3. Escolha um Grupo de Recursos ou crie um novo. Um grupo de recursos fornece uma maneira de monitorar, controlar o acesso, provisionar e gerenciar a cobrança de uma coleção de ativos do Azure. É recomendável manter todos os serviços do Azure associados a um único projeto (por exemplo, como esses laboratórios) em um grupo de recursos comum).

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

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

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

    6. Determine a Localização do seu grupo de recursos (se estiver a criar um novo Grupo de Recursos). O ideal seria que o local fosse na região onde o aplicativo seria executado. Alguns ativos do Azure só estão disponíveis em determinadas regiões. Para um desempenho ideal, selecione a mesma região da conta de armazenamento.

    7. Em Armazenamento, selecione Usar existente e, usando o menu suspenso, localize o armazenamento criado anteriormente.

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

      Detalhes do aplicativo da função de entrada

  4. Clique no botão Criar.

  5. Depois de clicar em Criar, você terá que esperar que o serviço seja criado, isso pode levar um minuto.

  6. Uma notificação aparecerá no portal assim que a instância de serviço for criada.

    Nova notificação do portal do Azure

  7. Clique nas notificações para explorar sua nova instância de serviço.

    Ir para a aplicação da função de recurso

  8. Clique no botão Ir para recurso na notificação para explorar sua nova instância de serviço. Você será direcionado para sua nova instância do serviço Aplicativo de Função.

  9. No painel do Aplicativo de Funções, passe o mouse sobre Funções, localizado no painel à esquerda, e clique no símbolo + (mais).

    Criar nova função

  10. Na próxima página, verifique se Webhook + API está selecionado e, para Escolha um idioma, selecione CSharp, pois esse será o idioma usado para este tutorial. Por fim, clique no botão Criar esta função .

    Selecione Web Hook Csharp

  11. Você deve ser levado para a página de código (run.csx), se não, clique na função recém-criada na lista Funções dentro do painel à esquerda.

    abrir nova função

  12. Copie o seguinte código para a sua função. Esta função simplesmente retornará um inteiro aleatório entre 0 e 2 quando chamado. Não se preocupe com o código existente, sinta-se à vontade para colar por cima dele.

        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 deve ser parecido com a imagem abaixo.

  15. Clique em Obter URL da função e tome nota do ponto de extremidade exibido. Você precisará inseri-lo na classe AzureServices que você criará posteriormente neste curso.

    Obter ponto final de função

    Inserir ponto final da função

Capítulo 3 - Criação do projeto Unity

O seguinte é uma configuração típica para desenvolver com Realidade Mista e, como tal, é um bom modelo para outros projetos.

Configure e teste o seu auricular imersivo de realidade mista.

Nota

Você não precisará de controladores de movimento para este curso. Se você precisar de suporte para configurar o fone de ouvido imersivo, visite o artigo de configuração de realidade mista.

  1. Abra o Unity e clique em Novo.

    Criar novo projeto de unidade

  2. Agora você precisará fornecer um nome de Projeto Unity. Inserir MR_Azure_Functions. Verifique se o tipo de projeto está definido como 3D. Defina o Local para algum lugar apropriado para você (lembre-se, mais perto de diretórios raiz é melhor). Em seguida, clique em Criar projeto.

    Dê um nome ao novo projeto de unidade

  3. Com o Unity aberto, vale a pena verificar se o Editor de Scripts padrão está definido como Visual Studio. Vá para Editar>Preferências e, na nova janela, navegue até Ferramentas Externas. Altere o Editor de Scripts Externo para Visual Studio 2017. Feche a janela Preferências .

    Definir o Visual Studio como editor de scripts

  4. Em seguida, vá para Configurações de compilação de arquivos>e mude a plataforma para a Plataforma Universal do Windows, clicando no botão Alternar plataforma.

    Mudar de plataforma para UWP

  5. Vá para Configurações de compilação de arquivo>e certifique-se de que:

    1. Dispositivo de destino está definido como Qualquer dispositivo.

      Para Microsoft HoloLens, defina Target Device como HoloLens.

    2. O tipo de compilação está definido como D3D

    3. O SDK está definido como Instalado mais recente

    4. Versão do Visual Studio está definida como A versão mais recente instalada

    5. Build and Run está definido como Máquina Local

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

      1. Faça isso selecionando Adicionar cenas abertas. Será exibida uma janela de salvamento.

        Adicionar cenas abertas

      2. Crie uma nova pasta para esta e qualquer cena futura e, em seguida, selecione o botão Nova pasta , para criar uma nova pasta, nomeie-a Cenas.

        criar pasta de cenas

      3. Abra a pasta Cenas recém-criada e, no campo de texto Nome do arquivo:, digite FunctionsScene e pressione Salvar.

        Salvar cena de funções

  6. As configurações restantes, em Configurações de compilação, devem ser deixadas como padrão por enquanto.

    Deixe as configurações de compilação padrão

  7. Na janela Configurações de compilação, clique no botão Configurações do player, isso abrirá o painel relacionado no espaço onde o inspetor está localizado.

    Configurações do Player no Inspetor

  8. Neste painel, algumas configurações precisam ser verificadas:

    1. Na guia Outras configurações:

      1. A versão do Scripting Runtime deve ser experimental (equivalente ao .NET 4.6), o que acionará a necessidade de reiniciar o Editor.
      2. O back-end de scripts deve ser .NET
      3. O nível de compatibilidade da API deve ser .NET 4.6
    2. Na guia Configurações de publicação , em Recursos, verifique:

      • InternetClient

        Definir capacidades

    3. Mais abaixo no painel, em Configurações XR (encontradas abaixo de Configurações de publicação), marque Realidade Virtual suportada, verifique se o SDK de realidade mista do Windows foi adicionado.

      definir configurações de XR

  9. De volta às configurações de compilação, o Unity C# Projects não está mais acinzentado, marque a caixa de seleção ao lado disso.

    Marcar projetos C#

  10. Feche a janela Configurações de compilação.

  11. Salve sua cena e projeto (FILE>SAVE SCENE / FILE>SAVE PROJECT).

Capítulo 4 - Configuração da câmera principal

Importante

Se você deseja pular os componentes Unity set deste curso e continuar direto no código, sinta-se à vontade para baixar este .unitypackage e importá-lo para seu projeto como um pacote personalizado. Isso também conterá as DLLs do próximo capítulo. Após a importação, continuar a partir do Capítulo 7.

  1. No Painel de Hierarquia, você encontrará um objeto chamado Câmara Principal, este objeto representa o seu ponto de vista "cabeça" uma vez que você está "dentro" do seu aplicativo.

  2. Com o Painel Unity à sua frente, selecione o GameObject da câmera principal. Você notará que o Painel do Inspetor (geralmente encontrado à direita, dentro do Painel) mostrará os vários componentes desse GameObject, com Transform na parte superior, seguido por Camera e alguns outros componentes. Você precisará redefinir a transformação da câmera principal, para que ela esteja posicionada corretamente.

  3. Para fazer isso, selecione o ícone de engrenagem ao lado do componente Transformar da câmera e selecione Redefinir.

    redefinir transformação

  4. Em seguida, atualize o componente Transformar para ter a seguinte aparência:

Transformar - Posição

X Y Z
0 1 0

Transformar - Rotação

X Y Z
0 0 0

Transformar - Escala

X Y Z
1 1 1

Definir transformação de câmera

Capítulo 5 - Configurando a cena Unity

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

    Criar novo plano

  2. Com o objeto Plane selecionado, altere os seguintes parâmetros no Painel do Inspetor:

Transformar - Posição

X Y Z
0 0 4

Transformar - Escala

X Y Z
10 1 10

definir a posição e a escala do plano

Vista de cena do avião

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

    1. Renomeie o cubo para GazeButton (com o cubo selecionado, pressione 'F2').

    2. Altere os seguintes parâmetros para Transformar posição no painel Inspetor:

      X Y Z
      0 3 5

      definir transformação do botão de olhar

      Visualização de cena do botão de olhar

    3. Clique no botão suspenso Tag e clique em Add Tag para abrir o painel Tags & Layers.

      adicionar nova tag

      selecione mais

    4. Selecione o botão + (mais) e, no campo Novo nome da tag, digite GazeButton e pressione Salvar.

      nomear nova tag

    5. Clique no objeto GazeButton no Painel Hierarquia e, no Painel Inspetor, atribua a tag GazeButton recém-criada.

      atribuir o botão olhar à nova etiqueta

  2. Clique com o botão direito do mouse no objeto GazeButton, no Painel Hierarquia, e adicione um Empty GameObject (que será adicionado como um objeto filho).

  3. Selecione o novo objeto e renomeie-o ShapeSpawnPoint.

    1. Altere os seguintes parâmetros para Transformar posição no painel Inspetor:

      X Y Z
      0 -1 0

      Atualizar transformação de ponto de desova de forma

      modo de exibição de cena do ponto de desova de forma

  4. Em seguida, você criará um objeto Texto 3D para fornecer comentários sobre o status do serviço do Azure.

    Clique com o botão direito do mouse no GazeButton no painel Hierarquia novamente e adicione um objeto 3D Object>3D Text como uma criança.

    criar novo objeto de texto 3D

  5. Renomeie o objeto 3D Text para AzureStatusText.

  6. Altere a posição de transformação do objeto AzureStatusText da seguinte maneira:

    X Y Z
    0 0 -0.6
  7. Altere a escala de transformação do objeto AzureStatusText da seguinte maneira: | X | Y | Z | | :---: | :---: | :---: | | 0,1 | 0,1 | 0,1 |

    Nota

    Não se preocupe se ele parecer estar fora do centro, pois isso será corrigido quando o componente Text Mesh abaixo for atualizado.

  8. Altere o componente Malha de texto para corresponder ao abaixo:

    Definir componente de malha de texto

    Gorjeta

    A cor selecionada aqui é a cor hexadecimal: 000000FF, embora sinta-se livre para escolher o seu próprio, apenas certifique-se de que é legível.

  9. Sua estrutura do Painel de Hierarquia agora deve ter esta aparência:

    Malha de texto na hierarquia

  10. Sua cena agora deve ter esta aparência:

    Malha de texto na vista de cena

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

Você usará o Armazenamento do Azure para Unity (que por si só aproveita o SDK do .Net para Azure). Você pode ler mais sobre isso no artigo Armazenamento do Azure para Unity.

Atualmente, há um problema conhecido no Unity que exige que os plug-ins sejam reconfigurados após a importação. Essas etapas (4 - 7 nesta seção) não serão mais necessárias depois que o bug for resolvido.

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

  1. Adicione o arquivo .unitypackage ao Unity usando a opção de menu Pacote personalizado do pacote> de importação de ativos.>

  2. Na caixa Import Unity Package que aparece, você pode selecionar tudo em Plugin>Storage. Desmarque todo o resto, pois não é necessário para este curso.

    importar para o pacote

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

  4. Vá para a pasta Armazenamento em Plugins, na visualização Projeto, e selecione apenas os seguintes plug-ins:

    • Microsoft.Data.Edm

    • Microsoft.Data.OData

    • Microsoft.WindowsAzure.Storage

    • Newtonsoft.Json

    • Sistema.Espacial

      desmarque Qualquer plataforma

  5. Com esses plug-ins específicos selecionados, desmarque Qualquer plataforma e desmarque WSAPlayer e clique em Aplicar.

    Aplicar DLLs de plataforma

    Nota

    Estamos marcando esses plugins específicos para serem usados apenas no Editor Unity. Isso ocorre porque existem versões diferentes dos mesmos plug-ins na pasta WSA que serão usadas depois que o projeto for exportado do Unity.

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

    • Microsoft.Data.Services.Client

      Definir Não processar para DLLs

  7. Marque a caixa Não processar em Configurações da plataforma e clique em Aplicar.

    Aplicar sem processamento

    Nota

    Estamos marcando este plugin "Não processar" porque o patcher de montagem Unity tem dificuldade em processar este plugin. O plugin ainda funcionará mesmo que não seja processado.

Capítulo 7 - Criar a classe AzureServices

A primeira classe que você vai criar é a classe AzureServices .

A classe AzureServices será responsável por:

  • Armazenando credenciais da Conta do Azure.

  • Chamando sua Função de Aplicativo do Azure.

  • O carregamento e o download do arquivo de dados em seu Armazenamento em Nuvem do Azure.

Para criar esta classe:

  1. Clique com o botão direito do mouse na Pasta de ativos, localizada no Painel do projeto, Criar>pasta. Nomeie a pasta Scripts.

    Criar nova pasta

    Pasta de chamadas - scripts

  2. Clique duas vezes na pasta recém-criada, para abri-la.

  3. Clique com o botão direito do mouse dentro da pasta, Create>C# Script. Chame o script AzureServices.

  4. Clique duas vezes na nova classe AzureServices para abri-la com o Visual Studio.

  5. Adicione os seguintes namespaces à parte superior dos 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 do Inspetor dentro da 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 dentro da 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

    Certifique-se de substituir os valores de ponto de extremidade e cadeia de conexão pelos valores do seu armazenamento do Azure, encontrados no Portal do Azure

  8. O código para os métodos Awake() e Start() agora precisa ser adicionado. Esses 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

    Preencheremos o código para CallAzureFunctionForNextShape() em um capítulo futuro.

  9. Exclua o método Update(), pois essa classe não o usará.

  10. Salve suas alterações no Visual Studio e retorne ao Unity.

  11. Clique e arraste a classe AzureServices da pasta Scripts para o objeto Main Camera no Painel de Hierarquia.

  12. Selecione a Câmara Principal e, em seguida, pegue o objeto filho AzureStatusText abaixo do objeto GazeButton e coloque-o dentro do 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 status do Azure

Capítulo 8 - Criar a classe ShapeFactory

O próximo script a ser criado é a classe ShapeFactory . A função dessa classe é criar uma nova forma, quando solicitado, e manter um histórico das formas criadas em uma Lista de 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 o aplicativo é iniciado, se um arquivo armazenado for encontrado em seu Armazenamento do Azure, a lista Histórico de Formas será recuperada e reproduzida, com o objeto Texto 3D fornecendo se a forma gerada é do armazenamento ou nova.

Para criar esta classe:

  1. Vá para a pasta Scripts que você criou anteriormente.

  2. Clique com o botão direito do mouse dentro da pasta, Create>C# Script. Chame o script de ShapeFactory.

  3. Clique duas vezes no novo script ShapeFactory para abri-lo com o Visual Studio.

  4. Verifique se a classe ShapeFactory inclui os seguintes namespaces:

        using System.Collections.Generic;
        using UnityEngine;
    
  5. Adicione as variáveis mostradas 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 Boolean é usado para especificar se a forma criada atualmente é do armazenamento ou nova. Coloque o seguinte código em sua 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 salvar suas alterações no Visual Studio antes de retornar ao Unity.

  8. De volta ao Editor Unity, clique e arraste a classe ShapeFactory da pasta Scripts para o objeto Main Camera no Painel Hierarchy.

  9. Com a câmera principal selecionada, você notará que o componente de script ShapeFactory está faltando a referência Spawn Point . Para corrigi-lo, arraste o objeto ShapeSpawnPoint do Painel de Hierarquia para o destino de referência do Ponto de Desova.

    Definir destino de referência de fábrica de forma

Capítulo 9 - Criar a classe Gaze

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

Esta classe é responsável por criar um Raycast que será projetado para a frente a partir da câmera principal, para detetar qual objeto o usuário está olhando. Nesse caso, o Raycast precisará identificar se o usuário está olhando para o objeto GazeButton na cena e acionar um comportamento.

Para criar esta classe:

  1. Vá para a pasta Scripts que você criou anteriormente.

  2. Clique com o botão direito do mouse no Painel Projeto, Criar>Script C#. Chame o roteiro de Olhar.

  3. Clique duas vezes no novo script Gaze para abri-lo com o Visual Studio.

  4. Verifique se o namespace a seguir 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 dessas variáveis poderão ser editadas no Editor.

  1. O código para os métodos Awake() e Start() agora precisa 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 criará um objeto cursor no início, juntamente com o método Update(), que executará o método Raycast, além de ser onde o booleano GazeEnabled é alternado:

        /// <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 alvo 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 alternará a cor atual dos objetos GazeButton, indicando se está criando uma nova forma ou não.

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

  6. Clique e arraste a classe Gaze da pasta Scripts para o objeto Main Camera no Painel Hierarchy.

Capítulo 10 - Concluindo a classe AzureServices

Com os outros scripts em vigor, agora é possível concluir a classe AzureServices . Este objetivo será atingido mediante:

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

    Esse método também verificará a existência de um arquivo armazenado anteriormente contendo a Lista de Formas.

    Se o arquivo for encontrado, ele desabilitará o usuário Olhar e acionará a criação de Formas, de acordo com o padrão de formas, conforme armazenado no arquivo de Armazenamento do Azure. O usuário pode ver isso, pois a malha de texto fornecerá exibição 'Armazenamento' ou 'Novo', dependendo da origem das formas.

    Se nenhum arquivo for encontrado, ele habilitará o Olhar, permitindo que o usuário crie formas ao olhar para 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 próximo trecho de código é de dentro do método Start(), no qual uma chamada será feita para o método CreateCloudIdentityAsync(). Sinta-se à vontade para copiar o seu 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 para o método CallAzureFunctionForNextShape(). Você usará o Aplicativo de Função do Azure criado anteriormente para solicitar um índice de forma. Depois que a nova forma for recebida, esse método enviará a forma para a classe ShapeFactory para criar a nova forma na cena. Use 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 de caracteres, concatenando os inteiros armazenados na lista de histórico de formas e salvando-a em seu Arquivo 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 recuperar o texto armazenado no arquivo localizado em seu Arquivo de Armazenamento do Azure e desserialize-o em uma lista.

  6. Uma vez concluído esse processo, o método reativa o olhar para que o usuário 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. Salve suas alterações no Visual Studio antes de retornar ao Unity.

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

Para iniciar o processo de compilação:

  1. Vá para Configurações de compilação de arquivo>.

    Crie o aplicativo

  2. Clique em Compilar. Unity irá iniciar uma janela do Explorador de Arquivos, onde você precisa criar e, em seguida, selecionar uma pasta para construir o aplicativo. Crie essa pasta agora e nomeie-a como App. Em seguida, com a pasta App selecionada, pressione Select Folder.

  3. Unity começará a construir seu projeto para a pasta App .

  4. Assim que o Unity terminar de construir (pode levar algum tempo), ele abrirá uma janela do Explorador de Arquivos no local da sua compilação (verifique sua barra de tarefas, pois nem sempre ela aparecerá acima de suas janelas, mas notificará você sobre a adição de uma nova janela).

Capítulo 12 - Implantando seu aplicativo

Para implantar seu aplicativo:

  1. Navegue até a pasta App que foi criada no último capítulo. Você verá um arquivo com o nome de seus aplicativos, com a extensão '.sln', que você deve clicar duas vezes, para abri-lo no Visual Studio.

  2. Na Plataforma de Solução, selecione x86, Máquina Local.

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

    Para o Microsoft HoloLens, você pode achar mais fácil definir isso como Máquina Remota, para que você não esteja preso ao seu computador. No entanto, você também precisará fazer o seguinte:

    • Conheça o endereço IP do seu HoloLens, que pode ser encontrado nas Configurações>de Rede e Opções Avançadas de Wi-Fi>da Internet>, o IPv4 é o endereço que você deve usar.
    • Verifique se o Modo de Desenvolvedor está Ativado, encontrado em Atualização de Configurações>e Segurança>para desenvolvedores.

    Implantar solução

  4. Vá para o menu Build e clique em Deploy Solution para fazer sideload do aplicativo para sua máquina.

  5. Seu aplicativo agora deve aparecer na lista de aplicativos instalados, prontos para serem iniciados e testados!

Seu aplicativo de armazenamento e funções do Azure concluído

Parabéns, você criou um aplicativo de realidade mista que aproveita os serviços do Azure Functions e do Armazenamento do Azure. Seu aplicativo poderá se basear em dados armazenados e fornecer uma ação com base nesses dados.

produto final -fim

Exercícios de bónus

Exercício 1

Crie um segundo ponto de desova e registre a partir de qual ponto de desova um objeto foi criado. Ao carregar o arquivo de dados, reproduza as formas que estão sendo geradas a partir do local em que foram originalmente criadas.

Exercício 2

Crie uma maneira de reiniciar o aplicativo, em vez de ter que reabri-lo a cada vez. Loading Scenes é um bom ponto de partida. Depois de fazer isso, crie uma maneira de limpar a lista armazenada no Armazenamento do Azure, para que ela possa ser facilmente redefinida do seu aplicativo.