HoloLens (1ère génération) et Azure 309 : Application Insights
Remarque
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 didacticiels lorsqu’ils sont publiés.
Dans ce cours, vous allez apprendre à ajouter des fonctionnalités Application Insights à une application de réalité mixte, à l’aide de l’API Azure Application Insights pour collecter des analyses concernant le comportement de l’utilisateur.
Application Insights est un service Microsoft, permettant 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 être tout ce qui se trouve entre les performances et les informations personnalisées que vous souhaitez collecter. Pour plus d’informations, consultez la page Application Insights.
Après avoir terminé ce cours, vous disposez d’une application de casque immersif de réalité mixte, qui sera en mesure d’effectuer les opérations suivantes :
- Permettre à l’utilisateur de regarder et de se déplacer autour d’une scène.
- Déclenchez l’envoi d’analyses au service Application Insights à l’aide du regard et de la proximité des objets en scène.
- L’application appelle également le service, en récupérant des informations sur l’objet qui a été abordé le plus par l’utilisateur, au cours des 24 dernières 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 basé sur Unity. Il vous sera possible d’appliquer ces concepts à une application personnalisée que vous créez peut-être.
Prise en charge des appareils
Cours | HoloLens | Casques immersifs |
---|---|---|
MR et Azure 309 : Application Insights | ✔️ | ✔️ |
Remarque
Bien que ce cours se concentre principalement sur les casques immersifs Windows Mixed Reality (VR), vous pouvez également appliquer ce que vous apprenez dans ce cours à Microsoft HoloLens. À 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 remarquerez peut-être un écho pendant la capture vocale.
Prérequis
Remarque
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 l’écriture (juillet 2018). Vous êtes libre d’utiliser le logiciel le plus récent, comme indiqué dans l’article d’installation des outils , bien qu’il ne soit pas supposé que les informations de ce cours correspondent parfaitement à ce que vous trouverez dans les logiciels plus récents que ceux répertoriés ci-dessous.
Nous vous recommandons le matériel et les 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 sdk Windows 10 le plus récent
- Unity 2017.4
- Visual Studio 2017
- Un casque immersif Windows Mixed Reality (VR) ou Microsoft HoloLens 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 la configuration d’Azure et la récupération des données Application Insights
Avant de commencer
Pour éviter les problèmes lors de la création de ce projet, il est fortement recommandé de créer le projet dans ce didacticiel dans un dossier racine ou proche de la racine (des chemins de dossier longs peuvent provoquer des problèmes au moment de la génération).
Avertissement
Sachez que les données qui vont à Application Insights prennent du temps, donc soyez patient. Si vous souhaitez vérifier si le service a reçu vos données, consultez le chapitre 14, qui vous montre 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.
Remarque
Si vous n’avez pas encore de compte Azure, vous devez en créer un. Si vous suivez ce tutoriel dans une situation de salle de classe ou de laboratoire, demandez à votre instructeur ou à l’un des proctoreurs de vous aider à configurer votre nouveau compte.
Une fois connecté, cliquez sur Nouveau dans le coin supérieur gauche, puis recherchez Application Insights, puis cliquez sur Entrée.
Remarque
Le mot Nouveau peut avoir été remplacé par Créer une ressource, dans des 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 votre nom souhaité pour cette instance de service.
En tant que 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 d’une collection de ressources Azure. Il est recommandé de conserver tous les services Azure associés à un seul projet (par exemple, tels que ces cours) sous un groupe de ressources commun.
Si vous souhaitez en savoir plus sur les groupes de ressources Azure, consultez l’article du groupe de ressources.
Sélectionnez un emplacement.
Vous devez également confirmer que vous avez compris les conditions générales appliquées à ce service.
Sélectionnez Créer.
Une fois que vous avez cliqué sur Créer, vous devez attendre que le service soit créé, cela peut prendre une minute.
Une notification s’affiche dans le portail une fois l’instance de service créée.
Sélectionnez les notifications pour explorer votre nouvelle instance de service.
Cliquez sur le bouton Accéder à la ressource dans la notification pour explorer votre nouvelle instance de service. Vous accédez à votre nouvelle instance application Insights Service .
Remarque
Gardez cette page web ouverte et facile à accéder, vous revenez souvent ici pour voir les données collectées.
Important
Pour implémenter Application Insights, vous devez utiliser trois valeurs spécifiques (3) : 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 du Bloc-notes vierge, car vous les utiliserez bientôt dans votre code.
Pour trouver 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 plus bas , vous trouverez l’accès à l’API, que vous devez cliquer. Le panneau à droite fournit l’ID d’application de votre application.
Lorsque le panneau ID d’application est 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, tapez une description et cochez les trois cases.
Cliquez sur Générer la clé. Votre clé API est créée et affichée.
Avertissement
Il s’agit de la seule fois que votre clé de service s’affiche. Veillez donc à effectuer une copie de celle-ci maintenant.
Chapitre 2 - Configurer le projet Unity
Voici une configuration classique pour le développement avec la réalité mixte et, par conséquent, 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 que les répertoires racines sont plus proches). Cliquez ensuite sur Créer un projet.
Avec Unity ouvert, il vaut la peine 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 à partir de la nouvelle fenêtre, accédez à Outils externes.> Remplacez l’é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 vers plateforme Windows universelle, en cliquant sur le bouton Basculer la plateforme.
Accédez aux paramètres de génération de fichiers > et assurez-vous que :
L’appareil cible est défini sur n’importe quel appareil
Pour Microsoft HoloLens, définissez l’appareil cible sur HoloLens.
Le type de build est défini sur D3D
Le KIT SDK est défini sur La dernière version installée
La génération et l’exécution sont définies 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 nouvelle scène, puis cliquez sur le bouton Nouveau dossier pour créer un dossier, nommez-le Scènes.
Ouvrez votre dossier Scènes nouvellement créé, puis dans le champ Fichier : champ de texte, tapez ApplicationInsightsScene, puis cliquez sur Enregistrer.
Les paramètres restants, dans Paramètres de build, doivent être laissés comme valeurs par défaut pour l’instant.
Dans la fenêtre Paramètres de build, sélectionnez Paramètres du lecteur, ce qui ouvre 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 un redémarrage de l’éditeur.
Le serveur principal de script doit être .NET
Le niveau de compatibilité des API doit être .NET 4.6
Sous l’onglet Paramètres de publication, sous Fonctionnalités, vérifiez :
InternetClient
Plus loin dans le panneau, dans les paramètres XR (trouvés ci-dessous paramètres de publication), cochez Virtual Reality Pris en charge, vérifiez que le Kit de développement logiciel (SDK ) Windows Mixed Reality est ajouté.
De retour dans les paramètres de build, les projets C# Unity ne sont plus grisés ; cochez la case en regard de cela.
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, importez-le dans votre projet en tant que package personnalisé. Cela contiendra également les DLL du chapitre suivant. Après l’importation, continuez à partir du chapitre 6.
Important
Pour utiliser Application Insights dans Unity, vous devez importer la DLL pour celle-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 », contenant 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 package > de ressources>.
Dans la zone Importer un package Unity qui s’affiche, vérifiez que tous les plug-ins sous (et y compris) 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
Avec ce plug-in sélectionné, vérifiez que n’importe quelle plateforme est désactivée, puis vérifiez que WSAPlayer est également désactivé, puis cliquez sur Appliquer. Pour vérifier que les fichiers sont correctement configurés, procédez comme suit.
Remarque
Le marquage des plug-ins comme celui-ci 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 sera utilisé après l’exportation du projet à partir d’Unity.
Ensuite, vous devez ouvrir le dossier WSA dans le dossier Insights . Vous verrez une copie du même fichier que celui que vous avez configuré. Sélectionnez ce fichier, puis, dans l’inspecteur, vérifiez que toute plateforme n’est pas cochée, puis vérifiez que seul WSAPlayer est activé. 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 connaître l’apparence du 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 dans le 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 l’objet 3D, puis sur Sphere.
Renommez la sphère en main droite.
Définir l’échelle de transformation de la main droite sur 0.1, 0.1, 0.1
Supprimez le composant Collider Sphere de la main droite en cliquant sur l’engrenage dans le composant Collisionder Sphere, puis en supprimant le composant.
Dans le panneau Hiérarchie, faites glisser l’appareil photo principal et les objets main droite sur l’objet Parent de l’appareil photo.
Définissez la position de transformation de l’appareil photo principal 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 dans le panneau Hiérarchie, puis sur l’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 projet, cliquez avec le bouton droit, puis créez un dossier, puis créez un dossier. Nommez-le Matériaux.
Ouvrez le dossier Matériaux, puis cliquez avec le bouton droit, cliquez sur Créer, puis Matériau pour créer un matériau. Nommez-le Bleu.
Avec le nouveau matériau Bleu sélectionné, examinez l’Inspecteur, puis cliquez sur la fenêtre rectangulaire en même temps que Albedo. Sélectionnez une couleur bleue (l’image ci-dessous est Hex Color : #3592FFFF). Cliquez sur le bouton fermer une fois que vous avez choisi.
Faites glisser votre nouveau matériau à partir du dossier Matériaux, sur votre plan nouvellement créé, dans votre scène (ou déposez-le sur l’objet Plan dans la hiérarchie).
Cliquez avec le bouton droit dans une zone vide dans le panneau Hiérarchie, puis sur l’objet 3D, Capsule.
- Avec la Capsule sélectionnée, changez sa position de transformation en : -10, 1, 0.
Cliquez avec le bouton droit dans une zone vide dans le panneau Hiérarchie, puis sur l’objet 3D, cube.
- Une fois le cube sélectionné, remplacez sa position de transformation par : 0, 0, 10.
Cliquez avec le bouton droit dans une zone vide dans le panneau Hiérarchie, puis sur l’objet 3D, Sphere.
- Une fois la sphère sélectionnée, remplacez sa position de transformation par : 10, 0, 0.
Remarque
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 d’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 de la scène pour y parvenir, ils doivent être étiquetés. Sélectionnez l’un des objets, puis, dans le panneau Inspecteur, cliquez sur Ajouter une balise..., ce qui permutera l’inspecteur avec le panneau Étiquettes et couches.
Cliquez sur le symbole + (plus), puis tapez le nom de la balise en tant qu’ObjectInScene.
Avertissement
Si vous utilisez un autre nom pour votre balise, vous devez vous assurer que cette modification est également apportée aux données DataFromAnalytics, ObjectTrigger et Gaze, aux scripts ultérieurement, afin que vos objets soient trouvés et détectés dans votre scène.
Avec la balise créée, vous devez maintenant l’appliquer aux trois de vos objets. Dans la hiérarchie, maintenez la touche Maj enfoncée, cliquez sur la capsule, le cube et la sphère, les objets, puis, dans l’inspecteur, cliquez sur le menu déroulant en même temps que balise, puis cliquez 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 éléments suivants :
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 à l’instance du service Application Insights.
Pour créer cette classe :
Cliquez avec le bouton droit dans le volet projet, puis créez 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 indiqué ci-dessous :
using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; using UnityEngine;
Dans 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;
Remarque
Définissez correctement les valeurs instrumentationKey, applicationId et API_Key , à l’aide des clés de service à partir du portail Azure, comme indiqué dans le chapitre 1, étape 9.
Ajoutez ensuite les méthodes Start() et Awake(), qui seront appelées lorsque la classe initialise :
/// <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é à partir de la caméra principale pour détecter l’objet que l’utilisateur examine. Dans ce cas, raycast doit identifier si l’utilisateur examine un objet avec la balise ObjectInScene, puis compte combien de temps l’utilisateur regarde cet objet.
Double-cliquez sur le dossier Scripts pour l’ouvrir.
Cliquez avec le bouton droit dans le dossier Scripts, cliquez sur Créer un>script C#. Nommez le regard du script.
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 l’accès cible :
/// <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 examiné 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 éléments suivants :
- Ajout de composants nécessaires pour la collision avec la caméra principale.
- Détection si la caméra est proche 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, 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 éléments suivants :
- Extraction de données analytiques sur l’objet qui a été approché par la caméra le plus.
- À l’aide des clés de service, qui autorisent la communication avec votre instance Azure Application Insights Service.
- Tri des objets dans la scène, selon lequel le nombre d’événements est le plus élevé.
- Modification 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, 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 numéro d’événement 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 sont 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 modifie ensuite la couleur matérielle de ce GameObject en vert (comme commentaires pour qu’il ait le nombre le plus élevé). Cela affiche un message avec les résultats d’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 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 Mouvement
Le script Mouvement est le script suivant que vous devez créer. Il est responsable de ce qui suit :
- Déplacer 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, cliquez sur Créer un>script C#. Nommez le mouvement du script.
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 caméra et définir ses cibles de référence. Ce script gère ensuite le placement des autres scripts où ils doivent être.
Dans le dossier Scripts du panneau projet, faites glisser le script Mouvement vers l’objet Camera Parent , situé dans le panneau Hiérarchie.
Cliquez sur le parent de l’appareil photo. Dans le panneau Hierarchy, faites glisser l’objet Main droite du panneau Hierarchy vers la cible de référence, contrôleur, dans le panneau Inspecteur. Définissez la vitesse utilisateur sur 5, comme illustré dans l’image ci-dessous.
Chapitre 12 - Générer le projet Unity
Tout ce qui est nécessaire pour la section Unity de ce projet a été terminé. Il est donc temps de le générer à partir d’Unity.
Accédez aux paramètres de build( Paramètres de build de fichier>).
Dans la fenêtre Paramètres de build, cliquez sur Générer.
Une fenêtre Explorateur de fichiers s’affiche, vous invitant à entrer un emplacement pour 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 et créez un autre dossier (à l’aide du nouveau dossier une fois de plus) et nommez-le MR_Azure_Application_Insights.
Une fois le dossier MR_Azure_Application_Insights sélectionné, cliquez sur Sélectionner un dossier. Le projet prend une minute ou plus pour générer.
Après la génération, Explorateur de fichiers s’affiche en affichant 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 la plateforme de solutions, sélectionnez x86, Ordinateur local.
Dans la 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.
Parcourez la scène, approchez les objets et examinez-les, lorsque le service Azure Insight a collecté suffisamment de données d’événement, il définit l’objet qui a été abordé le plus en vert.
Important
Si le temps d’attente moyen pour les événements et les métriques à collecter par le service prend environ 15 minutes, il peut prendre jusqu’à 1 heure.
Chapitre 14 - Portail du service Application Insights
Une fois que vous avez parcouru la scène et examiné plusieurs objets, vous pouvez voir les données collectées dans le portail Application Insights Service .
Revenez à 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 associés à votre application. Comme mentionné ci-dessus, il peut prendre un certain temps (jusqu’à 1 heure) pour que les données soient affichées dans le graphique
Sélectionnez la barre Événements dans le total des événements par version de l’application pour afficher une répartition détaillée des événements avec leurs noms.
Votre application 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 au sein de vos scripts. De cette façon, vous pouvez demander à Azure ce que l’objet le plus populaire était (à partir du regard ou des résultats de proximité) et générer un de ces objets supplémentaires .
Exercice 2
Triez vos résultats Application Insights par heure, afin que vous obteniez les données les plus pertinentes et implémentez ces données sensibles au temps dans votre application.