HoloLens (1ª geração) e Azure 305: Funções e armazenamento
Observação
Os tutoriais do Mixed Reality Academy foram projetados com o HoloLens (1ª geração) e os headsets imersivos de realidade misturada em mente. Dessa forma, achamos que é importante continuar disponibilizando esses tutoriais para os desenvolvedores que ainda buscam obter diretrizes para o desenvolvimento visando esses dispositivos. Esses tutoriais não serão atualizados com os conjuntos de ferramentas mais recentes nem com as interações usadas para o HoloLens 2. Eles serão mantidos para continuar funcionando nos dispositivos compatíveis. Haverá uma nova série de tutoriais que serão postados no futuro que demonstrarão como desenvolver para HoloLens 2. Este aviso será atualizado com um link para esses tutoriais quando eles forem postados.
Neste curso, você aprenderá a criar e usar Azure Functions e armazenar dados com um recurso de Armazenamento do Azure, dentro de um aplicativo de realidade misturada.
Azure Functions é um serviço da Microsoft, que permite que os desenvolvedores executem pequenas partes de código, 'functions', 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. Azure Functions dá suporte a 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 que os desenvolvedores armazenem dados, com o seguro de que eles estarão altamente disponíveis, seguros, duráveis, escaloná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.
Depois de concluir este curso, você terá um aplicativo de headset imersivo de realidade misturada que poderá fazer o seguinte:
- Permitir que o usuário olhe ao redor de uma cena.
- Dispare a geração de objetos quando o usuário olhar para um "botão" 3D.
- Os objetos gerados serão escolhidos por uma Função do Azure.
- À medida que cada objeto é gerado, o aplicativo armazenará o tipo de objeto em um Arquivo do Azure, localizado no Armazenamento do Azure.
- Ao carregar uma segunda vez, os dados do Arquivo do Azure serão recuperados e usados para reproduzir as ações de geração da instância anterior do aplicativo.
Em seu aplicativo, cabe a você saber como você integrará os resultados ao seu design. Este curso foi projetado para ensinar como integrar um Serviço do Azure ao seu Projeto do Unity. É seu trabalho usar o conhecimento obtido com este curso para aprimorar seu Aplicativo de realidade misturada.
Suporte a dispositivos
Curso | HoloLens | Headsets imersivos |
---|---|---|
MR e Azure 305: funções e armazenamento | ✔️ | ✔️ |
Observação
Embora este curso se concentre principalmente em Windows Mixed Reality headsets imersivos (VR), você também pode aplicar o que aprende neste curso a Microsoft HoloLens. Ao acompanhar o curso, você verá anotações sobre as alterações que talvez precise empregar para dar suporte ao HoloLens.
Pré-requisitos
Observação
Este tutorial foi projetado para desenvolvedores que têm experiência básica com Unity e C#. Lembre-se também de que os pré-requisitos e as instruções escritas neste documento representam o que foi testado e verificado no momento da gravação (maio de 2018). Você é livre para usar o software mais recente, conforme listado no artigo instalar as ferramentas , embora não se presuma que as informações neste curso corresponderão perfeitamente ao que você encontrará no software mais recente do que o listado abaixo.
Recomendamos o seguinte hardware e software para este curso:
- Um computador de desenvolvimento, compatível com Windows Mixed Reality para desenvolvimento imersivo de headset (VR)
- Windows 10 Fall Creators Update (ou posterior) com o modo desenvolvedor habilitado
- O SDK do Windows 10 mais recente
- Unity 2017.4
- Visual Studio 2017
- Um headset (VR) imersivo Windows Mixed Reality ou Microsoft HoloLens com o modo desenvolvedor habilitado
- Uma assinatura de uma conta do Azure para criar recursos do Azure
- Acesso à Internet para instalação do Azure e recuperação de dados
Antes de começar
Para evitar problemas ao criar esse projeto, é altamente sugerido que você crie o projeto mencionado neste tutorial em uma pasta raiz ou quase raiz (caminhos de pasta longa podem causar problemas no tempo de build).
Capítulo 1 – 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.
Faça logon no Portal do Azure.
Observação
Se você ainda não tiver uma conta do Azure, precisará criar uma. Se você estiver seguindo este tutorial em uma situação de sala de aula ou laboratório, peça ajuda ao instrutor ou a um dos supervisores para configurar sua nova conta.
Depois de fazer logon, clique em Novo no canto superior esquerdo, pesquise Conta de armazenamento e clique em Inserir.
Observação
A palavra Novo pode ter sido substituída por Criar um recurso, em portais mais recentes.
A nova página fornecerá uma descrição do serviço de conta de Armazenamento do Azure . Na parte inferior esquerda deste prompt, selecione o botão Criar para criar uma associação com esse serviço.
Depois de clicar em Criar:
Insira um Nome para sua conta, lembre-se de que esse campo aceita apenas números e letras minúsculas.
Para Modelo de implantação, selecione Gerenciador de recursos.
Em Tipo de conta, selecione Armazenamento (uso geral v1).
Determine o Local do grupo de recursos (se você estiver criando um novo Grupo de Recursos). O local seria idealmente na região em que o aplicativo seria executado. Alguns ativos do Azure só estão disponíveis em determinadas regiões.
Para Replicação , selecione Ra-GRS (armazenamento com redundância geográfica de acesso de leitura).
Para Desempenho, selecione Standard.
Deixe a transferência segura necessária como Desabilitada.
Selecione uma Assinatura.
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 você quiser ler mais sobre os Grupos de Recursos do Azure, visite o artigo do grupo de recursos.
Você também precisará confirmar que entendeu os Termos e Condições aplicados a esse Serviço.
Selecione Criar.
Depois de clicar em Criar, você precisará aguardar a criação do serviço, isso pode levar um minuto.
Uma notificação será exibida no portal depois que a instância de Serviço for criada.
Clique nas notificações para explorar sua nova instância de Serviço.
Clique no botão Ir para o 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 .
Clique em Chaves de acesso para revelar os pontos de extremidade deste serviço de nuvem. Use o Bloco de Notas ou semelhante para copiar uma de suas chaves para uso posterior. Além disso, observe o valor da cadeia de conexão , pois ele será usado na classe AzureServices , que você criará posteriormente.
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:
No portal do Azure, clique em Novo no canto superior esquerdo, pesquise Aplicativo de Funções e clique em Enter.
Observação
A palavra Novo pode ter sido substituída por Criar um recurso, em portais mais recentes.
A nova página fornecerá uma descrição do serviço de Aplicativo de Funções do Azure . Na parte inferior esquerda desse prompt, selecione o botão Criar para criar uma associação com esse serviço.
Depois de clicar em Criar:
Forneça um Nome do aplicativo. Somente letras e números podem ser usados aqui (maiúsculas ou minúsculas são permitidas).
Selecione sua Assinatura preferida.
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 você quiser ler mais sobre os Grupos de Recursos do Azure, visite o artigo do grupo de recursos.
Para este exercício, selecione Windows como o sistema operacional escolhido.
Selecione Plano de Consumo para o Plano de Hospedagem.
Determine o Local do grupo de recursos (se você estiver criando um novo Grupo de Recursos). O local seria idealmente na região em que o aplicativo seria executado. Alguns ativos do Azure só estão disponíveis em determinadas regiões. Para obter o desempenho ideal, selecione a mesma região que a conta de armazenamento.
Para Armazenamento, selecione Usar existente e, em seguida, usando o menu suspenso, localize o armazenamento criado anteriormente.
Deixe o Application Insights desativado para este exercício.
Selecione o botão Criar.
Depois de clicar em Criar, você terá que aguardar a criação do serviço, isso pode levar um minuto.
Uma notificação será exibida no portal depois que a instância de Serviço for criada.
Clique nas notificações para explorar sua nova instância de Serviço.
Clique no botão Ir para o recurso na notificação para explorar sua nova instância de serviço. Você será levado para sua nova instância do Serviço de Aplicativo de Funções .
No aplicativo de funções dashboard, passe o mouse sobre o Functions, encontrado no painel à esquerda e clique no símbolo + (mais).
Na próxima página, verifique se Webhook + API está selecionado e, para Escolher um idioma, selecione CSharp, pois essa será a linguagem usada para este tutorial. Por fim, clique no botão Criar essa função .
Você 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.
Copie o código a seguir para sua função. Essa função simplesmente retornará um inteiro aleatório entre 0 e 2 quando chamado. Não se preocupe com o código existente, fique à vontade para colar na parte superior 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;} }
Clique em Salvar.
O resultado deve ser semelhante à imagem abaixo.
Clique em Obter URL da função e anote o ponto de extremidade exibido. Você precisará inseri-lo na classe AzureServices que criará posteriormente neste curso.
Capítulo 3 – Configurando o projeto do Unity
Veja a seguir uma configuração típica para desenvolvimento com Realidade Misturada e, como tal, é um bom modelo para outros projetos.
Configure e teste o headset imersivo de realidade misturada.
Observação
Você não precisará de Controladores de Movimento para este curso. Se você precisar de suporte para configurar o headset imersivo, visite o artigo configuração de realidade misturada.
Abra o Unity e clique em Novo.
Agora você precisará fornecer um nome de projeto do Unity. Insira MR_Azure_Functions. Verifique se o tipo de projeto está definido como 3D. Defina o Local como em algum lugar apropriado para você (lembre-se de que mais perto dos diretórios raiz é melhor). Em seguida, clique em Criar projeto.
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 Script Externo para o Visual Studio 2017. Feche a janela Preferências.
Em seguida, vá paraConfigurações de Build de Arquivo> e alterne a plataforma para Plataforma Universal do Windows, clicando no botão Alternar Plataforma.
Vá paraConfigurações de Build de Arquivo> e verifique se:
O Dispositivo de Destino é definido como Qualquer Dispositivo.
Para Microsoft HoloLens, defina Dispositivo de Destino como HoloLens.
O Tipo de Build é definido como D3D
O SDK está definido como Mais recente instalado
A versão do Visual Studio está definida como Mais recente instalada
Compilar e Executar é definido como Computador Local
Salve a cena e adicione-a ao build.
Faça isso selecionando Adicionar Cenas Abertas. Uma janela salvar será exibida.
Crie uma nova pasta para essa cena e qualquer cena futura e, em seguida, selecione o botão Nova pasta para criar uma nova pasta, nomeie-a cenas.
Abra a pasta Cenas recém-criada e, em seguida, no campo de texto Nome do arquivo : , digite FunctionsScene e pressione Salvar.
As configurações restantes, em Configurações de Build, devem ser deixadas como padrão por enquanto.
Na janela Configurações de Build , clique no botão Configurações do Player . Isso abrirá o painel relacionado no espaço em que o Inspetor está localizado.
Neste painel, algumas configurações precisam ser verificadas:
Na guia Outras Configurações :
- A versão de runtime de script deve ser experimental (equivalente ao .NET 4.6), o que disparará a necessidade de reiniciar o Editor.
- O back-end de script deve ser .NET
- O nível de compatibilidade da API deve ser .NET 4.6
Na guia Configurações de Publicação, em Recursos, marcar:
InternetClient
Mais abaixo no painel, em Configurações de XR (encontradas abaixo de Configurações de Publicação), marque Realidade Virtual Com suporte, verifique se o SDK do Windows Mixed Reality foi adicionado.
De volta às Configurações de Build, os projetos C# do Unity não estão mais esmaecidos; marque a caixa de seleção ao lado disso.
Feche a janela Configurações de Build.
Salve a Cena e o Projeto (FILE>SAVE SCENE/FILE>SAVE PROJECT).
Capítulo 4 – Configurar a câmera principal
Importante
Se você quiser ignorar os componentes de Configuração do Unity deste curso e continuar diretamente no código, fique à 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, prossiga do Capítulo 7.
No Painel de Hierarquia, você encontrará um objeto chamado Câmera Principal, esse objeto representa o ponto de vista "cabeça" quando você estiver "dentro" do aplicativo.
Com o Painel do Unity à sua frente, selecione a Câmera Principal GameObject. Você observará que o Painel do Inspetor (geralmente encontrado à direita, no Painel) mostrará os vários componentes desse GameObject, com Transformar na parte superior, seguido pela Câmera e alguns outros componentes. Você precisará redefinir a Transformação da Câmera Principal, portanto, ela está posicionada corretamente.
Para fazer isso, selecione o ícone Engrenagem ao lado do componente Transformação da Câmera e selecione Redefinir.
Em seguida, atualize o componente Transformar para se parecer com:
Transformação – Posição
X | S | Z |
---|---|---|
0 | 1 | 0 |
Transformação – Rotação
X | S | Z |
---|---|---|
0 | 0 | 0 |
Transformar – Dimensionar
X | S | Z |
---|---|---|
1 | 1 | 1 |
Capítulo 5 – Configurando a cena do Unity
Clique com o botão direito do mouse em uma área vazia do Painel de Hierarquia, em Objeto 3D, adicione um Plano.
Com o objeto Plane selecionado, altere os seguintes parâmetros no Painel do Inspetor:
Transformação – Posição
X | S | Z |
---|---|---|
0 | 0 | 4 |
Transformar – Dimensionar
X | S | Z |
---|---|---|
10 | 1 | 10 |
Clique com o botão direito do mouse em uma área vazia do Painel de Hierarquia, em Objeto 3D, adicione um Cubo.
Renomeie o Cubo como GazeButton (com o Cubo selecionado, pressione 'F2').
Altere os seguintes parâmetros para Transformar Posição no Painel do Inspetor:
X S Z 0 3 5 Clique no botão suspenso Marca e clique em Adicionar Marca para abrir o painel Marcas & Camadas.
Selecione o botão + (mais) e, no campo Novo Nome da Marca , insira GazeButton e pressione Salvar.
Clique no objeto GazeButton no Painel de Hierarquia e, no Painel inspetor, atribua a marca GazeButton recém-criada.
Clique com o botão direito do mouse no objeto GazeButton , no Painel de Hierarquia, e adicione um GameObject Vazio (que será adicionado como um objeto filho ).
Selecione o novo objeto e renomeie-o como ShapeSpawnPoint.
Altere os seguintes parâmetros para Transformar Posição no Painel do Inspetor:
X S Z 0 -1 0
Em seguida, você criará um objeto Text 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 de Hierarquia novamente e adicione um objeto text 3D do objeto>3D como um filho.
Renomeie o objeto Text 3D para AzureStatusText.
Altere o objeto AzureStatusTextTransform Position da seguinte maneira:
X S Z 0 0 -0,6 Altere o objeto AzureStatusTextTransformar Escala da seguinte maneira: | X | Y | Z | | :---: | :---: | :---: | | 0.1 | 0.1 | 0.1 |
Observação
Não se preocupe se ele parecer estar fora do centro, pois isso será corrigido quando o componente de Malha de Texto abaixo for atualizado.
Altere o componente Malha de Texto para corresponder ao seguinte:
Dica
A cor selecionada aqui é Cor hexadecimal: 000000FF, embora fique à vontade para escolher a sua, apenas verifique se ela é legível.
Sua estrutura do Painel de Hierarquia agora deve ter esta aparência:
Sua cena agora deve ter esta aparência:
Capítulo 6 – Importar o Armazenamento do Azure para Unity
Você usará o Armazenamento do Azure para Unity (que 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 a 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, verifique se você baixou o '.unitypackage' mais recente do GitHub. Em seguida, faça o seguinte:
Adicione o arquivo .unitypackage ao Unity usando a opção de menuPacote Personalizado do Pacote> deImportaçãode Ativos>.
Na caixa Importar Pacote do Unity exibida, você pode selecionar tudo em >Armazenamento de Plug-ins. Desmarque todo o resto, pois não é necessário para este curso.
Clique no botão Importar para adicionar os itens ao seu projeto.
Vá para a pasta Armazenamento em Plug-ins, no modo de exibição Projeto, e selecione somente os seguintes plug-ins:
Microsoft.Data.Edm
Microsoft.Data.OData
Microsoft.WindowsAzure.Storage
Newtonsoft.Json
System.Spatial
Com esses plug-ins específicos selecionados, desmarqueQualquer Plataforma e desmarqueWSAPlayer e clique em Aplicar.
Observação
Estamos marcando esses plug-ins específicos para serem usados apenas no Editor do Unity. Isso ocorre porque há versões diferentes dos mesmos plug-ins na pasta WSA que serão usadas depois que o projeto for exportado do Unity.
Na pasta Plug-in de armazenamento , selecione apenas:
Microsoft.Data.Services.Client
Marque a caixa Não Processar em Configurações da Plataforma e clique em Aplicar.
Observação
Estamos marcando esse plug-in "Não processar" porque o patcher de assembly do Unity tem dificuldade para processar esse plug-in. O plug-in 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 Azure App.
O upload e o download do arquivo de dados no Armazenamento em Nuvem do Azure.
Para criar esta Classe:
Clique com o botão direito do mouse na Pasta de Ativos , localizada no Painel de Projeto, Criar>Pasta. Nomeie a pasta Scripts.
Clique duas vezes na pasta que acabou de ser criada para abri-la.
Clique com o botão direito do mouse dentro da pasta Criar>Script C#. Chame o script AzureServices.
Clique duas vezes na nova classe AzureServices para abri-la com o Visual Studio.
Adicione os seguintes namespaces à 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;
Adicione os seguintes Campos de 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;
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
Substitua os valores de ponto de extremidade e cadeia de conexão pelos valores do armazenamento do Azure, encontrados no Portal do Azure
O código para 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 de CallAzureFunctionForNextShape() em um capítulo futuro.
Exclua o método Update(), pois essa classe não o usará.
Salve suas alterações no Visual Studio e retorne ao Unity.
Clique e arraste a classe AzureServices da pasta Scripts para o objeto Câmera Principal no Painel hierarquia.
Selecione a Câmera 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 .
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 solicitada, 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 armazenada no Armazenamento do Azure. Quando o aplicativo é iniciado, se um arquivo armazenado é encontrado no Armazenamento do Azure, a lista Histórico de Formas é recuperada e reproduzida, com o objeto Texto 3D fornecendo se a forma gerada é de armazenamento ou nova.
Para criar essa classe:
Vá para a pasta Scripts que você criou anteriormente.
Clique com o botão direito do mouse dentro da pasta Criar>Script C#. Chame o script ShapeFactory.
Clique duas vezes no novo script ShapeFactory para abri-lo com o Visual Studio.
Verifique se a classe ShapeFactory inclui os seguintes namespaces:
using System.Collections.Generic; using UnityEngine;
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>(); }
O método CreateShape() gera as formas primitivas, com base no parâmetro inteiro fornecido. O parâmetro booliano é usado para especificar se a forma criada no momento é de 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); } }
Salve as alterações no Visual Studio antes de retornar ao Unity.
De volta ao Editor do Unity, clique e arraste a classe ShapeFactory da pasta Scripts para o objeto Câmera Principal no Painel hierarquia.
Com a Câmera Principal selecionada, você observará que o componente de script ShapeFactory não tem a referência de Ponto de Geração . Para corrigi-lo, arraste o objeto ShapeSpawnPoint do Painel de Hierarquia para o destino de referência do Ponto de Geração .
Capítulo 9 – Criar a classe Gaze
O último script que você precisa criar é a classe Gaze .
Essa classe é responsável por criar um Raycast que será projetado para frente a partir da Câmera Principal, para detectar qual objeto o usuário está examinando. Nesse caso, o Raycast precisará identificar se o usuário está examinando o objeto GazeButton na cena e disparar um comportamento.
Para criar esta Classe:
Vá para a pasta Scripts que você criou anteriormente.
Clique com o botão direito do mouse no Painel de Projeto, Criar>Script C#. Chame o script de Foco.
Clique duas vezes no novo script De foco para abri-lo com o Visual Studio.
Verifique se o namespace a seguir está incluído na parte superior do script:
using UnityEngine;
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.
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(); }
Adicione o seguinte código, que criará um objeto de cursor no início, juntamente com o método Update(), que executará o método Raycast, além de ser onde o booliano 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(); } }
Em seguida, adicione o método UpdateRaycast(), que projetará um Raycast e detectará o destino de ocorrência.
private void UpdateRaycast() { // Set the old focused gameobject. _oldFocusedObject = FocusedObject; RaycastHit hitInfo; // Initialise Raycasting. Hit = Physics.Raycast(_gazeOrigin, _gazeDirection, out hitInfo, GazeMaxDistance, LayerMask); HitInfo = hitInfo; // Check whether raycast has hit. if (Hit == true) { Position = hitInfo.point; Normal = hitInfo.normal; // Check whether the hit has a collider. if (hitInfo.collider != null) { // Set the focused object with what the user just looked at. FocusedObject = hitInfo.collider.gameObject; } else { // Object looked on is not valid, set focused gameobject to null. FocusedObject = null; } } else { // No object looked upon, set focused gameobject to null. FocusedObject = null; // Provide default position for cursor. Position = _gazeOrigin + (_gazeDirection * GazeMaxDistance); // Provide a default normal. Normal = _gazeDirection; } // Lerp the cursor to the given position, which helps to stabilize the gaze. Cursor.transform.position = Vector3.Lerp(Cursor.transform.position, Position, 0.6f); // Check whether the previous focused object is this same // object. If so, reset the focused object. if (FocusedObject != _oldFocusedObject) { ResetFocusedObject(); if (FocusedObject != null) { if (FocusedObject.CompareTag(InteractibleTag.ToString())) { // Set the Focused object to green - success! FocusedObject.GetComponent<Renderer>().material.color = Color.green; // Start the Azure Function, to provide the next shape! AzureServices.instance.CallAzureFunctionForNextShape(); } } } }
Por fim, adicione o método ResetFocusedObject(), que alternará a cor atual dos objetos GazeButton, indicando se ele 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; } } }
Salve suas alterações no Visual Studio antes de retornar ao Unity.
Clique e arraste a classe Gaze da pasta Scripts para o objeto Câmera Principal no Painel hierarquia.
Capítulo 10 – Concluindo a classe AzureServices
Com os outros scripts em vigor, agora é possível concluir a classe AzureServices . Isso será feito por meio de:
Adicionando 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 marcar para a existência de um Arquivo armazenado anteriormente que contém a Lista de Formas.
Se o arquivo for encontrado, ele desabilitará o Foco do usuário e disparará a criação da forma, 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á a exibição "Armazenamento" ou "Novo", dependendo da origem das formas.
Se nenhum arquivo for encontrado, ele habilitará o Foco, permitindo que o usuário crie formas ao examinar 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(); } }
O próximo snippet de código é de dentro do método Start() ; em que uma chamada será feita para o método CreateCloudIdentityAsync(). Fique à vontade para copiar sobre 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(); }
Preencha o código do método CallAzureFunctionForNextShape(). Você usará o Aplicativo de Funções 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(); }
Adicione um método para criar uma cadeia de caracteres concatenando os inteiros armazenados na lista de histórico de formas e salvando-o 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); }
Adicione um método para recuperar o texto armazenado no arquivo localizado em seu Arquivo de Armazenamento do Azure e desserializá-lo em uma lista.
Depois que esse processo for concluído, o método reabilitará o foco 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!"; }
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 build:
Vá paraConfigurações de Build de Arquivo>.
Clique em Compilar. O Unity iniciará uma janela de Explorador de Arquivos, na qual você precisa criar e selecionar uma pasta para compilar o aplicativo. Crie essa pasta agora e nomeie-a como Aplicativo. Em seguida, com a pasta Aplicativo selecionada, pressione Selecionar Pasta.
O Unity começará a compilar seu projeto para a pasta Aplicativo .
Depois que o Unity terminar de compilar (pode levar algum tempo), ele abrirá uma janela Explorador de Arquivos no local do build (marcar barra de tarefas, pois ela pode nem sempre aparecer acima das janelas, mas notificará você sobre a adição de uma nova janela).
Capítulo 12 – Implantando seu aplicativo
Para implantar seu aplicativo:
Navegue até a pasta Aplicativo que foi criada no último Capítulo. Você verá um arquivo com o nome dos aplicativos, com a extensão '.sln', que você deve clicar duas vezes, para abri-lo no Visual Studio.
Na Plataforma de Solução, selecione x86, Computador Local.
Na Configuração da Solução , selecione Depurar.
Para o Microsoft HoloLens, talvez seja mais fácil defini-lo como Computador Remoto, para que você não seja vinculado ao computador. No entanto, você também precisará fazer o seguinte:
- Conheça o endereço IP do HoloLens, que pode ser encontrado na Rede de Configurações&Opções Avançadas> deWi-Fi> daInternet>; o IPv4 é o endereço que você deve usar.
- Verifique se o Modo de Desenvolvedor está Ativado; encontrado em Configurações>Atualização & Segurança>para desenvolvedores.
Vá para o menu Compilar e clique em Implantar Solução para fazer sideload do aplicativo em seu computador.
Seu aplicativo agora deve aparecer na lista de aplicativos instalados, prontos para serem iniciados e testados!
Seu aplicativo de armazenamento e Azure Functions concluído
Parabéns, você criou um aplicativo de realidade misturada que aproveita o Azure Functions e os serviços de Armazenamento do Azure. Seu aplicativo poderá desenhar em dados armazenados e fornecer uma ação com base nesses dados.
Exercícios de bônus
Exercício 1
Crie um segundo ponto de geração e registro do qual ponto de geração um objeto foi criado. Quando você carregar o arquivo de dados, reproduza as formas que estão sendo geradas do local em que foram criadas originalmente.
Exercício 2
Crie uma maneira de reiniciar o aplicativo, em vez de precisar abri-lo novamente a cada vez. Carregar Cenas é um bom ponto para começar. 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.