HoloLens (1ª geração) e Azure 309: Application Insights
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 o HoloLens 2. Este aviso será atualizado com um link para esses tutoriais quando eles forem postados.
Neste curso, você aprenderá a adicionar recursos do Application Insights a um aplicativo de realidade misturada, 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 gerenciem-nas em 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 do Application Insights.
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 e se mova em uma cena.
- Dispare o envio de análises para o Serviço Application Insights, usando Gaze e Proximidade para objetos na cena.
- O aplicativo também chamará 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ê possa estar criando.
Suporte a dispositivos
Curso | HoloLens | Headsets imersivos |
---|---|---|
MR e Azure 309: Application Insights | ✔️ | ✔️ |
Observação
Embora este curso se concentre principalmente em headsets imersivos (VR) do Windows Mixed Reality, você também pode aplicar o que aprendeu neste curso ao Microsoft HoloLens. Ao acompanhar o curso, você verá anotações sobre as alterações que talvez precise empregar para dar suporte ao HoloLens. Ao usar o HoloLens, você pode observar algum eco durante a captura de voz.
Pré-requisitos
Observação
Este tutorial foi desenvolvido para desenvolvedores que têm experiência básica com Unity e C#. Esteja ciente também de que os pré-requisitos e as instruções escritas 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 deste curso corresponderão perfeitamente ao que você encontrará em softwares mais recentes do que os listados abaixo.
Recomendamos o seguinte hardware e software para este curso:
- Um computador de desenvolvimento, compatível com Windows Mixed Reality para desenvolvimento de headset imersivo (VR)
- Windows 10 Fall Creators Update (ou posterior) com o modo de desenvolvedor habilitado
- O SDK mais recente do Windows 10
- Unidade 2017.4
- Visual Studio 2017
- Um headset imersivo (VR) Windows Mixed Reality ou Microsoft HoloLens com o modo Desenvolvedor habilitado
- Um conjunto de fones de ouvido com microfone embutido (se o fone de ouvido não tiver microfone e alto-falantes embutidos)
- 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 no tempo de compilação).
Aviso
Lembre-se de que os dados que vão para o Application Insights levam tempo, portanto, seja paciente. Se você quiser verificar se o Serviço recebeu seus dados, confira o Capítulo 14, que 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.
Entre 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 sala de aula ou laboratório, peça ajuda ao seu instrutor ou a um dos supervisores para configurar sua nova conta.
Depois de fazer logon, clique em Novo no canto superior esquerdo, pesquise Application Insights 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 à direita fornecerá uma descrição do Serviço do Azure Application Insights . Na parte inferior esquerda 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 você quiser ler mais sobre os Grupos de Recursos do Azure, visite o artigo do grupo de recursos.
Selecione um Local.
Você também precisará confirmar que entendeu 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 será exibida 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 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 do Application Insights.
Observação
Mantenha esta página da 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 de 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, pois você os usará em breve em seu código.
Para encontrar a Chave de Instrumentação, você precisará rolar para baixo na 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á 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 do Unity
Veja a seguir uma configuração típica para desenvolver com a realidade misturada e, como tal, é um bom modelo para outros projetos.
Abra o Unity e clique em Novo.
Agora você precisará fornecer um nome de projeto do Unity e inserir MR_Azure_Application_Insights. Certifique-se de que o modelo esteja definido como 3D. Defina o local para algum lugar apropriado para você (lembre-se, 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 Visual Studio 2017. Feche a janela Preferências.
Em seguida, vá para Configurações de Build de Arquivo > e alterne a plataforma para Plataforma Universal do Windows, clicando no botão Alternar Plataforma.
Vá para Configurações de Build de Arquivo > e certifique-se de que:
O dispositivo de destino está definido como Qualquer dispositivo
Para o Microsoft HoloLens, defina Dispositivo de Destino como HoloLens.
O Tipo de Construção está definido como D3D
O SDK está definido como Instalado mais recente
Build and Run está definido como Computador Local
Salve a cena e adicione-a à compilação.
Faça isso selecionando Adicionar cenas abertas. Uma janela de salvamento aparecerá.
Crie uma nova pasta para esta e qualquer cena futura e clique no botão Nova pasta , para criar uma nova pasta, nomeie-a como 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 Build, devem ser deixadas como padrão por enquanto.
Na janela Configurações de construçã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 tempo de execução do 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, marque:
InternetClient
Mais abaixo no painel, em Configurações 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 dela.
Feche a janela Configurações de Build.
Salve sua cena e projeto (FILE>SAVE SCENE / FILE>SAVE PROJECT).
Capítulo 3 – Importar o pacote do Unity
Importante
Se você quiser ignorar os componentes de configuração do Unity deste curso e continuar direto para o código, sinta-se à vontade para baixar este Azure-MR-309.unitypackage, 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, continue 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 a 7 nesta seção) não serão mais necessárias depois que o bug for resolvido.
Para importar o Application Insights para seu próprio projeto, verifique se você baixou o '.unitypackage', que contém os plug-ins. 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 do Unity que aparece, certifique-se de que tudo em (e incluindo) Plug-ins esteja 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 Qualquer plataforma esteja desmarcada, certifique-se de que o WSAPlayer também esteja desmarcado e clique em Aplicar. Fazer isso é apenas para confirmar que os arquivos estão configurados corretamente.
Observação
Marcar os plug-ins dessa forma os configura para serem usados apenas no Unity Editor. 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 esse arquivo e, no inspetor, verifique se Qualquer plataforma está desmarcada e, em seguida, verifique se apenas o WSAPlayer está marcado. Clique em Aplicar.
Agora você precisará seguir as etapas 4 a 6, mas para os plug-ins Newtonsoft. Veja a captura de tela abaixo para saber como deve ser o resultado.
Capítulo 4 - Configurar a câmera e os controles do usuário
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 mouse em uma área vazia no Painel de Hierarquia e, em seguida, em Criar>Vazio.
Renomeie o novo GameObject vazio para Camera Parent.
Clique com o botão direito do mouse em uma área vazia no Painel de hierarquia, depois em Objeto 3D e depois em Esfera.
Renomeie a esfera para a mão direita.
Defina a escala de transformação da mão direita para 0,1, 0,1, 0,1
Remova o componente Sphere Collider do lado direito clicando na engrenagem no componente Sphere Collider e, em seguida, em Remover 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 Mão direita como 0, 0, 0.
Capítulo 5 – Configurar os objetos na cena do Unity
Agora você criará algumas formas básicas para sua cena, com as quais o usuário pode interagir.
Clique com o botão direito do mouse em uma área vazia no Painel de Hierarquia, depois em Objeto 3D e selecione Plano.
Defina a Posição de transformação do plano 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 como 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 como 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 é Cor hexadecimal: #3592FFFF). Clique no botão fechar depois de escolher.
Arraste seu novo material da pasta Materiais, para o Plano recém-criado, dentro de sua cena (ou solte-o no objeto Plano dentro da Hierarquia).
Clique com o botão direito do mouse em uma á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 mouse em uma área vazia no Painel de Hierarquia e, em seguida, em Objeto 3D, Esfera.
- Com a Esfera selecionada, altere sua Posição de Transformação para: 10, 0, 0.
Observação
Esses 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 isso, eles precisam ser marcados. Selecione um dos objetos e, no painel Inspetor, clique em Adicionar marca..., que trocará o Inspetor pelo painel Marcas e camadas.
Clique no símbolo + (mais) e digite o nome da tag 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 detectados em sua cena.
Com a tag criada, agora você precisa aplicá-la a todos os três objetos. Na Hierarquia, mantenha pressionada a tecla Shift e clique nos objetos Cápsula, Cubo e Esfera e, em seguida, no Inspetor, clique no menu suspenso ao lado de Marca 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:
Criar 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.
Enviando eventos para a instância do Serviço Application Insights.
Para criar essa classe:
Clique com o botão direito do mouse no painel Projeto e, em seguida, em Criar>pasta. Nomeie a pasta Scripts.
Com a pasta Scripts criada, clique duas vezes nela para abrir. 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 serem os seguintes:
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;
Observação
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 frente a partir da Câmera Principal, para detectar qual objeto o usuário está olhando. Nesse caso, o Raycast precisará identificar se o usuário está olhando para um objeto com a marca ObjectInScene e, em seguida, contar por 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 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 detectar o hit de destino:
/// <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 ao Application Insights quando o usuário tiver examinado 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 script 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 é o ObjectTrigger, que é responsável por:
- Adicionando componentes necessários para colisão à câmera principal.
- Detectando 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 do Azure Application Insights.
- Classificando os objetos na cena, de acordo com qual 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(); }
Na 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-chave, com um GameObject e um número de contagem de eventos de espaço reservado. Em seguida, ele inicializa a corrotina 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 no 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 contagem de eventos mais alta. Em seguida, ele altera a cor do material desse GameObject para verde (como feedback para ele ter a contagem mais alta). 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 Movimento é o próximo script que você precisará criar. É responsável por:
- Movendo a câmera principal de acordo com a direção para a qual a câmera está olhando.
- Adicionando 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 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 de mão 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 de movimento 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 Projeto, arraste o script Movimento para o objeto Pai da câmera, localizado no Painel Hierarquia.
Clique no pai da câmera. No Painel de Hierarquia, arraste o objeto Mão Direita do Painel de Hierarquia para o destino de referência, Controlador, no Painel Inspetor. Defina a velocidade do usuário como 5, conforme mostrado na imagem abaixo.
Capítulo 12 – Criar o projeto do Unity
Tudo o que é necessário para a seção Unity deste projeto foi concluído, então é hora de criá-lo a partir do 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 aparecerá, 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 Nova pasta 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 a localização do seu novo projeto.
Capítulo 13 – Implantar MR_Azure_Application_Insights aplicativo em seu computador
Para implantar o aplicativo MR_Azure_Application_Insights em seu computador local:
Abra o arquivo de solução do seu aplicativo MR_Azure_Application_Insights no Visual Studio.
Na Plataforma de Solução, selecione x86, Computador Local.
Na Configuração da Solução, selecione Depurar.
Vá para o menu Compilar e clique em Implantar Solução para fazer o sideload do aplicativo em seu computador.
Seu aplicativo agora deve aparecer na lista de aplicativos instalados, pronto para ser iniciado.
Inicie o aplicativo de realidade misturada.
Mova-se pela cena, aproximando-se de objetos e olhando para eles, quando o Azure Insight Service tiver coletado dados de evento suficientes, ele definirá o objeto que foi mais abordado como verde.
Importante
Embora 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 observar vários objetos, você poderá ver os dados coletados no portal do Serviço Application Insights.
Volte para o portal do Serviço do Application Insights.
Selecione Metrics Explorer.
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 uma análise detalhada dos eventos com seus nomes.
Você concluiu seu aplicativo do Application Insights Service
Parabéns, você criou um aplicativo de realidade misturada que aproveita o Serviço 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 definir suas coordenadas no plano dentro de seus scripts. Dessa forma, você pode perguntar ao Azure qual era o objeto mais popular (seja a partir de resultados de foco 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.