HoloLens (1ère génération) et Azure 309 : Application Insights
Notes
Les tutoriels Mixed Reality Academy ont été conçus pour les appareils HoloLens (1re génération) et les casques immersifs de réalité mixte. Nous estimons qu’il est important de laisser ces tutoriels à la disposition des développeurs qui recherchent encore des conseils pour développer des applications sur ces appareils. Notez que ces tutoriels ne sont pas mis à jour avec les derniers ensembles d’outils ou interactions utilisés pour HoloLens 2. Ils sont fournis dans le but de fonctionner sur les appareils pris en charge. Il y aura une nouvelle série de tutoriels qui seront publiés à l’avenir qui montreront comment développer pour HoloLens 2. Cet avis sera mis à jour avec un lien vers ces tutoriels lorsqu’ils seront publiés.
Dans ce cours, vous allez apprendre à ajouter des fonctionnalités Application Insights à une application de réalité mixte, en utilisant l’API Azure Application Insights pour collecter des analyses concernant le comportement des utilisateurs.
Application Insights est un service Microsoft qui permet aux développeurs de collecter des analyses à partir de leurs applications et de les gérer à partir d’un portail facile à utiliser. L’analytique peut aller des performances aux informations personnalisées que vous souhaitez collecter. Pour plus d’informations, consultez la page Application Insights.
Après avoir terminé ce cours, vous aurez une application de casque immersif de réalité mixte, qui sera en mesure d’effectuer les opérations suivantes :
- Autorisez l’utilisateur à regarder et à se déplacer dans une scène.
- Déclenchez l’envoi d’analyses au service Application Insights à l’aide de La vue et de la proximité d’objets en scène.
- L’application appelle également le service, récupérant les informations sur l’objet qui a été le plus approché par l’utilisateur, au cours des dernières 24 heures. Cet objet va changer sa couleur en vert.
Ce cours vous apprendra à obtenir les résultats du service Application Insights dans un exemple d’application Unity. Il vous appartient d’appliquer ces concepts à une application personnalisée que vous créez peut-être.
Prise en charge des appareils
Cours | HoloLens | Casques immersifs |
---|---|---|
Réalité mixte - Azure - Cours 309 : Application Insights | ✔️ | ✔️ |
Notes
Bien que ce cours se concentre principalement sur Windows Mixed Reality casques immersifs (VR), vous pouvez également appliquer ce que vous avez appris dans ce cours à Microsoft HoloLens. Au fur et à mesure que vous suivez le cours, vous verrez des notes sur les modifications que vous devrez peut-être utiliser pour prendre en charge HoloLens. Lorsque vous utilisez HoloLens, vous pouvez remarquer un certain écho lors de la capture vocale.
Prérequis
Notes
Ce tutoriel est conçu pour les développeurs qui ont une expérience de base avec Unity et C#. Sachez également que les conditions préalables et les instructions écrites contenues dans ce document représentent ce qui a été testé et vérifié au moment de la rédaction (juillet 2018). Vous êtes libre d’utiliser les logiciels les plus récents, comme indiqué dans l’article installer les outils , mais il ne faut pas supposer que les informations de ce cours correspondront parfaitement à ce que vous trouverez dans les logiciels plus récents que ce qui est listé ci-dessous.
Nous vous recommandons les matériels et logiciels suivants pour ce cours :
- Un PC de développement, compatible avec Windows Mixed Reality pour le développement de casque immersif (VR)
- Windows 10 Fall Creators Update (ou version ultérieure) avec le mode Développeur activé
- Le dernier Windows 10 SDK
- Unity 2017.4
- Visual Studio 2017
- Casque ou Microsoft HoloLensimmersif (VR) Windows Mixed Reality avec le mode Développeur activé
- Un ensemble de casques avec un microphone intégré (si le casque n’a pas de micro et de haut-parleurs intégrés)
- Accès Internet pour l’installation d’Azure et la récupération des données Application Insights
Avant de commencer
Pour éviter les problèmes lors de la génération de ce projet, il est fortement recommandé de créer le projet dans ce didacticiel dans un dossier racine ou proche de la racine (les chemins d’accès de dossiers longs peuvent provoquer des problèmes au moment de la génération).
Avertissement
N’oubliez pas que l’accès des données à Application Insights prend du temps. Soyez donc patient. Si vous souhaitez case activée si le service a reçu vos données, case activée le chapitre 14, qui vous indiquera comment naviguer dans le portail.
Chapitre 1 - Portail Azure
Pour utiliser Application Insights, vous devez créer et configurer un service Application Insights dans le Portail Azure.
Connectez-vous au Portail Azure.
Notes
Si vous n’avez pas encore de compte Azure, vous devez en créer un. Si vous suivez ce tutoriel dans une situation de classe ou de laboratoire, demandez à votre instructeur ou à l’un des surveillants de l’aide pour configurer votre nouveau compte.
Une fois connecté, cliquez sur Nouveau dans le coin supérieur gauche, recherchez Application Insights, puis cliquez sur Entrée.
Notes
Le mot Nouveau a peut-être été remplacé par Créer une ressource, dans les portails plus récents.
La nouvelle page à droite fournit une description du service Azure Application Insights. En bas à gauche de cette page, sélectionnez le bouton Créer pour créer une association avec ce service.
Une fois que vous avez cliqué sur Créer :
Insérez le nom souhaité pour ce service instance.
Comme Type d’application, sélectionnez Général.
Sélectionnez un abonnement approprié.
Choisissez un groupe de ressources ou créez-en un. Un groupe de ressources permet de surveiller, de contrôler l’accès, de provisionner et de gérer la facturation pour une collection de ressources Azure. Il est recommandé de conserver tous les services Azure associés à un seul projet (par exemple, ces cours) sous un groupe de ressources commun).
Si vous souhaitez en savoir plus sur les groupes de ressources Azure, consultez l’article sur les groupes de ressources.
Sélectionnez un emplacement.
Vous devez également confirmer que vous avez bien compris les conditions générales appliquées à ce service.
Sélectionnez Create (Créer).
Une fois que vous avez cliqué sur Créer, vous devez attendre que le service soit créé, ce qui peut prendre une minute.
Une notification s’affiche dans le portail une fois le service instance créé.
Sélectionnez les notifications pour explorer votre nouveau service instance.
Cliquez sur le bouton Accéder à la ressource dans la notification pour explorer votre nouvelle instance de service. Vous serez dirigé vers votre nouvelle instance Application Insights Service.
Notes
Gardez cette page web ouverte et facile d’accès, vous y retournerez souvent pour voir les données collectées.
Important
Pour implémenter Application Insights, vous devez utiliser trois (3) valeurs spécifiques : Clé d’instrumentation, ID d’application et Clé API. Vous verrez ci-dessous comment récupérer ces valeurs à partir de votre service. Veillez à noter ces valeurs sur une page vide du Bloc-notes , car vous les utiliserez bientôt dans votre code.
Pour rechercher la clé d’instrumentation, vous devez faire défiler la liste des fonctions de service et sélectionner Propriétés. L’onglet affiché affiche la clé de service.
Un peu sous Propriétés, vous trouverez l’accès à l’API, sur lequel vous devez cliquer. Le panneau de droite indique l’ID d’application de votre application.
Le panneau ID d’application étant toujours ouvert, cliquez sur Créer une clé API, ce qui ouvre le panneau Créer une clé API .
Dans le panneau Créer une clé API maintenant ouvert, tapez une description, puis cochez les trois cases.
Cliquez sur Générer la clé. Votre clé API sera créée et affichée.
Avertissement
C’est la seule fois que votre clé de service s’affiche. Veillez donc à en faire une copie dès maintenant.
Chapitre 2 - Configurer le projet Unity
Voici une configuration classique pour le développement avec la réalité mixte, et en tant que tel, est un bon modèle pour d’autres projets.
Ouvrez Unity , puis cliquez sur Nouveau.
Vous devez maintenant fournir un nom de projet Unity, insérer MR_Azure_Application_Insights. Vérifiez que le modèle est défini sur 3D. Définissez l’emplacement sur un emplacement approprié pour vous (n’oubliez pas qu’il est préférable de se rapprocher des répertoires racine). Cliquez ensuite sur Créer un projet.
Une fois Unity ouvert, il est utile de vérifier que l’éditeur de script par défaut est défini sur Visual Studio. Accédez à Modifier les > préférences , puis, dans la nouvelle fenêtre, accédez à Outils externes. Remplacez Éditeur de script externe par Visual Studio 2017. Fermez la fenêtre Préférences.
Ensuite, accédez à Paramètres de génération de fichiers > et basculez la plateforme sur plateforme Windows universelle, en cliquant sur le bouton Changer de plateforme.
Accédez à Paramètres > de génération de fichiers et assurez-vous que :
L’appareil cible est défini sur N’importe quel appareil
Pour le Microsoft HoloLens, définissez Appareil cible sur HoloLens.
Le type de build est défini sur D3D
Le KIT DE DÉVELOPPEMENT LOGICIEL (SDK) est défini sur Dernier installé
Générer et exécuter est défini sur Ordinateur local
Enregistrez la scène et ajoutez-la à la build.
Pour ce faire, sélectionnez Ajouter des scènes ouvertes. Une fenêtre d’enregistrement s’affiche.
Créez un dossier pour ce dossier et toute scène ultérieure, puis cliquez sur le bouton Nouveau dossier pour créer un dossier, nommez-le Scènes.
Ouvrez votre dossier Scenes nouvellement créé, puis dans le champ de texte Nom de fichier : , tapez ApplicationInsightsScene, puis cliquez sur Enregistrer.
Les paramètres restants, dans Paramètres de build, doivent être conservés par défaut pour l’instant.
Dans la fenêtre Paramètres de build, sélectionnez Paramètres du lecteur pour ouvrir le panneau associé dans l’espace où se trouve l’inspecteur.
Dans ce panneau, quelques paramètres doivent être vérifiés :
Sous l’onglet Autres paramètres :
La version du runtime de script doit être expérimentale (équivalent.NET 4.6), ce qui déclenche la nécessité de redémarrer l’éditeur.
Le serveur principal de script doit être .NET
Le niveau de compatibilité de l’API doit être .NET 4.6
Sous l’onglet Paramètres de publication, sous Fonctionnalités, case activée :
InternetClient
Plus bas dans le panneau, dans Paramètres XR (sous Paramètres de publication), cochez Réalité virtuelle prise en charge, assurez-vous que le KIT de développement logiciel (SDK) Windows Mixed Reality est ajouté.
De retour dans les paramètres de build, les projets Unity C# ne sont plus grisés ; cochez la case en regard de ceci.
Fermez la fenêtre Build Settings.
Enregistrez votre scène et votre projet (FILE>SAVE SCENE / FILE>SAVE PROJECT).
Chapitre 3 - Importer le package Unity
Important
Si vous souhaitez ignorer les composants de configuration Unity de ce cours et continuer directement dans le code, n’hésitez pas à télécharger ce package Azure-MR-309.unity, puis à l’importer dans votre projet en tant que package personnalisé. Cela contiendra également les DLL du chapitre suivant. Après l’importation, passez au chapitre 6.
Important
Pour utiliser Application Insights dans Unity, vous devez importer la DLL pour celui-ci, ainsi que la DLL Newtonsoft. Il existe actuellement un problème connu dans Unity qui nécessite la reconfiguration des plug-ins après l’importation. Ces étapes (4 à 7 dans cette section) ne seront plus nécessaires une fois le bogue résolu.
Pour importer Application Insights dans votre propre projet, vérifiez que vous avez téléchargé le fichier « .unitypackage », qui contient les plug-ins. Ensuite, procédez comme suit :
Ajoutez le fichier .unitypackage** à Unity à l’aide de l’option > de menu Package personnalisé d’importation de ressources>.
Dans la zone Importer le package Unity qui s’affiche, vérifiez que tous les éléments sous (et y compris) Plug-ins sont sélectionnés.
Cliquez sur le bouton Importer pour ajouter les éléments à votre projet.
Accédez au dossier Insights sous Plug-ins dans l’affichage Projet et sélectionnez les plug-ins suivants uniquement :
- Microsoft.ApplicationInsights
Une fois ce plug-in sélectionné, vérifiez que n’importe quelle plateforme est décochée, vérifiez que WSAPlayer est également décoché, puis cliquez sur Appliquer. Cela permet simplement de vérifier que les fichiers sont correctement configurés.
Notes
Marquer les plug-ins comme ceci, les configure pour qu’ils soient utilisés uniquement dans l’éditeur Unity. Il existe un autre ensemble de DLL dans le dossier WSA qui seront utilisées une fois le projet exporté à partir d’Unity.
Ensuite, vous devez ouvrir le dossier WSA , dans le dossier Insights . Vous verrez une copie du fichier que vous avez configuré. Sélectionnez ce fichier, puis, dans l’inspecteur, vérifiez que toute plateforme est décochée, puis vérifiez que seulWSAPlayer est coché. Cliquez sur Appliquer.
Vous devez maintenant suivre les étapes 4 à 6, mais pour les plug-ins Newtonsoft à la place. Consultez la capture d’écran ci-dessous pour savoir à quoi doit ressembler le résultat.
Chapitre 4 - Configurer la caméra et les contrôles utilisateur
Dans ce chapitre, vous allez configurer la caméra et les contrôles pour permettre à l’utilisateur de voir et de se déplacer dans la scène.
Cliquez avec le bouton droit dans une zone vide du panneau hiérarchie, puis sur Créer>vide.
Renommez le nouvel Objet GameObject vide en Camera Parent.
Cliquez avec le bouton droit dans une zone vide dans le panneau hiérarchie, puis sur Objet 3D, puis sur Sphere.
Renommez la sphère en Main droite.
Définissez l’échelle de transformation de la main droite sur 0.1, 0.1, 0.1
Supprimez le composant Sphere Collider de la main droite en cliquant sur l’engrenage dans le composant Sphere Collider , puis Supprimer le composant.
Dans le panneau hiérarchie, faites glisser les objets Main Camera et Right Hand sur l’objet Camera Parent .
Définissez la position de transformation de la caméra principale et de l’objet main droite sur 0, 0, 0.
Chapitre 5 - Configurer les objets dans la scène Unity
Vous allez maintenant créer des formes de base pour votre scène, avec lesquelles l’utilisateur peut interagir.
Cliquez avec le bouton droit dans une zone vide du panneau hiérarchie, puis sur Objet 3D, puis sélectionnez Plan.
Définissez la position de transformation du plan sur 0, -1, 0.
Définissez l’échelle de transformation de plan sur 5, 1, 5.
Créez un matériau de base à utiliser avec votre objet Plane , afin que les autres formes soient plus faciles à voir. Accédez à votre panneau de projet, cliquez avec le bouton droit, puis cliquez sur Créer, puis sur Dossier pour créer un dossier. Nommez-le Matériaux.
Ouvrez le dossier Matériaux , puis cliquez avec le bouton droit, cliquez sur Créer, puis sur Matériau pour créer un nouveau matériau. Nommez-le Bleu.
Une fois le nouveau matériau bleu sélectionné, regardez l’inspecteur, puis cliquez sur la fenêtre rectangulaire à côté d’Albedo. Sélectionnez une couleur bleue (l’image ci-dessous est Couleur hexadécimal : #3592FFFF). Cliquez sur le bouton Fermer une fois que vous avez choisi.
Faites glisser votre nouveau matériel à partir du dossier Matériaux , sur votre plan nouvellement créé, au sein de votre scène (ou déposez-le sur l’objet Plane dans la hiérarchie).
Cliquez avec le bouton droit dans une zone vide du panneau hiérarchie, puis sur Objet 3D, Capsule.
- Une fois la capsule sélectionnée, modifiez sa positionde transformation sur : -10, 1, 0.
Cliquez avec le bouton droit dans une zone vide du panneau hiérarchie, puis sur Objet 3D, Cube.
- Une fois le cube sélectionné, remplacez sa positionde transformation par : 0, 0, 10.
Cliquez avec le bouton droit dans une zone vide du panneau hiérarchie, puis sur Objet 3D, Sphère.
- Une fois la sphère sélectionnée, remplacez sa positionde transformation par : 10, 0, 0.
Notes
Ces valeurs position sont des suggestions. Vous êtes libre de définir les positions des objets sur ce que vous souhaitez, bien qu’il soit plus facile pour l’utilisateur de l’application si les distances des objets ne sont pas trop éloignées de la caméra.
Lorsque votre application est en cours d’exécution, elle doit être en mesure d’identifier les objets dans la scène. Pour ce faire, ils doivent être marqués. Sélectionnez l’un des objets et, dans le panneau Inspecteur, cliquez sur Ajouter une balise... pour permuter l’inspecteur avec le panneau Balises & Calques.
Cliquez sur le symbole + (plus), puis tapez le nom de la balise ObjectInScene.
Avertissement
Si vous utilisez un nom différent pour votre balise, vous devez vous assurer que cette modification est également apportée aux scripts DataFromAnalytics, ObjectTrigger et Gaze, plus tard, afin que vos objets soient trouvés et détectés dans votre scène.
Une fois la balise créée, vous devez maintenant l’appliquer aux trois objets. À partir de la hiérarchie, maintenez la touche Maj enfoncée, cliquez sur les objets Capsule, Cube et Sphere, puis dans l’Inspecteur, cliquez sur le menu déroulant en regard de Balise, puis sur la balise ObjectInScene que vous avez créée.
Chapitre 6 - Créer la classe ApplicationInsightsTracker
Le premier script que vous devez créer est ApplicationInsightsTracker, qui est responsable des tâches suivantes :
Création d’événements basés sur les interactions utilisateur à soumettre à Azure Application Insights.
Création de noms d’événements appropriés, en fonction de l’interaction de l’utilisateur.
Envoi d’événements au service Application Insights instance.
Pour créer cette classe :
Cliquez avec le bouton droit dans le volet Projet, puis sur Créer un>dossier. Nommez le dossier Scripts.
Une fois le dossier Scripts créé, double-cliquez dessus pour l’ouvrir. Ensuite, dans ce dossier, cliquez avec le bouton droit sur Créer un>script C#. Nommez le script ApplicationInsightsTracker.
Double-cliquez sur le nouveau script ApplicationInsightsTracker pour l’ouvrir avec Visual Studio.
Mettez à jour les espaces de noms en haut du script comme suit :
using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; using UnityEngine;
À l’intérieur de la classe, insérez les variables suivantes :
/// <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;
Notes
Définissez correctement les valeurs instrumentationKey, applicationId et API_Key à l’aide des clés de service du portail Azure, comme indiqué au chapitre 1, étape 9.
Ajoutez ensuite les méthodes Start() et Awake(), qui seront appelées lors de l’initialisation de la classe :
/// <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; }
Ajoutez les méthodes responsables de l’envoi des événements et des métriques inscrits par votre application :
/// <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); }
Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.
Chapitre 7 - Créer le script De regard
Le script suivant à créer est le script De regard . Ce script est chargé de créer un Raycast qui sera projeté vers l’avant à partir de la caméra principale, afin de détecter l’objet que l’utilisateur examine. Dans ce cas, raycast doit déterminer si l’utilisateur regarde un objet avec la balise ObjectInScene , puis compter la durée pendant laquelle l’utilisateur regarde cet objet.
Double-cliquez sur le dossier Scripts pour l’ouvrir.
Cliquez avec le bouton droit dans le dossier Scripts , puis cliquez sur Créer un>script C#. Nommez le script Gaze.
Double-cliquez sur le script pour l’ouvrir avec Visual Studio.
Remplacez le code existant par le code ci-dessous :
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; }
Le code des méthodes Awake() et Start() doit maintenant être ajouté.
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; }
Dans la classe Gaze , ajoutez le code suivant dans la méthode Update() pour projeter un Raycast et détecter la cible atteinte :
/// <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; } }
Ajoutez la méthode ResetFocusedObject() pour envoyer des données à Application Insights lorsque l’utilisateur a consulté un objet.
/// <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; } } }
Vous avez maintenant terminé le script De regard . Enregistrez vos modifications dans Visual Studio avant de revenir à Unity.
Chapitre 8 - Créer la classe ObjectTrigger
Le script suivant que vous devez créer est ObjectTrigger, qui est responsable des opérations suivantes :
- Ajout des composants nécessaires à la collision à la caméra principale.
- Détection si la caméra se trouve à proximité d’un objet marqué comme ObjectInScene.
Pour créer le script :
Double-cliquez sur le dossier Scripts pour l’ouvrir.
Cliquez avec le bouton droit dans le dossier Scripts , puis cliquez sur Créer un>script C#. Nommez le script ObjectTrigger.
Double-cliquez sur le script pour l’ouvrir avec Visual Studio. Remplacez le code existant par le code ci-dessous :
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); } } }
Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.
Chapitre 9 - Créer la classe DataFromAnalytics
Vous devez maintenant créer le script DataFromAnalytics , qui est responsable des opérations suivantes :
- Extraction de données analytiques sur l’objet qui a été le plus approché par la caméra.
- À l’aide des clés de service, qui permettent la communication avec votre instance Azure Application Insights Service.
- Tri des objets dans la scène, selon laquelle a le nombre d’événements le plus élevé.
- Changement de la couleur du matériau, de l’objet le plus approché, en vert.
Pour créer le script :
Double-cliquez sur le dossier Scripts pour l’ouvrir.
Cliquez avec le bouton droit dans le dossier Scripts , puis cliquez sur Créer un>script C#. Nommez le script DataFromAnalytics.
Double-cliquez sur le script pour l’ouvrir avec Visual Studio.
Insérez les espaces de noms suivants :
using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Networking;
Dans le script, insérez les éléments suivants :
/// <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(); }
Dans la classe DataFromAnalytics , juste après la méthode Start(), ajoutez la méthode suivante appelée FetchAnalytics(). Cette méthode est chargée de remplir la liste des paires clé-valeur, avec un GameObject et un nombre d’événements d’espace réservé. Il initialise ensuite la coroutine GetWebRequest(). La structure de requête de l’appel à Application Insights se trouve également dans cette méthode, en tant que point de terminaison d’URL de requête .
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)); } }
Juste en dessous de la méthode FetchAnalytics(), ajoutez une méthode appelée GetWebRequest(), qui retourne un IEnumerator. Cette méthode est chargée de demander le nombre de fois où un événement correspondant à un GameObject spécifique a été appelé dans Application Insights. Lorsque toutes les requêtes envoyées ont été retournées, la méthode DetermineWinner() est appelée.
/// <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(); } } } }
La méthode suivante est DetermineWinner(), qui trie la liste des paires GameObject et Int en fonction du nombre d’événements le plus élevé. Il change ensuite la couleur matérielle de ce GameObject en vert (en tant que commentaires pour qu’il ait le nombre le plus élevé). Cela affiche un message avec les résultats de l’analyse.
/// <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); }
Ajoutez la structure de classe qui sera utilisée pour désérialiser l’objet JSON reçu d’Application Insights. Ajoutez ces classes tout en bas de votre fichier de classe DataFromAnalytics , en dehors de la définition 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; } }
Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.
Chapitre 10 - Créer la classe Movement
Le script Mouvement est le script suivant que vous devez créer. Il est responsable des opérations suivantes :
- Déplacement de la caméra principale en fonction de la direction vers laquelle la caméra regarde.
- Ajout de tous les autres scripts aux objets de scène.
Pour créer le script :
Double-cliquez sur le dossier Scripts pour l’ouvrir.
Cliquez avec le bouton droit dans le dossier Scripts , puis cliquez sur Créer un>script C#. Nommez le script Movement.
Double-cliquez sur le script pour l’ouvrir avec Visual Studio.
Remplacez le code existant par le code ci-dessous :
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() { } }
Dans la classe Movement , sous la méthode Update() vide, insérez les méthodes suivantes qui permettent à l’utilisateur d’utiliser le contrôleur de main pour se déplacer dans l’espace virtuel :
/// <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); }
Enfin, ajoutez l’appel de méthode dans la méthode Update().
// Update is called once per frame void Update() { UpdateControllerState(); }
Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.
Chapitre 11 - Configuration des références de scripts
Dans ce chapitre, vous devez placer le script Mouvement sur le parent de l’appareil photo et définir ses cibles de référence. Ce script gère ensuite le placement des autres scripts là où ils doivent se trouver.
À partir du dossier Scripts du volet projet, faites glisser le script Movement vers l’objet Camera Parent , situé dans le panneau Hiérarchie.
Cliquez sur le parent de l’appareil photo. Dans le panneau Hiérarchie, faites glisser l’objet Main droite du panneau de hiérarchie vers la cible de référence, Contrôleur, dans le panneau Inspecteur. Définissez vitesse utilisateur sur 5, comme illustré dans l’image ci-dessous.
Chapitre 12 - Générer le projet Unity
Tout ce dont vous avez besoin pour la section Unity de ce projet est maintenant terminé. Il est donc temps de le générer à partir d’Unity.
Accédez à Paramètres de build (Paramètres de build de fichiers>).
Dans la fenêtre Paramètres de build, cliquez sur Générer.
Une fenêtre Explorateur de fichiers s’affiche, vous invitant à indiquer l’emplacement de la build. Créez un dossier (en cliquant sur Nouveau dossier dans le coin supérieur gauche) et nommez-le BUILDS.
Ouvrez le nouveau dossier BUILDS , créez un autre dossier (à l’aide de Nouveau dossier ) et nommez-le MR_Azure_Application_Insights.
Avec le dossier MR_Azure_Application_Insights sélectionné, cliquez sur Sélectionner un dossier. La génération du projet prend environ une minute.
Après build, Explorateur de fichiers s’affiche pour vous indiquer l’emplacement de votre nouveau projet.
Chapitre 13 - Déployer MR_Azure_Application_Insights application sur votre ordinateur
Pour déployer l’application MR_Azure_Application_Insights sur votre ordinateur local :
Ouvrez le fichier solution de votre application MR_Azure_Application_Insights dans Visual Studio.
Dans Plateforme de solution, sélectionnez x86, Ordinateur local.
Dans configuration de la solution , sélectionnez Déboguer.
Accédez au menu Générer , puis cliquez sur Déployer la solution pour charger l’application sur votre ordinateur.
Votre application doit maintenant apparaître dans la liste des applications installées, prêtes à être lancées.
Lancez l’application de réalité mixte.
Se déplacer dans la scène, s’approcher des objets et les regarder, lorsque le service Azure Insight a collecté suffisamment de données d’événement, il définit l’objet qui a été approché le plus en vert.
Important
Alors que le temps d’attente moyen pour que les événements et les métriques soient collectés par le service prend environ 15 minutes, dans certains cas, cela peut prendre jusqu’à 1 heure.
Chapitre 14 - Portail application Insights Service
Une fois que vous avez parcouru la scène et regardé plusieurs objets, vous pouvez voir les données collectées dans le portail Application Insights Service .
Retour à votre portail Application Insights Service.
Sélectionnez Metrics Explorer.
Il s’ouvre dans un onglet contenant le graphique, qui représente les événements et les métriques liés à votre application. Comme mentionné ci-dessus, l’affichage des données dans le graphique peut prendre un certain temps (jusqu’à 1 heure)
Sélectionnez la barre Événements dans le Nombre total d’événements par version de l’application pour afficher une répartition détaillée des événements avec leurs noms.
Votre application Application Insights Service terminée
Félicitations, vous avez créé une application de réalité mixte qui tire parti du service Application Insights pour surveiller l’activité de l’utilisateur au sein de votre application.
Exercices bonus
Exercice 1
Essayez de générer, plutôt que de créer manuellement, les objets ObjectInScene et définissez leurs coordonnées sur le plan dans vos scripts. De cette façon, vous pouvez demander à Azure quel était l’objet le plus populaire (à partir du regard ou des résultats de proximité) et générer un de ces objets supplémentaire .
Exercice 2
Triez vos résultats Application Insights par heure, afin d’obtenir les données les plus pertinentes, et implémentez ces données sensibles au temps dans votre application.