HoloLens (1.ª geração) e Azure 309: Perspetivas da aplicação
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.
Neste curso, você aprenderá como adicionar recursos do Application Insights a um aplicativo de realidade mista, usando a API do Azure Application Insights para coletar análises sobre o comportamento do usuário.
O Application Insights é um serviço da Microsoft, permitindo que os desenvolvedores coletem análises de seus aplicativos e as gerenciem a partir de um portal fácil de usar. A análise pode ser qualquer coisa, desde desempenho até informações personalizadas que você gostaria de coletar. Para obter mais informações, visite a página Application Insights.
Tendo concluído este curso, você terá um aplicativo de fone de ouvido imersivo de realidade mista, que será capaz de fazer o seguinte:
- Permita que o usuário olhe e se mova em torno de uma cena.
- Acione o envio de análises para o Serviço do Application Insights, usando o Olhar e a Proximidade com objetos em cena.
- O aplicativo também acionará o Serviço, buscando informações sobre qual objeto foi mais abordado pelo usuário, nas últimas 24 horas. Esse objeto mudará sua cor para verde.
Este curso ensinará como obter os resultados do Serviço Application Insights em um aplicativo de exemplo baseado em Unity. Caberá a você aplicar esses conceitos a um aplicativo personalizado que você pode estar criando.
Suporte de dispositivos
Curso | HoloLens | Auriculares imersivos |
---|---|---|
MR e Azure 309: Insights de aplicativos | ✔️ | ✔️ |
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. Ao utilizar o HoloLens, poderá notar algum eco durante a captura de voz.
Pré-requisitos
Nota
Este tutorial foi projetado para desenvolvedores que têm experiência básica com Unity e C#. Tenha também em atenção que os pré-requisitos e as instruções escritas contidas neste documento representam o que foi testado e verificado no momento da redação (julho 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:
- Um PC de desenvolvimento, compatível com Windows Mixed Reality para desenvolvimento imersivo (VR) de auriculares
- Windows 10 Fall Creators Update (ou posterior) com o modo de desenvolvedor ativado
- O SDK mais recente do Windows 10
- Unidade 2017.4
- Visual Studio 2017
- Um auricular imersivo (VR) Windows Mixed Reality ou Microsoft HoloLens com o modo de programador ativado
- Um conjunto de auscultadores com microfone incorporado (se o auricular não tiver um microfone e altifalantes incorporados)
- Acesso à Internet para configuração do Azure e recuperação de dados do Application Insights
Antes de começar
Para evitar problemas ao criar este projeto, é altamente recomendável que você crie o projeto neste tutorial em uma pasta raiz ou quase raiz (caminhos de pasta longos podem causar problemas em tempo de compilação).
Aviso
Esteja ciente, os dados que vão para o Application Insights levam tempo, então seja paciente. Se quiser verificar se o Serviço recebeu os seus dados, consulte o Capítulo 14, que lhe mostrará como navegar no portal.
Capítulo 1 - O Portal do Azure
Para usar o Application Insights, você precisará criar e configurar um Serviço do Application Insights no portal do Azure.
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.
Depois de iniciar sessão, clique em Novo no canto superior esquerdo, procure Informações sobre Aplicações e clique em Enter.
Nota
A palavra Novo pode ter sido substituída por Criar um recurso, em portais mais recentes.
A nova página à direita fornecerá uma descrição do Serviço Azure Application Insights . No canto inferior esquerdo desta página, selecione o botão Criar para criar uma associação com este Serviço.
Depois de clicar em Criar:
Insira o Nome desejado para esta instância de Serviço.
Como Tipo de Aplicativo, selecione Geral.
Selecione uma Assinatura apropriada.
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 cursos) em um grupo de recursos comum).
Se desejar ler mais sobre os Grupos de Recursos do Azure, visite o artigo do grupo de recursos.
Selecione uma Localização.
Também terá de confirmar que compreendeu os Termos e Condições aplicados a este Serviço.
Selecione Criar.
Depois de clicar em Criar, você terá que esperar que o Serviço seja criado, isso pode levar um minuto.
Uma notificação aparecerá no portal assim que a instância de serviço for criada.
Selecione as notificações para explorar sua nova instância de serviço.
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 Application Insights.
Nota
Mantenha esta página web aberta e de fácil acesso, você voltará aqui com frequência para ver os dados coletados.
Importante
Para implementar o Application Insights, você precisará usar três (3) valores específicos: Chave de instrumentação, ID do aplicativo e Chave da API. Abaixo, você verá como recuperar esses valores do seu Serviço. Certifique-se de anotar esses valores em uma página em branco do Bloco de Notas , porque você os usará em breve em seu código.
Para encontrar a Chave de Instrumentação, você precisará rolar para baixo a lista de funções de Serviço e selecionar Propriedades, a guia exibida revelará a Chave de Serviço.
Um pouco abaixo de Propriedades, você encontrará o Acesso à API, no qual você precisa clicar. O painel à direita fornecerá a ID do aplicativo do seu aplicativo.
Com o painel ID do aplicativo ainda aberto, clique em Criar chave de API, que abrirá o painel Criar chave de API.
No painel Criar chave de API, agora aberto, digite uma descrição e marque as três caixas.
Clique em Gerar chave. Sua chave de API será criada e exibida.
Aviso
Esta é a única vez que sua Chave de Serviço será exibida, portanto, certifique-se de fazer uma cópia dela agora.
Capítulo 2 - Configurar o projeto Unity
A seguir está uma configuração típica para desenvolver com a realidade mista e, como tal, é um bom modelo para outros projetos.
Abra o Unity e clique em Novo.
Agora você precisará fornecer um nome de Projeto Unity, inserir MR_Azure_Application_Insights. Certifique-se de que o Modelo 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.
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 .
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.
Vá para Configurações de compilação de arquivo > e certifique-se de que:
O Dispositivo de Destino está definido como Qualquer dispositivo
Para o Microsoft HoloLens, defina Target Device como HoloLens.
O tipo de compilação está definido como D3D
O SDK está definido como Instalado mais recente
Build and Run está definido como Máquina Local
Salve a cena e adicione-a à compilação.
Faça isso selecionando Adicionar cenas abertas. Será exibida uma janela de salvamento.
Crie uma nova pasta para esta e qualquer cena futura e, em seguida, clique no botão Nova pasta , para criar uma nova pasta, nomeie-a Cenas.
Abra a pasta Cenas recém-criada e, no campo de texto Nome do arquivo:, digite ApplicationInsightsScene e clique em Salvar.
As configurações restantes, em Configurações de compilação, devem ser deixadas como padrão por enquanto.
Na janela Configurações de Compilação , selecione Configurações do Player, isso abrirá o painel relacionado no espaço onde o Inspetor está localizado.
Neste painel, algumas configurações precisam ser verificadas:
Na guia Outras configurações:
A versão do Scripting Runtime deve ser experimental (equivalente ao .NET 4.6), o que acionará a necessidade de reiniciar o Editor.
O back-end de scripts deve ser .NET
O nível de compatibilidade da API deve ser .NET 4.6
Na guia Configurações de publicação , em Recursos, verifique:
InternetClient
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.
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.
Feche a janela Configurações de compilação.
Salve sua cena e projeto (FILE>SAVE SCENE / FILE>SAVE PROJECT).
Capítulo 3 - Importar o pacote Unity
Importante
Se você deseja ignorar os componentes Unity set deste curso e continuar direto no código, sinta-se à vontade para baixar este pacote Azure-MR-309.unity, importe-o 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 6.
Importante
Para usar o Application Insights no Unity, você precisa importar a DLL para ele, juntamente com a DLL Newtonsoft. 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 Application Insights para o seu próprio projeto, certifique-se de ter baixado o '.unitypackage', que contém os plugins. Em seguida, faça o seguinte:
Adicione o.unitypackage** ao Unity usando a opção de menu Pacote personalizado do pacote > de importação de ativos>.
Na caixa Importar pacote Unity que aparece, verifique se tudo em (e incluindo) Plug-ins está selecionado.
Clique no botão Importar para adicionar os itens ao seu projeto.
Vá para a pasta Insights em Plug-ins na visualização Projeto e selecione apenas os seguintes plug-ins:
- Microsoft.ApplicationInsights
Com este plug-in selecionado, certifique-se de que Any Platform está desmarcada, certifique-se de que o WSAPlayer também está desmarcado e clique em Aplicar. Fazer isso é apenas para confirmar que os arquivos estão configurados corretamente.
Nota
Marcando os plugins assim, configura-os para serem usados apenas no Editor Unity. Há um conjunto diferente de DLLs na pasta WSA que será usado depois que o projeto for exportado do Unity.
Em seguida, você precisa abrir a pasta WSA , dentro da pasta Insights . Você verá uma cópia do mesmo arquivo que configurou. Selecione este arquivo e, no inspetor, verifique se Any Platform está desmarcada e, em seguida, verifique se apenas o WSAPlayer está marcado. Clique em Aplicar.
Agora você precisará seguir os passos 4-6, mas para os plugins Newtonsoft . Veja a captura de tela abaixo para saber como deve ser o resultado.
Capítulo 4 - Configurar a câmara e os controlos do utilizador
Neste capítulo, você configurará a câmera e os controles para permitir que o usuário veja e se mova na cena.
Clique com o botão direito do rato numa área vazia no Painel Hierarquia e, em seguida, em Criar>Vazio.
Renomeie o novo GameObject vazio para Camera Parent.
Clique com o botão direito do rato numa área vazia no Painel de Hierarquia, depois em Objeto 3D e, em seguida, em Esfera.
Renomeie a esfera para Mão Direita.
Defina a escala de transformação da mão direita como 0,1, 0,1, 0,1
Remova o componente Sphere Collider da mão direita clicando na engrenagem no componente Sphere Collider e, em seguida, remova o componente.
No painel Hierarquia, arraste os objetos Câmera principal e Mão direita para o objeto Pai da câmera.
Defina a posição de transformação da câmera principal e do objeto da mão direita como 0, 0, 0.
Capítulo 5 - Configurar os objetos na cena Unity
Agora você criará algumas formas básicas para sua cena, com as quais o usuário poderá interagir.
Clique com o botão direito do rato numa área vazia no Painel de Hierarquia, depois em Objeto 3D e, em seguida, selecione Plano.
Defina a posição de transformação plana como 0, -1, 0.
Defina a escala de transformação plana como 5, 1, 5.
Crie um material básico para usar com seu objeto Plane , para que as outras formas sejam mais fáceis de ver. Navegue até o Painel do Projeto, clique com o botão direito do mouse e, em seguida , em Criar, seguido de Pasta, para criar uma nova pasta. Nomeie-o Materiais.
Abra a pasta Materiais, clique com o botão direito do mouse, clique em Criar e, em seguida, em Material, para criar um novo material. Nomeie-o Azul.
Com o novo material azul selecionado, olhe para o Inspetor e clique na janela retangular ao lado de Albedo. Selecione uma cor azul (a imagem abaixo é Hex Color: #3592FFFF). Clique no botão Fechar depois de ter escolhido.
Arraste o novo material da pasta Materiais para o Plano recém-criado, dentro da cena (ou solte-o no objeto Plano dentro da Hierarquia).
Clique com o botão direito do rato numa área vazia no Painel de Hierarquia e, em seguida, em Objeto 3D, Cápsula.
- Com a cápsula selecionada, altere sua posição de transformação para: -10, 1, 0.
Clique com o botão direito do mouse em uma área vazia no Painel de Hierarquia e, em seguida, em Objeto 3D, Cubo.
- Com o cubo selecionado, altere sua posição de transformação para: 0, 0, 10.
Clique com o botão direito do rato numa área vazia no Painel Hierarquia e, em seguida, em Objeto 3D, Esfera.
- Com a Esfera selecionada, altere sua Posição de Transformação para: 10, 0, 0.
Nota
Estes valores de Posição são sugestões. Você é livre para definir as posições dos objetos para o que quiser, embora seja mais fácil para o usuário do aplicativo se as distâncias dos objetos não estiverem muito longe da câmera.
Quando seu aplicativo está em execução, ele precisa ser capaz de identificar os objetos dentro da cena, para conseguir isso, eles precisam ser marcados. Selecione um dos objetos e, no painel Inspetor, clique em Adicionar tag..., que trocará o inspetor pelo painel Tags & Camadas.
Clique no símbolo + (mais) e digite o nome da marca como ObjectInScene.
Aviso
Se você usar um nome diferente para sua tag, precisará garantir que essa alteração também seja feita nos scripts DataFromAnalytics, ObjectTrigger e Gaze, posteriormente, para que seus objetos sejam encontrados e detetados em sua cena.
Com a tag criada, agora você precisa aplicá-la a todos os seus três objetos. Na Hierarquia, mantenha pressionada a tecla Shift, clique nos objetos Cápsula, Cubo e Esfera e, em seguida, no Inspetor, clique no menu suspenso ao lado de Tag e, em seguida, clique na marca ObjectInScene que você criou.
Capítulo 6 - Criar a classe ApplicationInsightsTracker
O primeiro script que você precisa criar é o ApplicationInsightsTracker, que é responsável por:
Criação de eventos com base nas interações do usuário para enviar ao Azure Application Insights.
Criação de nomes de eventos apropriados, dependendo da interação do usuário.
Envio de eventos para a instância do Serviço Application Insights.
Para criar esta classe:
Clique com o botão direito do rato no Painel do Projeto e, em seguida, em Criar> Pasta. Nomeie a pasta Scripts.
Com a pasta Scripts criada, clique duas vezes nela para abri-la. Em seguida, dentro dessa pasta, clique com o botão direito do mouse em Criar>script C#. Nomeie o script ApplicationInsightsTracker.
Clique duas vezes no novo script ApplicationInsightsTracker para abri-lo com o Visual Studio.
Atualize os namespaces na parte superior do script para que fiquem como abaixo:
using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; using UnityEngine;
Dentro da classe insira as seguintes variáveis:
/// <summary> /// Allows this class to behavior like a singleton /// </summary> public static ApplicationInsightsTracker Instance; /// <summary> /// Insert your Instrumentation Key here /// </summary> internal string instrumentationKey = "Insert Instrumentation Key here"; /// <summary> /// Insert your Application Id here /// </summary> internal string applicationId = "Insert Application Id here"; /// <summary> /// Insert your API Key here /// </summary> internal string API_Key = "Insert API Key here"; /// <summary> /// Represent the Analytic Custom Event object /// </summary> private TelemetryClient telemetryClient; /// <summary> /// Represent the Analytic object able to host gaze duration /// </summary> private MetricTelemetry metric;
Nota
Defina os valores instrumentationKey, applicationId e API_Key adequadamente, usando as Chaves de Serviço do Portal do Azure, conforme mencionado no Capítulo 1, etapa 9 em diante.
Em seguida, adicione os métodos Start() e Awake(), que serão chamados quando a classe for inicializada:
/// <summary> /// Sets this class instance as a singleton /// </summary> void Awake() { Instance = this; } /// <summary> /// Use this for initialization /// </summary> void Start() { // Instantiate telemetry and metric telemetryClient = new TelemetryClient(); metric = new MetricTelemetry(); // Assign the Instrumentation Key to the Event and Metric objects TelemetryConfiguration.Active.InstrumentationKey = instrumentationKey; telemetryClient.InstrumentationKey = instrumentationKey; }
Adicione os métodos responsáveis pelo envio dos eventos e métricas cadastrados pelo seu aplicativo:
/// <summary> /// Submit the Event to Azure Analytics using the event trigger object /// </summary> public void RecordProximityEvent(string objectName) { telemetryClient.TrackEvent(CreateEventName(objectName)); } /// <summary> /// Uses the name of the object involved in the event to create /// and return an Event Name convention /// </summary> public string CreateEventName(string name) { string eventName = $"User near {name}"; return eventName; } /// <summary> /// Submit a Metric to Azure Analytics using the metric gazed object /// and the time count of the gaze /// </summary> public void RecordGazeMetrics(string objectName, int time) { // Output Console information about gaze. Debug.Log($"Finished gazing at {objectName}, which went for <b>{time}</b> second{(time != 1 ? "s" : "")}"); metric.Name = $"Gazed {objectName}"; metric.Value = time; telemetryClient.TrackMetric(metric); }
Certifique-se de salvar suas alterações no Visual Studio antes de retornar ao Unity.
Capítulo 7 - Criar o script Gaze
O próximo script a ser criado é o script Gaze . Este script é 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 um objeto com a tag ObjectInScene e, em seguida, contar quanto tempo o usuário olha para esse objeto.
Clique duas vezes na pasta Scripts para abri-la.
Clique com o botão direito do mouse dentro da pasta Scripts, clique em Criar>script C#. Nomeie o script como Gaze.
Clique duas vezes no script para abri-lo com o Visual Studio.
Substitua o código existente pelo seguinte:
using UnityEngine; public class Gaze : MonoBehaviour { /// <summary> /// Provides Singleton-like behavior to this class. /// </summary> public static Gaze Instance; /// <summary> /// Provides a reference to the object the user is currently looking at. /// </summary> public GameObject FocusedGameObject { get; private set; } /// <summary> /// Provides whether an object has been successfully hit by the raycast. /// </summary> public bool Hit { get; private set; } /// <summary> /// Provides a reference to compare whether the user is still looking at /// the same object (and has not looked away). /// </summary> private GameObject _oldFocusedObject = null; /// <summary> /// Max Ray Distance /// </summary> private float _gazeMaxDistance = 300; /// <summary> /// Max Ray Distance /// </summary> private float _gazeTimeCounter = 0; /// <summary> /// The cursor object will be created when the app is running, /// this will store its values. /// </summary> private GameObject _cursor; }
O código para os métodos Awake() e Start() agora precisa ser adicionado.
private void Awake() { // Set this class to behave similar to singleton Instance = this; _cursor = CreateCursor(); } void Start() { FocusedGameObject = null; } /// <summary> /// Create a cursor object, to provide what the user /// is looking at. /// </summary> /// <returns></returns> private GameObject CreateCursor() { GameObject newCursor = GameObject.CreatePrimitive(PrimitiveType.Sphere); // Remove the collider, so it does not block raycast. Destroy(newCursor.GetComponent<SphereCollider>()); newCursor.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f); newCursor.GetComponent<MeshRenderer>().material.color = Color.HSVToRGB(0.0223f, 0.7922f, 1.000f); newCursor.SetActive(false); return newCursor; }
Dentro da classe Gaze , adicione o seguinte código no método Update() para projetar um Raycast e detetar o alvo atingido:
/// <summary> /// Called every frame /// </summary> void Update() { // Set the old focused gameobject. _oldFocusedObject = FocusedGameObject; RaycastHit hitInfo; // Initialize Raycasting. Hit = Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hitInfo, _gazeMaxDistance); // Check whether raycast has hit. if (Hit == true) { // Check whether the hit has a collider. if (hitInfo.collider != null) { // Set the focused object with what the user just looked at. FocusedGameObject = hitInfo.collider.gameObject; // Lerp the cursor to the hit point, which helps to stabilize the gaze. _cursor.transform.position = Vector3.Lerp(_cursor.transform.position, hitInfo.point, 0.6f); _cursor.SetActive(true); } else { // Object looked on is not valid, set focused gameobject to null. FocusedGameObject = null; _cursor.SetActive(false); } } else { // No object looked upon, set focused gameobject to null. FocusedGameObject = null; _cursor.SetActive(false); } // Check whether the previous focused object is this same object. If so, reset the focused object. if (FocusedGameObject != _oldFocusedObject) { ResetFocusedObject(); } // If they are the same, but are null, reset the counter. else if (FocusedGameObject == null && _oldFocusedObject == null) { _gazeTimeCounter = 0; } // Count whilst the user continues looking at the same object. else { _gazeTimeCounter += Time.deltaTime; } }
Adicione o método ResetFocusedObject() para enviar dados para o Application Insights quando o usuário tiver olhado para um objeto.
/// <summary> /// Reset the old focused object, stop the gaze timer, and send data if it /// is greater than one. /// </summary> public void ResetFocusedObject() { // Ensure the old focused object is not null. if (_oldFocusedObject != null) { // Only looking for objects with the correct tag. if (_oldFocusedObject.CompareTag("ObjectInScene")) { // Turn the timer into an int, and ensure that more than zero time has passed. int gazeAsInt = (int)_gazeTimeCounter; if (gazeAsInt > 0) { //Record the object gazed and duration of gaze for Analytics ApplicationInsightsTracker.Instance.RecordGazeMetrics(_oldFocusedObject.name, gazeAsInt); } //Reset timer _gazeTimeCounter = 0; } } }
Agora você concluiu o roteiro do Gaze . Salve suas alterações no Visual Studio antes de retornar ao Unity.
Capítulo 8 - Criar a classe ObjectTrigger
O próximo script que você precisa criar é ObjectTrigger, que é responsável por:
- Adicionar componentes necessários para a colisão à câmara principal.
- Detetar se a câmera está perto de um objeto marcado como ObjectInScene.
Para criar o script:
Clique duas vezes na pasta Scripts para abri-la.
Clique com o botão direito do mouse dentro da pasta Scripts, clique em Criar>script C#. Nomeie o script ObjectTrigger.
Clique duas vezes no script para abri-lo com o Visual Studio. Substitua o código existente pelo seguinte:
using UnityEngine; public class ObjectTrigger : MonoBehaviour { private void Start() { // Add the Collider and Rigidbody components, // and set their respective settings. This allows for collision. gameObject.AddComponent<SphereCollider>().radius = 1.5f; gameObject.AddComponent<Rigidbody>().useGravity = false; } /// <summary> /// Triggered when an object with a collider enters this objects trigger collider. /// </summary> /// <param name="collision">Collided object</param> private void OnCollisionEnter(Collision collision) { CompareTriggerEvent(collision, true); } /// <summary> /// Triggered when an object with a collider exits this objects trigger collider. /// </summary> /// <param name="collision">Collided object</param> private void OnCollisionExit(Collision collision) { CompareTriggerEvent(collision, false); } /// <summary> /// Method for providing debug message, and sending event information to InsightsTracker. /// </summary> /// <param name="other">Collided object</param> /// <param name="enter">Enter = true, Exit = False</param> private void CompareTriggerEvent(Collision other, bool enter) { if (other.collider.CompareTag("ObjectInScene")) { string message = $"User is{(enter == true ? " " : " no longer ")}near <b>{other.gameObject.name}</b>"; if (enter == true) { ApplicationInsightsTracker.Instance.RecordProximityEvent(other.gameObject.name); } Debug.Log(message); } } }
Certifique-se de salvar suas alterações no Visual Studio antes de retornar ao Unity.
Capítulo 9 - Criar a classe DataFromAnalytics
Agora você precisará criar o script DataFromAnalytics , que é responsável por:
- Buscar dados analíticos sobre qual objeto foi mais abordado pela câmera.
- Usando as chaves de serviço, que permitem a comunicação com sua instância do Serviço Azure Application Insights.
- Classificando os objetos em cena, de acordo com o que tem a maior contagem de eventos.
- Alterar a cor do material, do objeto mais abordado, para verde.
Para criar o script:
Clique duas vezes na pasta Scripts para abri-la.
Clique com o botão direito do mouse dentro da pasta Scripts, clique em Criar>script C#. Nomeie o script DataFromAnalytics.
Clique duas vezes no script para abri-lo com o Visual Studio.
Insira os seguintes namespaces:
using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Networking;
Dentro do script, insira o seguinte:
/// <summary> /// Number of most recent events to be queried /// </summary> private int _quantityOfEventsQueried = 10; /// <summary> /// The timespan with which to query. Needs to be in hours. /// </summary> private int _timepspanAsHours = 24; /// <summary> /// A list of the objects in the scene /// </summary> private List<GameObject> _listOfGameObjectsInScene; /// <summary> /// Number of queries which have returned, after being sent. /// </summary> private int _queriesReturned = 0; /// <summary> /// List of GameObjects, as the Key, with their event count, as the Value. /// </summary> private List<KeyValuePair<GameObject, int>> _pairedObjectsWithEventCount = new List<KeyValuePair<GameObject, int>>(); // Use this for initialization void Start() { // Find all objects in scene which have the ObjectInScene tag (as there may be other GameObjects in the scene which you do not want). _listOfGameObjectsInScene = GameObject.FindGameObjectsWithTag("ObjectInScene").ToList(); FetchAnalytics(); }
Dentro da classe DataFromAnalytics , logo após o método Start(), adicione o seguinte método chamado FetchAnalytics(). Esse método é responsável por preencher a lista de pares de valores de chave, com um GameObject e um número de contagem de eventos de espaço reservado. Em seguida, inicializa a co-rotina GetWebRequest(). A estrutura de consulta da chamada para o Application Insights também pode ser encontrada nesse método, como o ponto de extremidade da URL de consulta.
private void FetchAnalytics() { // Iterate through the objects in the list for (int i = 0; i < _listOfGameObjectsInScene.Count; i++) { // The current event number is not known, so set it to zero. int eventCount = 0; // Add new pair to list, as placeholder, until eventCount is known. _pairedObjectsWithEventCount.Add(new KeyValuePair<GameObject, int>(_listOfGameObjectsInScene[i], eventCount)); // Set the renderer of the object to the default color, white _listOfGameObjectsInScene[i].GetComponent<Renderer>().material.color = Color.white; // Create the appropriate object name using Insights structure string objectName = _listOfGameObjectsInScene[i].name; // Build the queryUrl for this object. string queryUrl = Uri.EscapeUriString(string.Format( "https://api.applicationinsights.io/v1/apps/{0}/events/$all?timespan=PT{1}H&$search={2}&$select=customMetric/name&$top={3}&$count=true", ApplicationInsightsTracker.Instance.applicationId, _timepspanAsHours, "Gazed " + objectName, _quantityOfEventsQueried)); // Send this object away within the WebRequest Coroutine, to determine it is event count. StartCoroutine("GetWebRequest", new KeyValuePair<string, int>(queryUrl, i)); } }
Logo abaixo do método FetchAnalytics(), adicione um método chamado GetWebRequest(), que retorna um IEnumerator. Esse método é responsável por solicitar o número de vezes que um evento, correspondente a um GameObject específico, foi chamado dentro do Application Insights. Quando todas as consultas enviadas forem retornadas, o método DetermineWinner() será chamado.
/// <summary> /// Requests the data count for number of events, according to the /// input query URL. /// </summary> /// <param name="webQueryPair">Query URL and the list number count.</param> /// <returns></returns> private IEnumerator GetWebRequest(KeyValuePair<string, int> webQueryPair) { // Set the URL and count as their own variables (for readability). string url = webQueryPair.Key; int currentCount = webQueryPair.Value; using (UnityWebRequest unityWebRequest = UnityWebRequest.Get(url)) { DownloadHandlerBuffer handlerBuffer = new DownloadHandlerBuffer(); unityWebRequest.downloadHandler = handlerBuffer; unityWebRequest.SetRequestHeader("host", "api.applicationinsights.io"); unityWebRequest.SetRequestHeader("x-api-key", ApplicationInsightsTracker.Instance.API_Key); yield return unityWebRequest.SendWebRequest(); if (unityWebRequest.isNetworkError) { // Failure with web request. Debug.Log("<color=red>Error Sending:</color> " + unityWebRequest.error); } else { // This query has returned, so add to the current count. _queriesReturned++; // Initialize event count integer. int eventCount = 0; // Deserialize the response with the custom Analytics class. Analytics welcome = JsonConvert.DeserializeObject<Analytics>(unityWebRequest.downloadHandler.text); // Get and return the count for the Event if (int.TryParse(welcome.OdataCount, out eventCount) == false) { // Parsing failed. Can sometimes mean that the Query URL was incorrect. Debug.Log("<color=red>Failure to Parse Data Results. Check Query URL for issues.</color>"); } else { // Overwrite the current pair, with its actual values, now that the event count is known. _pairedObjectsWithEventCount[currentCount] = new KeyValuePair<GameObject, int>(_pairedObjectsWithEventCount[currentCount].Key, eventCount); } // If all queries (compared with the number which was sent away) have // returned, then run the determine winner method. if (_queriesReturned == _pairedObjectsWithEventCount.Count) { DetermineWinner(); } } } }
O próximo método é DetermineWinner(), que classifica a lista de pares GameObject e Int, de acordo com a maior contagem de eventos. Em seguida, ele altera a cor do material desse GameObject para verde (como feedback para ele ter a maior contagem). Isso exibe uma mensagem com os resultados da análise.
/// <summary> /// Call to determine the keyValue pair, within the objects list, /// with the highest event count. /// </summary> private void DetermineWinner() { // Sort the values within the list of pairs. _pairedObjectsWithEventCount.Sort((x, y) => y.Value.CompareTo(x.Value)); // Change its colour to green _pairedObjectsWithEventCount.First().Key.GetComponent<Renderer>().material.color = Color.green; // Provide the winner, and other results, within the console window. string message = $"<b>Analytics Results:</b>\n " + $"<i>{_pairedObjectsWithEventCount.First().Key.name}</i> has the highest event count, " + $"with <i>{_pairedObjectsWithEventCount.First().Value.ToString()}</i>.\nFollowed by: "; for (int i = 1; i < _pairedObjectsWithEventCount.Count; i++) { message += $"{_pairedObjectsWithEventCount[i].Key.name}, " + $"with {_pairedObjectsWithEventCount[i].Value.ToString()} events.\n"; } Debug.Log(message); }
Adicione a estrutura de classe que será usada para desserializar o objeto JSON, recebido do Application Insights. Adicione essas classes na parte inferior do arquivo de classe DataFromAnalytics , fora da definição de classe.
/// <summary> /// These classes represent the structure of the JSON response from Azure Insight /// </summary> [Serializable] public class Analytics { [JsonProperty("@odata.context")] public string OdataContext { get; set; } [JsonProperty("@odata.count")] public string OdataCount { get; set; } [JsonProperty("value")] public Value[] Value { get; set; } } [Serializable] public class Value { [JsonProperty("customMetric")] public CustomMetric CustomMetric { get; set; } } [Serializable] public class CustomMetric { [JsonProperty("name")] public string Name { get; set; } }
Certifique-se de salvar suas alterações no Visual Studio antes de retornar ao Unity.
Capítulo 10 - Criar a classe Movement
O script Movement é o próximo script que você precisará criar. É responsável por:
- Movendo a câmera principal de acordo com a direção que a câmera está olhando.
- Adicionar todos os outros scripts a objetos de cena.
Para criar o script:
Clique duas vezes na pasta Scripts para abri-la.
Clique com o botão direito do mouse dentro da pasta Scripts, clique em Criar>script C#. Nomeie o script como Movimento.
Clique duas vezes no script para abri-lo com o Visual Studio.
Substitua o código existente pelo seguinte:
using UnityEngine; using UnityEngine.XR.WSA.Input; public class Movement : MonoBehaviour { /// <summary> /// The rendered object representing the right controller. /// </summary> public GameObject Controller; /// <summary> /// The movement speed of the user. /// </summary> public float UserSpeed; /// <summary> /// Provides whether source updates have been registered. /// </summary> private bool _isAttached = false; /// <summary> /// The chosen controller hand to use. /// </summary> private InteractionSourceHandedness _handness = InteractionSourceHandedness.Right; /// <summary> /// Used to calculate and proposes movement translation. /// </summary> private Vector3 _playerMovementTranslation; private void Start() { // You are now adding components dynamically // to ensure they are existing on the correct object // Add all camera related scripts to the camera. Camera.main.gameObject.AddComponent<Gaze>(); Camera.main.gameObject.AddComponent<ObjectTrigger>(); // Add all other scripts to this object. gameObject.AddComponent<ApplicationInsightsTracker>(); gameObject.AddComponent<DataFromAnalytics>(); } // Update is called once per frame void Update() { } }
Dentro da classe Movement, abaixo do método Update() vazio, insira os seguintes métodos que permitem ao usuário usar o controlador manual para se mover no espaço virtual:
/// <summary> /// Used for tracking the current position and rotation of the controller. /// </summary> private void UpdateControllerState() { #if UNITY_WSA && UNITY_2017_2_OR_NEWER // Check for current connected controllers, only if WSA. string message = string.Empty; if (InteractionManager.GetCurrentReading().Length > 0) { foreach (var sourceState in InteractionManager.GetCurrentReading()) { if (sourceState.source.kind == InteractionSourceKind.Controller && sourceState.source.handedness == _handness) { // If a controller source is found, which matches the selected handness, // check whether interaction source updated events have been registered. if (_isAttached == false) { // Register events, as not yet registered. message = "<color=green>Source Found: Registering Controller Source Events</color>"; _isAttached = true; InteractionManager.InteractionSourceUpdated += InteractionManager_InteractionSourceUpdated; } // Update the position and rotation information for the controller. Vector3 newPosition; if (sourceState.sourcePose.TryGetPosition(out newPosition, InteractionSourceNode.Pointer) && ValidPosition(newPosition)) { Controller.transform.localPosition = newPosition; } Quaternion newRotation; if (sourceState.sourcePose.TryGetRotation(out newRotation, InteractionSourceNode.Pointer) && ValidRotation(newRotation)) { Controller.transform.localRotation = newRotation; } } } } else { // Controller source not detected. message = "<color=blue>Trying to detect controller source</color>"; if (_isAttached == true) { // A source was previously connected, however, has been lost. Disconnected // all registered events. _isAttached = false; InteractionManager.InteractionSourceUpdated -= InteractionManager_InteractionSourceUpdated; message = "<color=red>Source Lost: Detaching Controller Source Events</color>"; } } if(message != string.Empty) { Debug.Log(message); } #endif }
/// <summary> /// This registered event is triggered when a source state has been updated. /// </summary> /// <param name="obj"></param> private void InteractionManager_InteractionSourceUpdated(InteractionSourceUpdatedEventArgs obj) { if (obj.state.source.handedness == _handness) { if(obj.state.thumbstickPosition.magnitude > 0.2f) { float thumbstickY = obj.state.thumbstickPosition.y; // Vertical Input. if (thumbstickY > 0.3f || thumbstickY < -0.3f) { _playerMovementTranslation = Camera.main.transform.forward; _playerMovementTranslation.y = 0; transform.Translate(_playerMovementTranslation * UserSpeed * Time.deltaTime * thumbstickY, Space.World); } } } }
/// <summary> /// Check that controller position is valid. /// </summary> /// <param name="inputVector3">The Vector3 to check</param> /// <returns>The position is valid</returns> private bool ValidPosition(Vector3 inputVector3) { return !float.IsNaN(inputVector3.x) && !float.IsNaN(inputVector3.y) && !float.IsNaN(inputVector3.z) && !float.IsInfinity(inputVector3.x) && !float.IsInfinity(inputVector3.y) && !float.IsInfinity(inputVector3.z); } /// <summary> /// Check that controller rotation is valid. /// </summary> /// <param name="inputQuaternion">The Quaternion to check</param> /// <returns>The rotation is valid</returns> private bool ValidRotation(Quaternion inputQuaternion) { return !float.IsNaN(inputQuaternion.x) && !float.IsNaN(inputQuaternion.y) && !float.IsNaN(inputQuaternion.z) && !float.IsNaN(inputQuaternion.w) && !float.IsInfinity(inputQuaternion.x) && !float.IsInfinity(inputQuaternion.y) && !float.IsInfinity(inputQuaternion.z) && !float.IsInfinity(inputQuaternion.w); }
Por fim, adicione a chamada de método dentro do método Update( ).
// Update is called once per frame void Update() { UpdateControllerState(); }
Certifique-se de salvar suas alterações no Visual Studio antes de retornar ao Unity.
Capítulo 11 - Configurando as referências de scripts
Neste capítulo, você precisa colocar o script Movement no pai da câmera e definir seus alvos de referência. Esse script tratará de colocar os outros scripts onde eles precisam estar.
Na pasta Scripts no Painel do projeto, arraste o script Movimento para o objeto pai da câmera, localizado no painel Hierarquia.
Clique no pai da câmera. No Painel Hierarquia, arraste o objeto Mão Direita do Painel Hierarquia para o destino de referência, Controlador, no Painel Inspetor. Defina a velocidade do usuário para 5, como mostrado na imagem abaixo.
Capítulo 12 - Construir o projeto Unity
Tudo o que era necessário para a seção Unity deste projeto já foi concluído, então é hora de construí-lo a partir de Unity.
Navegue até Configurações de compilação, (Configurações de compilação de arquivo>).
Na janela Configurações de compilação, clique em Compilar.
Uma janela do Explorador de Arquivos será exibida, solicitando um local para a compilação. Crie uma nova pasta (clicando em Nova pasta no canto superior esquerdo) e nomeie-a como BUILDS.
Abra a nova pasta BUILDS e crie outra pasta (usando New Folder mais uma vez) e nomeie-a MR_Azure_Application_Insights.
Com a pasta MR_Azure_Application_Insights selecionada, clique em Selecionar pasta. O projeto levará cerca de um minuto para ser construído.
Após a compilação, o Explorador de Arquivos aparecerá mostrando o local do seu novo projeto.
Capítulo 13 - Implantar MR_Azure_Application_Insights aplicativo em sua máquina
Para implantar o aplicativo MR_Azure_Application_Insights em sua máquina local:
Abra o arquivo de solução do seu aplicativo MR_Azure_Application_Insights no Visual Studio.
Na Plataforma de Solução, selecione x86, Máquina Local.
Na Configuração da Solução, selecione Depurar.
Vá para o menu Build e clique em Deploy Solution para fazer sideload do aplicativo para sua máquina.
Seu aplicativo agora deve aparecer na lista de aplicativos instalados, pronto para ser iniciado.
Inicie o aplicativo de realidade mista.
Mova-se pela cena, aproximando-se de objetos e olhando para eles, quando o Serviço Azure Insight tiver coletado dados de evento suficientes, ele definirá o objeto que foi mais abordado como verde.
Importante
Enquanto o tempo médio de espera para que os Eventos e Métricas sejam coletados pelo Serviço leva cerca de 15 minutos, em algumas ocasiões pode levar até 1 hora.
Capítulo 14 - O portal do Serviço Application Insights
Depois de percorrer a cena e olhar para vários objetos, você pode ver os dados coletados no portal do Serviço do Application Insights .
Volte para o portal do Serviço do Application Insights.
Selecione Explorador de Métricas.
Ele será aberto em uma guia contendo o gráfico, que representa os Eventos e Métricas relacionados ao seu aplicativo. Como mencionado acima, pode levar algum tempo (até 1 hora) para que os dados sejam exibidos no gráfico
Selecione a barra Eventos no Total de Eventos por Versão do Aplicativo para ver um detalhamento detalhado dos eventos com seus nomes.
Seu aplicativo de serviço do Application Insights concluído
Parabéns, você criou um aplicativo de realidade mista que aproveita o Serviço do Application Insights para monitorar a atividade do usuário em seu aplicativo.
Exercícios de Bónus
Exercício 1
Tente gerar, em vez de criar manualmente, os objetos ObjectInScene e defina suas coordenadas no plano dentro de seus scripts. Dessa forma, você pode perguntar ao Azure qual era o objeto mais popular (seja de resultados de olhar ou proximidade) e gerar um extra desses objetos.
Exercício 2
Classifique os resultados do Application Insights por tempo, para obter os dados mais relevantes, e implemente esses dados sensíveis ao tempo em seu aplicativo.