IMA5 2018/2019 P26 : Différence entre versions
(→Semaine 15-16) |
(→Documents Rendus) |
||
(10 révisions intermédiaires par le même utilisateur non affichées) | |||
Ligne 27 : | Ligne 27 : | ||
==Liste des tâches à effectuer== | ==Liste des tâches à effectuer== | ||
==Calendrier prévisionnel== | ==Calendrier prévisionnel== | ||
+ | |||
+ | [[Fichier::My_GANTT_PRE_.png]] | ||
=Réalisation du Projet= | =Réalisation du Projet= | ||
Ligne 498 : | Ligne 500 : | ||
Pendant des 2 semaines, j'ai intégré le programme de contrôle de Kinect à le programme de l'émulateur pour contrôler le jeux vidéo par les mains dans l'air. | Pendant des 2 semaines, j'ai intégré le programme de contrôle de Kinect à le programme de l'émulateur pour contrôler le jeux vidéo par les mains dans l'air. | ||
− | Premièrement, j'ai | + | Premièrement, j'ai récupéré le frame de Kinect et les positions des mains. Après, j'ai laissé la taille de la fenêtre et le frame de Kinect être mêmes. Après, j'ai utilisé les fonctions de dessiner de C# à dessiner un cercle dans la position de la main pour présenter la position de la main. |
− | Après, j'ai écrit | + | Après, j'ai écrit le programme pour dessiner des buttons dans la fenêtre de l'émulateur pour simuler le joystick. Chaque grand cercle représente un buton. Il y a 8 buttons qui représente UP, DOWN, LEFT, RIGHT, START, SELECT, A et B. |
− | [[Fichier: | + | |
+ | [[Fichier:VirtualButton.png|700px|center]] | ||
Après, j'ai écrit un programme pour réaliser mon algorithme à détecter si les butons sont touché. | Après, j'ai écrit un programme pour réaliser mon algorithme à détecter si les butons sont touché. | ||
Ligne 510 : | Ligne 513 : | ||
J'ai créé un array qui représente le statue de button (soit appuyé, soit non appuyé) .Chaque fois que la frame d'émulateur renouvelle, le programme va juger si la center de cercle de main est dans un cercle de button, le programme va regarder le statue du button, si la statue est 'non appuyé', il va mettre la statue à 'appuyé' et envoyer l'instruction de 'appuyer' à l'émulateur. Si la statue d'un button est 'appuyé', mais la centre des mains ne sont pas dans la cercle de button, le programme va mettre la statue du button à 'non appuyé' et envoyer l'instruction de 'soulever' à l'émulateur. | J'ai créé un array qui représente le statue de button (soit appuyé, soit non appuyé) .Chaque fois que la frame d'émulateur renouvelle, le programme va juger si la center de cercle de main est dans un cercle de button, le programme va regarder le statue du button, si la statue est 'non appuyé', il va mettre la statue à 'appuyé' et envoyer l'instruction de 'appuyer' à l'émulateur. Si la statue d'un button est 'appuyé', mais la centre des mains ne sont pas dans la cercle de button, le programme va mettre la statue du button à 'non appuyé' et envoyer l'instruction de 'soulever' à l'émulateur. | ||
− | [[ | + | unsafe void Draw(byte[] screen) |
+ | { | ||
+ | BitmapData _frameData = _frame.LockBits(new Rectangle(0, 0, 256, 240), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); | ||
+ | byte* ptr = (byte*)_frameData.Scan0; | ||
+ | for (int i = 0; i < 256 * 240; i++) | ||
+ | { | ||
+ | ptr[i] = screen[i]; | ||
+ | } | ||
+ | _frame.UnlockBits(_frameData); | ||
+ | g.DrawImage(_frame, 0, 0, Size.Width, Size.Height); | ||
+ | drawButtonVirtual(); | ||
+ | drawHandVirtuel(); | ||
+ | VirtualHandControl(); | ||
+ | if(client != null) client.SendBitmap(_frame); | ||
+ | } | ||
+ | void VirtualHandControl() | ||
+ | { | ||
+ | Controller.Button rightTouched = this.rightHand.TouchButton(this.buttonsVirtual); | ||
+ | Controller.Button leftTouched = this.leftHand.TouchButton(this.buttonsVirtual); | ||
+ | if(keyStates.A == false) | ||
+ | { | ||
+ | if(rightTouched == Controller.Button.A || leftTouched == Controller.Button.A) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.A, true); | ||
+ | keyStates.A = true; | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(rightTouched != Controller.Button.A && leftTouched != Controller.Button.A) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.A, false); | ||
+ | keyStates.A = false; | ||
+ | } | ||
+ | } | ||
+ | if(keyStates.B == false) | ||
+ | { | ||
+ | if(rightTouched == Controller.Button.B || leftTouched == Controller.Button.B) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.B, true); | ||
+ | keyStates.B = true; | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(rightTouched != Controller.Button.B && leftTouched != Controller.Button.B) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.B, false); | ||
+ | keyStates.B = false; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if(keyStates.Start == false) | ||
+ | { | ||
+ | if(rightTouched == Controller.Button.Start || leftTouched == Controller.Button.Start) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.Start, true); | ||
+ | keyStates.Start = true; | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(rightTouched != Controller.Button.Start && leftTouched != Controller.Button.Start) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.Start, false); | ||
+ | keyStates.Start = false; | ||
+ | } | ||
+ | } | ||
+ | if(keyStates.Selete == false) | ||
+ | { | ||
+ | if(rightTouched == Controller.Button.Select || leftTouched == Controller.Button.Select) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.Select, true); | ||
+ | keyStates.Selete = true; | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(rightTouched != Controller.Button.Select && leftTouched != Controller.Button.Select) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.Select, false); | ||
+ | keyStates.Selete = false; | ||
+ | } | ||
+ | } | ||
+ | if(keyStates.Up == false) | ||
+ | { | ||
+ | if(rightTouched == Controller.Button.Up || leftTouched == Controller.Button.Up) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.Up, true); | ||
+ | keyStates.Up = true; | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(rightTouched != Controller.Button.Up && leftTouched != Controller.Button.Up) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.Up, false); | ||
+ | keyStates.Up = false; | ||
+ | } | ||
+ | } | ||
+ | if(keyStates.Down == false) | ||
+ | { | ||
+ | if(rightTouched == Controller.Button.Down || leftTouched == Controller.Button.Down) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.Down, true); | ||
+ | keyStates.Down = true; | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(rightTouched != Controller.Button.Down && leftTouched != Controller.Button.Down) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.Down, false); | ||
+ | keyStates.Down = false; | ||
+ | } | ||
+ | } | ||
+ | if(keyStates.Right == false) | ||
+ | { | ||
+ | if(rightTouched == Controller.Button.Right || leftTouched == Controller.Button.Right) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.Right, true); | ||
+ | keyStates.Right = true; | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(rightTouched != Controller.Button.Right && leftTouched != Controller.Button.Right) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.Right, false); | ||
+ | keyStates.Right = false; | ||
+ | } | ||
+ | } | ||
+ | if(keyStates.Left == false) | ||
+ | { | ||
+ | if(rightTouched == Controller.Button.Left || leftTouched == Controller.Button.Left) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.Left, true); | ||
+ | keyStates.Left = true; | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(rightTouched != Controller.Button.Left && leftTouched != Controller.Button.Left) | ||
+ | { | ||
+ | _console.Controller.setButtonState(Controller.Button.Left, false); | ||
+ | keyStates.Left = false; | ||
+ | } | ||
+ | } | ||
+ | |||
A la fin, j'ai fait un test, le vidéo est suivant: | A la fin, j'ai fait un test, le vidéo est suivant: | ||
Ligne 518 : | Ligne 669 : | ||
<center><include iframe width="560" height="315" src="https://www.youtube.com/embed/n_O37m0drcI" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen/></center> | <center><include iframe width="560" height="315" src="https://www.youtube.com/embed/n_O37m0drcI" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen/></center> | ||
+ | |||
+ | =Poursuite du Projet= | ||
+ | |||
+ | Maintenant, j’ai réalisé à afficher l’image de jeux vidéo à FOVE, et permettre le joueur à modifier la dimension d’image et contrôler le jeux vidéo, et j’ai réalisé à utiliser le Kinect à ajouter une manière d’interaction d'un système simulant le "touch" à partir des mouvements des mains de l'utilisateur. | ||
+ | Mais, il y a deux problèmes : | ||
+ | |||
+ | 1. Quand les programmations lance, le renouvellement d’image est très lent. Maintenant, je pense que le problème est que la FOVE utilise beaucoup de ressource de CPU(85%), donc l’émulateur et l’intermédiaire n’ont pas assez de ressource de CPU, donc le renouvellement est lent. C’est important à résoudre le problème | ||
+ | |||
+ | 2. La détection des mains n’est pas stable. Parfois, le kinect ne peut pas détecter les positions des mains. Donc, c’est nécessaire à créer un algorithme à améliorer la stabilité de la détection des mains. | ||
=Documents Rendus= | =Documents Rendus= | ||
[[Fichier:Ji_YANG_PRE_Intermediaire.pdf]] | [[Fichier:Ji_YANG_PRE_Intermediaire.pdf]] | ||
+ | |||
[[Fichier:Presentation_Ji.pdf]] | [[Fichier:Presentation_Ji.pdf]] | ||
+ | |||
+ | [[Fichier:Presentation_final.pdf]] | ||
+ | |||
+ | Les codes sont tous dans le github: https://github.com/guigui00700/Interaction2DinVR/ |
Version actuelle datée du 27 février 2019 à 08:36
Sommaire
Présentation générale
Un certain nombre de logiciels permettent à l'heure actuelle de redonner vie à de vieux logiciels de jeux (jeux devenus libres de droit car délaissés par leurs propriétaires, ces jeux sont dits "abandonware"). D'autres jeux existent, totalement libres de droits dès leur diffusion. Dans le cadre d'un projet européen auquel l'équipe de recherche MINT participe, nous souhaitons mettre en place un prototype permettant à une structure de rééducation spécialisé dans la rééducation de l'enfant cérébrolésé de disposer d'un système de réalité virtuelle via lequel l'enfant peut jouer à l'un ou l'autre de ces vieux jeux. Le fait de réaliser cet objectif en réalité virtuelle plutôt que via un dispositif d'interaction standard est le suivant : la réalité virtuelle permettra ici de se libérer des contraintes matérielles, permettra d'adapter l'écran virtuel à la réalité de l'enfant, pourquoi pas par la suite de mettre en place des distracteurs ou d'augmenter la difficulté de l'interaction, etc... et ce afin de permettre aux soignants de disposer d'une application qui leur permette de graduer la difficulté d'interaction (permettant ainsi à l'enfant d'entrainer ses capacités motrices et cognitives), tout en disposant d'une base de jeux importantes et donc la dimension ludique n'est plus à prouver. Le travail consistera tout d'abord en un examen des différents émulateurs existants, et en la proposition d'une configuration permettant de réaliser l'interaction d'un affichage 2D dans un environnement type HTC Vive. On pourra par exemple utiliser une fonctionnalité du type VNC et sa compatibilité avec l'environnement Unity. On travaillera ensuite à l'intégration d'un système simulant le "touch" à partir des mouvements des mains de l'utilisateur. En fonction de l'avancement, on pourra explorer quelques configurations d'interaction mettant en valeur plusieurs configurations virtuelles différentes, permettant par la suite d'explorer les modalités d'interaction (ce dernier point pourra se faire en collaboration avec une structure médicale basée à Meerbush, près de Düsseldorf en Allemagne).
Objectifs
Mettre en place un dispositif de réalité virtuelle permettant à l'utilisateur d'interagir avec une application 2D classique, en simulant une interaction tactile.
Préparation du projet
Cahier des charges
Choix techniques : matériel et logiciel
Unity
Fceux
Fove/HTV Vive
Kinect
Visual Studio
Les ordinateurs
Liste des tâches à effectuer
Calendrier prévisionnel
Réalisation du Projet
Semaine 1-4
Pendant des semaines, j'ai fait des choses suivantes :
1. Tester des émulateur du jeux vidéo
2. Apprendre VNC et tester le VNC
3. Apprendre à utiliser Unity
4. Apprendre C#
5. Intégration du VNC à Unity
6. Réalisation d'une solution pour afficher l'image du jeux vidéo à Unity par TCP/IP
Details:
1. Tester des émulateur du jeux vidéo:
J'ai cherché les émulateur du jeux vidéo et j'ai trouvé qu'il y a deux grandes groupes des émulateur qui s'appliquent à mon projet: les émulateur d'Android et les émulateur de "Nintendo entertainment system"(NES, après je vais utiliser NES pour s'appeler). Ils sont convenables, parce qu'il y a beaucoup de jeux vidéo qui sont implémenté aux Android et NES, à la même temps, il est facile à trouver les deux genre d'émulateurs gratuits, par exemple :
Pour l'Android:
BlueStacks[1]
Il est gratuit et il marche en Windows et MacOS.
Nox[2]
Koplayer[3]
Pour NES:
FCEUX[4]
Il est gratuit et "Open source". Il supporte à utiliser le script de LUA à le contrôler.
JNES[5]
NEStopia[6]
Les autres NES émulateur de "open source"
Problème::
1). Comment on réalise l'interaction du jeux video par les dispositif de réalité virtuelle?
Solution 1: On fait une configuration pour intégrer le VNC à l'Unity, et il y a les APIs de Unity qui peut faire l'interaction avec le dispositif de réalité virtuelle.
Solution 2: On programme à transfère l'image du jeux vidéo à Unity pour l'afficher au dispositif de réalité virtuelle par TCP/IP et transfère par TCP/IP les consignes du dispositif de réalité virtuelle au jeux vidéo pour le contrôler.
2). Maintenant, je ne suis pas claire que si on peut utiliser les émulateurs et des jeux à mon projet légalement.*
2. Apprendre VNC et tester le VNC
Description du VNC[7]:
VNC (Virtual Network Computing, littéralement « informatique virtuelle en réseau ») est un système de visualisation et de contrôle de l'environnement de bureau d'un ordinateur distant. Il permet au logiciel client VNC de transmettre les informations de saisie du clavier et de la souris à l'ordinateur distant, possédant un logiciel serveur VNC à travers un réseau informatique. Il utilise le protocole RFB pour les communications.
VNC est indépendant du système d'exploitation : un client VNC installé sur n'importe quel système d'exploitation peut se connecter à un serveur VNC installé sur un système d'exploitation différent ou identique. Il existe des clients et des serveurs VNC pour la plupart des systèmes d'exploitation. Plusieurs clients peuvent se connecter en même temps à un unique serveur VNC.
Parmi les utilisations de ce protocole, on peut citer le support technique à distance, l'administration et la maintenance de systèmes ou logiciels ne permettant que des contrôles graphiques et demandant l'utilisation de la souris ou bien encore la visualisation distante d'applications diverses et variées, dans un but éducatif par exemple.
Teste:
J'ai installé le VNC Viewer[8] et VNC Server[9] est fait un test. (*VNC Viewer est gratuit, mais VNC Server n'est pas gratuit).
Premièrement, j'ai lancé un VNC Server
Après, j'ai lancé un VNC Viewer et connecté à mon serveur de VNC, j pouvais afficher l'écran du serveur et le contrôler.
3. Apprendre à utiliser Unity
La Description du Unity:[10]
Unity est un moteur de jeu multi-plateforme (smartphone, Mac, PC, consoles de jeux vidéo et web) développé par Unity Technologies. Il est l'un des plus répandus dans l'industrie du jeu vidéo, du fait de sa rapidité aux prototypages pour les très gros studios, aussi pour la sphère du jeu indépendant qui développe directement dessus pour sortir leurs applications sur tout support[à recycler].
Il a la particularité de proposer une licence gratuite dite « Personal » sans limitation au niveau du moteur.
Pour utiliser l'Unity, on dois savoir l'utilisation de deux choses: les objets et les scripts.
Les objets de l'Unity sont les éléments du jeux vidéo, par exemple, les images, les cubes, la lumière etc.
Les scripts de l'Unity sont les programme qui contrôlent les objets, les scripts sont C# et javascript.
4. Apprendre C#:
C# ressemble à Java. Il peut fonctionner à multi-plateforme. Il y a des bibliothèques sur le réseau d'internet et l'interactions. Maintenant, je peux utiliser à programmer.
5. Intégration du VNC à Unity
Au début, j'ai cherché la solution à intégrer du VNC à Unity, mais, je ne l'ai trouvé pas. on peut faire l'interaction entre deux ordinateurs à distance par VNC, mais, on ne peux implémenter VNC à Unity qui est important, parce qu'on dois utiliser Unity à connecter les dispositifs de la réalité virtuelle.
Après, je veux coder une programmation à simuler la fonction d'Unity. Et j'ai trouvé quelques bibliothèques outils:
1) VNsharp[11]
VNsharp est une 'open source' bibliothèques gratuites de C# pour la programmation de VNC. Je l'ai essayé, mais ça n'a fonctionné pas bien, parce, la bibliothèque dépende en le bibliothèque native du Windows (J'utilise Macbook) et si on l'utilise à Unity, on dois changer beaucoup de chose. Parce que l'environnement de Unity est Mono[12], qui n'est pas tout même que l'environnement Windows. Donc, je n'ai choisi pas à utiliser.
2) Unity-VNC-Client[13]
Unity-VNC-Client est une bibliothèque à Github. Dans la description, il dit que ça a fonctionné à Windows, mais, dans son Readme, je n'ai trouvé pas de façon à l'utiliser, donc je ne pouvais pas le tester.
3) Unity-VNC-Client(2) [14]
Dans la bibliothèque, il n'y avait pas aussi de façon d'utilisation.
6. Réalisation d'une solution pour afficher l'image du jeux vidéo à Unity par TCP/IP:
Il n'existe pas de bonne solution à intégrer le VNC à Unity, mais, j'ai pensé que je pouvais coder une programmation à stimuler la fonction de VNC moi-mème avec TCP/IP et C# et l'intégrer à Unity.
Mon idée est suivante:
Maintenant, j'ai fini à afficher l'image du jeux vidéo à Unity.
Les détails sont suivants:
Dans l'ordinateur A:
1) Entrer à Unity et Créer un nouveau projet, dans "Main Camera", créer un "Canvas", dans "Canvas", Créer un "Image".
2) Créer un script de C# dans "assets", et coder la programmation suivante: Fichier:GetRemoteImage.txt
3) Mettre l'image à "Canvas"
4) Copier "System.Drawing.dll" au dossier du projet. (System.Drawing.dll est dans "/Applications/Unity/Unity.app/Contents/Mono/lib/mono/2.0", si vous utiliser macbook. Si vous utiliser Linux ou Windows, il est aussi dans le dossier de Unity)
5) Ajouter les configurations suivantes dans le fichier "config"(config est dans "/Applications/Unity/Unity.app/Contents/Mono/etc/mono/", si vous utiliser macbook, si vous utiliser Linux ou Windows, il est aussi dans le dossier de Unity) <dllmap dll="gdiplus" target="/Library/Frameworks/Mono.framework/Versions/"Version of Unity"/lib/libgdiplus.dylib" /> <dllmap dll="gdiplus.dll" target="/Library/Frameworks/Mono.framework/Versions/"Version of Unity"/lib/libgdiplus.dylib" />
Dans l'odinateur B (Windows):
1). Installer FCEUX, et Télécharger le ROM de "Super Mario"[15]
2). Créer la programmation suivants:
3). Lancer "Super Mario" et la programmtion, taper le IP de l'ordinateur A et le port 50002
Dans l'ordinateur A:
6) Lancer la unity
Un vidéo de présentation est suivante:
Semaine 5-8
Pendant des mois, j'ai fini des choses suivantes:
1. J'ai intégrer mon programmation à Unity et la casque virtuelle, c'est à dire que l'image du jeux vidéo peux être affichée en casque virtuelle.
2. J'ai écrit une programmation qui nous permet de faire interaction entre la casque virtuelle et le souris, le clavier.
3. Avant, j'ai ajouté des trucs pour que tous les programations peux être lancé à la même oridiateur(pas besion de machine virtuelle).
4. J'ai changé l'émulator du jeux vidéo.
5. J'ai combiné tous les parties.
6. J'ai testé ma programmation avec la casque virtuelle, et il étais bien.
Après, je vais présenter des choses suivant:
1. Obtenir l'image du jeux vidéo avec 'handle'.
2. Envoyer le message de clavier et souris au jeux vidéo pour le contrôler.
3. Afficher l'image de vidéo à la casque virtuelle.
4. J'ai changé l'émulator du jeux vidéo.
5. J'ai combiné tous les parties.
6. Tester mes programmtion en réalité avec la casque virtuelle.
Détails:
1. Obtenir l'image du jeux vidéo:
Selon le travail précédent, je dois utiliser deux ordinateurs à lancer tous les programmations, un ordiateur obtien l'image de l'écran où le jeux vidéo est, l'autre ordinateur reçois l'image et l'achiffer à Unity. En fait, J'ai utilisé une machine virtuelle et ma machine physical à lancer tous programmation, mais, la machine virtuelle coute trop de resources de CPU et RAM, donc, je veux lancer tous les programmation à une machine.
Pour réaliser mon but, je dois avoir une manière à obtenir l'image de fenêtre du jeux vidéo. J'ai fait la recherche sur Internet, et j'ai trouvé que dans le système de Windows, il y a une chose qui s'appelle 'handle'[16] qui peut m'aider à récupérer l'image de la fenêtre du jeux vidéo.
Pour utiliser 'handle' en programmation C#, nous devons importer des programmathèque de 'Windows':
[DllImport("user32.dll", EntryPoint = "FindWindow")] public static extern IntPtr FindWindow(string IpClassName, string IpWindowName);
[DllImport("user32.dll")] public static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
Après, j'ai obtenu la 'handle' du jeux vidéo avec la function 'FindWindow'.
hwnd = WinIo.FindWindow(null,<--nom de fenêtre-->);
Ensuit, j'ai écrit une function pour obtenu l'image avec la 'handle' de la fenêtre.
RECT rect = new RECT(); GetWindowRect(hwnd, ref rect); int width = rect.Right - rect.Left; //窗口的宽度 int height = rect.Bottom - rect.Top; //窗口的高度 IntPtr hBitmap = CreateCompatibleBitmap(hdcFrom, width, height); bmp = System.Drawing.Image.FromHbitmap(hBitmap); return bmp;
A la fin, j'ai tester ma programmation. J'ai enregister les images de jeux vidéo à une dossier. Il a fonctionné bien.
2. Envoyer le message de clavier et souris au jeux vidéo pour le contrôler.
C'est pas facile à contrôler le jeux vidéo, parce que je n'ai eu pas la source de l'émulateur, et il n'y a pas d'interface avec qui je peux le contrôler. Mais, j'ai trouvé que je pouvais contrôler le jeux vidéo avec la 'handle' de Windows. Avec la 'handle', je peux envoyé des messages qui include les consignes du clavier à l'émulateur.
Premièrement, j'ai importer des programmathèque de 'Windows:
[DllImport("User32.dll", EntryPoint = "SendMessageA")] private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
Après, j'ai créé une nouveau type de data, VKkey, qui représente le code de clavier et souris:
public enum VKKey { // mouse movements move = 0x0001, leftdown = 0x0002, leftup = 0x0004, rightdown = 0x0008, rightup = 0x0010, middledown = 0x0020, //keyboard stuff VK_LBUTTON = 1, VK_RBUTTON = 2, VK_CANCEL = 3, VK_MBUTTON = 4, VK_BACK = 8, VK_TAB = 9, VK_CLEAR = 12, VK_RETURN = 13, VK_SHIFT = 16, VK_CONTROL = 17, VK_MENU = 18, VK_PAUSE = 19, VK_CAPITAL = 20, .... }
Les valeurs des variable sont corespondants les code de tous les clés du clavier, et corespondants à la parametres 'wParam' de la fonction 'SendMessage'.
Après, j'ai écrit une programmation à tester la fonction.
J'ai créé une fenêtre dans laquelle il y a un button, quand je clique le button, il va envoyer un message qui contient le code de clavier de '1' à l'application de 'calcultrice'.
Après, quand j'ai cliqué le button, j'ai vu que dans le calcultrice, il a affiché enncore '1', comme j'appuie sur la clé du clavier.
Donc, la fonction a marché bien.
3. Afficher l'image de vidéo à la casque virtuelle.
Pendant première 2 semaines, j'ai utilisé l'objet d'Unity, 'Image' , à afficher l'image du jeux vidéo. Mais, maintenant, j'ai remplacé 'Image' à l'autre Objet de 3D 'Quad'. Parce que Dans le casque virtuelle, les images dans l'objet 'Image' ne pouvent pas être affiché. Parce que l'objet 'Image' est d'un objet de UI qui ne peux pas être affiché dans la casque virtuelle. Dans le casque virtuelle, il n'a que l'objet de 3d qui peux être affiché. L'objet 'Quad' est un objet de 3D.
En bas, à la droite de l'image dessus,il y a un petite fenêtre, l'image dans laquel est ce qui est affiché dans la casque virtuelle, mais il n'y pas d'image dans l'objet 'Image'.
4. J'ai changé l'émulator du jeux vidéo.
Avant, j'ai utilisé l'émulateur FCEUX, mais, je l'ai remplacé par l'émulateur NEScafé[17], qui est un émulateur de 'opensource' et simple.
]J'ai changé l'émulateur, parce que j'ai eu un problème que quand j'ai envoyé des message de clavier à l'émulateur FCEUX, il n'avait rien de réponse.
J'ai utilisé spy++[18] à vérifié ma programmation.(spy++ est un outil avec qui nous pouvons regrader tous les messages entre des fenêtre).
J'ai trouvé que les messages de clavier sont arrivé à l'émulateur.
Après, j'ai regardé le source de FCEUX, mais, sa source de code est trop complexe. je n'ai pas trouvé où était le problème, donc j'ai changé l'émulateur.
NEScafé est plus simple, donc c'est plus facile à debugger.
Après que j'ai changé l'émulateur, j'ai trouvé que quand j'ai envoyé des message de clavier à l'émulateur NEScafé, sa réponse était correct.
5. J'ai combiné tous les parties.
Le nouveau prototype est comme l'image suivante:
6. Tester mes programmtion en réalité avec la casque virtuelle.
J'ai testé ma programmation avec la casque virtuelle, ça a marché pas mal, je peux contrôler le jeux vidéo par le clavier et je peux regrader l'image du jeux vidéo en réalité virtuelle avec la casque virtuelle.
Le vidéo est suivant:
Semaine 9-10
Par discuter avec Monsieur Grisoni et Valentin, nous avons trouvé qu'il y avait 2 problème et une optimisation.
Problème:
1. Performance
La casque virtuelle a deux caméras qui sont correspondants à deux yeux. Nous avons trouvé que les images des deux caméras ne sont pas même, la différence entre les deux caméra est une frame. Je vais donner un exemple, soit les frames du jeux vidéo sont {1,2,3,4,5,6,7,8,9...}, les frames d'un caméra sont {1,2,3,4,5,6,7,8,9...}, les frames d'autre caméra sont {22,3,4,5,6,7,8,9,10..}.
Nous avopns trouvé que le problème est à cause d'utilisation de la fonction 'update' d'Unity.
Avant, la programmation renouveler l'image jusqu'à la fonction 'update' est activé. mais, la casque virtuelle a deux caméra. Après un caméra fait l'activation de la fonction 'update', l'autre caméra peux faire l'activation de 'update'. ce ne sont pas simultanés. Donc les images de deux caméra ne sont pas mêmes.
Pour corriger le problème, nous n'allons pas utiliser la fonction 'update' à renouveler les images. Nous allons utiliser un thread à changer simultanement les images des deux caméras. Et nous allons changer un peu la structure de la ^programation. La nouvelle structure est suivante:
2. Stabilité
Nous avons eu la situation que la programmation a provoqué la plantage. Le problème peut être causé par la carte NVIDA. Valantin va corriger le problème.
Optimisation
Après, je vais ajouter quelque nouvelle manière d'intéraction avec Kinect[19], Je vais ajouter la fonction que le joueur peut contrôler le jeux vidéo par des geste spéciale.
Semaine 11-12
Dans les deux semaines, nous avons fait 3 choses:
1. Modéfier le code selon la nouvelle stucture que j'ai dit en semaine 9-10, et faire le teste.
Nous avons modifié le code pour réaliser la nouvelle structure que j'ai dit en semaine 9-10. Maintenant, il n'y a pas de retard entre les deux caméras de Fove. Mais, j'ai trouvé que quand le programmation a été lancé, le renouvellement de frame du jeux vidéo est devenu très lent. J'ai trouvé le problème que quand le Fove lance, il utilise beaucoup de resource de CPU, près de 85% de CPU est occupé par Fove. L'émulateur n'a pas assez de CPU, donc il devient lent.
Pour résoudre le problème, je vais mettre le "Intermédiaire" et l'émulateur à l'autre ordinateur pour que l'émulateur et "Intermédiaire" ons assez de ressource de CPU à fonctionner.
2. Chercher les information d'utiliser le Kinect à faire l'interaction.
J'ai trouvé le SDK de kinect pour Windows dans le site[20]. Il y a description d'utilisation de SDK. J'ai trouvé aussi un vidéo
Le vidéo présente une fonction intéressant qu'on peut utiliser le Kinect à touvé la postion des mains et obtenir la distance entre les mains et le Kinect. Nous pouvons utiliser la fonctions à simuler 'Cliquer' de sourris avec le Kinect. Par exemple, quand la distance entre les main est inférieur à une seuil, j'ai vais envoyer un message à 'serveur' que le joueur est en train de cliquer le sourris à telle postion.
3. Ecrire le raport intermédiaire.
Semaine 15-16
Pendant les 2 semaines, j'ai fait des choses suivantes:
1.Récupérer le Kinect v1.
2. Installer le 'runtime' de Kinect.
3. Ecrire un code pour utiliser le Kinect à trouver les positions des mains.
Les détails sont suivants:
Kinect, initialement connu sous le nom de code Project Natal1, est un périphérique destiné au matériel Microsoft avec la Xbox 360 pour la V1 et la Xbox One et Windows depuis la V2 permettant de contrôler une interface sans utiliser de manette.[21]
Pour utiliser le Kinect V1 en Système d'opération de Windows, j'ai téléchargé le runtime et SDK de Kinect V1: l'adresse est suivante: https://www.microsoft.com/en-us/download/details.aspx?id=40277.
Après, j'ai écrit un programme à détecter les positions des joints.
Pour controller le kinect, nous devons installer le SDK et le runtime de Kinecet importer la Bibliothèque de Kinect.
Après, j'ai créé un objet de KinectSensor qui donne la fonction pour obtenir les frames de caméra. J'ai obtenu les position des joints et les affichées dans une fenêtre.
private void SensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e) { Skeleton[] skeletons = new Skeleton[0]; using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame()) { if (skeletonFrame != null) { skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength]; skeletonFrame.CopySkeletonDataTo(skeletons); } } using (DrawingContext dc = this.drawingGroup.Open()) { dc.DrawRectangle(Brushes.Black, null, new Rect(0.0, 0.0, RenderWidth, RenderHeight)); if (skeletons.Length != 0) { foreach (Skeleton skel in skeletons) { RenderClippedEdges(skel, dc); if (skel.TrackingState == SkeletonTrackingState.Tracked) { this.DrawBonesAndJoints(skel, dc); } } } this.drawingGroup.ClipGeometry = new RectangleGeometry(new Rect(0.0, 0.0, RenderWidth, RenderHeight)); } }
Semaine 17-18
Pendant des 2 semaines, j'ai intégré le programme de contrôle de Kinect à le programme de l'émulateur pour contrôler le jeux vidéo par les mains dans l'air.
Premièrement, j'ai récupéré le frame de Kinect et les positions des mains. Après, j'ai laissé la taille de la fenêtre et le frame de Kinect être mêmes. Après, j'ai utilisé les fonctions de dessiner de C# à dessiner un cercle dans la position de la main pour présenter la position de la main.
Après, j'ai écrit le programme pour dessiner des buttons dans la fenêtre de l'émulateur pour simuler le joystick. Chaque grand cercle représente un buton. Il y a 8 buttons qui représente UP, DOWN, LEFT, RIGHT, START, SELECT, A et B.
Après, j'ai écrit un programme pour réaliser mon algorithme à détecter si les butons sont touché.
L'algorithme est suivant:
J'ai créé un array qui représente le statue de button (soit appuyé, soit non appuyé) .Chaque fois que la frame d'émulateur renouvelle, le programme va juger si la center de cercle de main est dans un cercle de button, le programme va regarder le statue du button, si la statue est 'non appuyé', il va mettre la statue à 'appuyé' et envoyer l'instruction de 'appuyer' à l'émulateur. Si la statue d'un button est 'appuyé', mais la centre des mains ne sont pas dans la cercle de button, le programme va mettre la statue du button à 'non appuyé' et envoyer l'instruction de 'soulever' à l'émulateur.
unsafe void Draw(byte[] screen) { BitmapData _frameData = _frame.LockBits(new Rectangle(0, 0, 256, 240), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); byte* ptr = (byte*)_frameData.Scan0; for (int i = 0; i < 256 * 240; i++) { ptr[i] = screen[i]; } _frame.UnlockBits(_frameData); g.DrawImage(_frame, 0, 0, Size.Width, Size.Height); drawButtonVirtual(); drawHandVirtuel(); VirtualHandControl(); if(client != null) client.SendBitmap(_frame); }
void VirtualHandControl() { Controller.Button rightTouched = this.rightHand.TouchButton(this.buttonsVirtual); Controller.Button leftTouched = this.leftHand.TouchButton(this.buttonsVirtual); if(keyStates.A == false) { if(rightTouched == Controller.Button.A || leftTouched == Controller.Button.A) { _console.Controller.setButtonState(Controller.Button.A, true); keyStates.A = true; } } else { if(rightTouched != Controller.Button.A && leftTouched != Controller.Button.A) { _console.Controller.setButtonState(Controller.Button.A, false); keyStates.A = false; } } if(keyStates.B == false) { if(rightTouched == Controller.Button.B || leftTouched == Controller.Button.B) { _console.Controller.setButtonState(Controller.Button.B, true); keyStates.B = true; } } else { if(rightTouched != Controller.Button.B && leftTouched != Controller.Button.B) { _console.Controller.setButtonState(Controller.Button.B, false); keyStates.B = false; } }
if(keyStates.Start == false) { if(rightTouched == Controller.Button.Start || leftTouched == Controller.Button.Start) { _console.Controller.setButtonState(Controller.Button.Start, true); keyStates.Start = true; } } else { if(rightTouched != Controller.Button.Start && leftTouched != Controller.Button.Start) { _console.Controller.setButtonState(Controller.Button.Start, false); keyStates.Start = false; } } if(keyStates.Selete == false) { if(rightTouched == Controller.Button.Select || leftTouched == Controller.Button.Select) { _console.Controller.setButtonState(Controller.Button.Select, true); keyStates.Selete = true; } } else { if(rightTouched != Controller.Button.Select && leftTouched != Controller.Button.Select) { _console.Controller.setButtonState(Controller.Button.Select, false); keyStates.Selete = false; } } if(keyStates.Up == false) { if(rightTouched == Controller.Button.Up || leftTouched == Controller.Button.Up) { _console.Controller.setButtonState(Controller.Button.Up, true); keyStates.Up = true; } } else { if(rightTouched != Controller.Button.Up && leftTouched != Controller.Button.Up) { _console.Controller.setButtonState(Controller.Button.Up, false); keyStates.Up = false; } } if(keyStates.Down == false) { if(rightTouched == Controller.Button.Down || leftTouched == Controller.Button.Down) { _console.Controller.setButtonState(Controller.Button.Down, true); keyStates.Down = true; } } else { if(rightTouched != Controller.Button.Down && leftTouched != Controller.Button.Down) { _console.Controller.setButtonState(Controller.Button.Down, false); keyStates.Down = false; } } if(keyStates.Right == false) { if(rightTouched == Controller.Button.Right || leftTouched == Controller.Button.Right) { _console.Controller.setButtonState(Controller.Button.Right, true); keyStates.Right = true; } } else { if(rightTouched != Controller.Button.Right && leftTouched != Controller.Button.Right) { _console.Controller.setButtonState(Controller.Button.Right, false); keyStates.Right = false; } } if(keyStates.Left == false) { if(rightTouched == Controller.Button.Left || leftTouched == Controller.Button.Left) { _console.Controller.setButtonState(Controller.Button.Left, true); keyStates.Left = true; } } else { if(rightTouched != Controller.Button.Left && leftTouched != Controller.Button.Left) { _console.Controller.setButtonState(Controller.Button.Left, false); keyStates.Left = false; } }
A la fin, j'ai fait un test, le vidéo est suivant:
Poursuite du Projet
Maintenant, j’ai réalisé à afficher l’image de jeux vidéo à FOVE, et permettre le joueur à modifier la dimension d’image et contrôler le jeux vidéo, et j’ai réalisé à utiliser le Kinect à ajouter une manière d’interaction d'un système simulant le "touch" à partir des mouvements des mains de l'utilisateur. Mais, il y a deux problèmes :
1. Quand les programmations lance, le renouvellement d’image est très lent. Maintenant, je pense que le problème est que la FOVE utilise beaucoup de ressource de CPU(85%), donc l’émulateur et l’intermédiaire n’ont pas assez de ressource de CPU, donc le renouvellement est lent. C’est important à résoudre le problème
2. La détection des mains n’est pas stable. Parfois, le kinect ne peut pas détecter les positions des mains. Donc, c’est nécessaire à créer un algorithme à améliorer la stabilité de la détection des mains.
Documents Rendus
Fichier:Ji YANG PRE Intermediaire.pdf
Fichier:Presentation final.pdf
Les codes sont tous dans le github: https://github.com/guigui00700/Interaction2DinVR/