samedi 26 mai 2012

Comment pauser un jeu dans Unity?

Pauser le jeu dans Unity est relativement facile. Si toutes vos fonctions sont dépendantes du taux de rafraîchissement (c'est le cas des animations), la commande suivante va pauser le jeu:
Time.timeScale = 0; 
La variable timeScale contrôle à qu'elle vitesse le temps s'écoule dans l'engin de jeu Unity. Une valeur inférieure à 1 va créer un effet de ralentie et 0, fige tout dans le temps. Pour résumer le jeu, remettez la valeur de timeScale à 1:
Time.timeScale = 1; 
Pour le code indépendant du taux de rafraîchissement, je suggère d'utiliser des messages (la fonction SendMessage). Une classe PauseGame contient deux fonctions statique: Pause et Resume. La fonction Pause met timeScale à 0 et envoie un message OnPauseGame à tous les objets dans la scène. De même, la fonction Resume remet timeScale à 1 et envoie le message global OnResumeGame. Les messages s'utilisent dans le code comme les fonctions Update et Start de Unity. La classe qui a besoin de pauser et de résumer implémente les fonctions OnPauseGame et OnResumeGame et ajoute le code spécifique pour cette classe:
public class A : MonoBehaviour
{
 void OnPauseGame()
 {
  //Do something to pause.
 }
 
 void OnResumeGame()
 {
  //Do something to resume.
 }
}
Pour mon script PauseGame (ci-dessous), j'ai ajouter la variable paused. Cette variable indique si le jeu est pausé ou non. Cette variable est utile pour bloquer du code dans la fonction Update comme suit:
public class B : MonoBehaviour
{
 void Update()
 {
  if ( !PauseGame.paused )
  {
   //Do something
  }
 }
}
Le script PauseGame (ci-dessous) pause le jeu lorsque la touche du bouton Pause est pressée. Vous devez définir ce bouton dans le menu Edit->Project Settings->Input. Dans mon cas, j'ai utilisé les touches P et Esc. Le script suit le modèle Singleton, d'où la variable s_instance. Ce modèle assure qu'une seule instance du script est créée. J'ai ajouté ce script sur ma caméra. Vous pouvez placer ce script n'importe où, mais il est important d'en avoir une copie dans votre scène pour pouvoir pauser le jeu.
/**
 * \file
 * 
 * \author Mentalogicus
 * \date 2012
 * 
 */

using UnityEngine;
using System.Collections;
using Mentalogicus;


/// \brief Pause the game with the button "Pause" defined in Unity Editor.
/// 
/// 
/// This class use the singleton modèle.
/// 
/// The game is paused by setting Time.timeScale to 0. Also, two messages are send:
/// - OnPauseGame: when the game is paused
/// - OnResumeGame: when the game is restarted
/// 
/// Those messages must be used in you script to manage codes that are not
/// frame independant (like those using absolute Time).
/// 
/// To use it, place the function OnPauseGame like this:
/// 
/// void OnPauseGame()
/// {
///  Do something.
/// }
/// 
/// 
/// 
[AddComponentMenu("Mentalogicus/Behaviour/PauseGame")]
public class PauseGame : MonoBehaviour
{
 #region Public members
 
 /// 
 /// Name of the message sent when pausing the game.
 /// 
 public const string ON_PAUSE_GAME = "OnPauseGame";
 
 /// 
 /// Name of the message sent when resuming the game
 /// 
 public const string ON_RESUME_GAME = "OnResumeGame";
 
 /// 
 /// Name of the button to pause or resume the game.
 /// 
 public const string BUTTON_NAME = "Pause";
 
 #endregion
 
 #region Private members
 
 /// 
 /// Flag that indicate if the game is paused.
 /// True-> the game is paused.
 /// False-> the game is running.
 /// 
 private bool _paused = false;
 
 /// 
 /// The s_instance store the single instance of PauseGame.
 /// 
 private static PauseGame s_instance = null;
 
 #endregion
 
 #region Properties
 
 /// 
 /// Return the instance of PauseGame.
 /// 
 /// 
 /// The instance.
 /// 
 public static PauseGame instance
 {
  get 
  {
   if ( !s_instance )
   {
    FindInstance();
   }
   return s_instance; 
  }
 }
 
 /// 
 /// Gets a value indicating whether the game is paused.
 /// 
 /// 
 /// true if paused; otherwise, false.
 /// 
 public static bool paused
 {
  get { return instance._paused; } 
 }
 
 #endregion
 
 #region Constructors
 
 /// 
 /// Initializes a new instance of the  class.
 /// 
 private PauseGame()
 {
  this._paused = false;
 }
 
 #endregion
 
 #region Static methods
 
 /// 
 /// Pause the game.
 /// 
 public static void Pause()
 {
  s_instance.PauseFunc();
 }
  
 /// 
 /// Resume the game.
 /// 
 public static void Resume()
 {
  s_instance.ResumeFunc();
 }
 
 /// 
 /// Finds the instance in the game.
 /// 
 private static void FindInstance()
 {
  s_instance = FindObjectOfType( typeof(PauseGame)) as PauseGame;
  
  if (!s_instance)
  {
   s_instance = null;
   throw( new UnityException("You need to add an active PauseGame script on a GameOjbect in your scene."));
  }

 }
 
 #endregion
 
 #region Private Methods
 
 /// 
 /// Awake this instance.
 /// 
 private void Awake()
 {
  FindInstance();
 } 
 
 /// 
 /// Pause the game and broadcast an OnPauseGame message to all GameObject of the game.
 /// 
 private void PauseFunc()
 {
  this._paused = true;
  
  Time.timeScale = 0;
  
  //Broadcasst the OnPauseGame event to all object in the game.
  Object[] objects = FindObjectsOfType (typeof(GameObject));
  
  foreach (GameObject go in objects) 
  {
   go.SendMessage (ON_PAUSE_GAME, SendMessageOptions.DontRequireReceiver);
  }
 }
 
 /// 
 /// Resume the game and broadcast an OnResumeGame message to all GameObject in the game.
 /// 
 private void ResumeFunc()
 {
  this._paused = false;
  
  Time.timeScale = 1;
  
  //Broadcasst the OnResumeGame event to all object in the game.
  Object[] objects = FindObjectsOfType (typeof(GameObject));
  
  foreach (GameObject go in objects) 
  {
   go.SendMessage (ON_RESUME_GAME, SendMessageOptions.DontRequireReceiver);
  }
 }
 
 /// 
 /// When Pause button is pressed, pause or resume the game
 /// depending on the actual state of the game.
 /// 
 private void Update ()
 {
  if ( Input.GetButtonUp( BUTTON_NAME ) )
  {
   if ( !this._paused )
   {
    this.Pause();
   }
   else
   {
    this.Resume();
   }
  }
 }
 
 #endregion
 
} //End class
PauseGame.cs by Mentalogicus is licensed under a Creative Commons Attribution 3.0 Unported License.

vendredi 18 mai 2012

Attention au thread dans Unity 3D.

Hier, j'ai passé une partie de la journée à chercher une faute dans un de mes codes. Le message d'erreur était le suivant: get_transform can only be called from the main thread. Constructors and field initializers will be executed from the loading thread when loading a scene. Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function. Après bien des recherches et des modifications inutiles, j'ai trouvé le coupable: System.Timers. Voici un code bien simple qui reproduit l'erreur ci-dessus:
using UnityEngine;
using System.Collections;
using System.Timers;

public class DemoTimer : MonoBehaviour
{
 private Timer _timer;
 
 void Start ()
 {
  this._timer = new Timer( 5 );
  this._timer.Elapsed += new ElapsedEventHandler( OnTimeEvent);
  this._timer.Start();
 }
 
 void OnTimeEvent( object sender, ElapsedEventArgs e )
 {
  //L'erreur se produit ici.
  this.transform.position = new Vector3(0,0,0); 
 }
  
}
Pourquoi ce code est fautif? Voici la raison. Unity utilise un seul thread. Un thread (http://fr.wikipedia.org/wiki/Thread_%28informatique%29) est un processus indépendant. Le timer crée un nouveau thread. Lorsque l'intervalle de temps est écoulé, la fonction OnTimeEvent est appelée dans le thread du timer. L'object this est créé dans le thread d'Unity. Lorsque j'écris
this.transform.position = new Vector3(0,0,0);
je tente d'accéder à un object qui vie dans un autre thread. Le thread timer ne sait pas quoi faire avec l'object this et me retourne une erreur. En règle générale, il est préférable de ne pas utiliser de thread avec Unity. L'engin de jeu Unity n'est pas thread safe. L'alternative à System.Timers dans Unity est d'utiliser une coroutine comme suit:
public class DemoTimer : MonoBehaviour
{
 void Start ()
 {
  StartCoroutine(this.OnTimeEvent()); 
 }
 
 IEnumerator OnTimeEvent()
 {
     yield return new WaitForSeconds(1);
  
  Debug.Log("Tic");
  this.transform.position = new Vector3(0,0,0);
 }
}

jeudi 10 mai 2012

Comment compiler un jeu PC dans Unity 3D?

Pour compiler un jeu dans Unity, sélectionner le menu File->Build Settings... ou File->Build. Aux fins de ce tutoriel, je clique sur Build Settings.
Le menu suivant apparaît:
L'encadré du haut offre la liste des scènes dans le projet. Le numéro à droite indique l'ordre des scènes. La scène la plus en haut est chargée en premier. Pour changer l'ordre des scènes, glissez la scène à l'endroit désiré. Les scènes doivent être cochées pour être ajoutées au jeu final.

L'encadré Platform indique la plate-forme sur lequel nous voulons que le jeu soit exécuté. Dans le cas présent, je choisis PC and Mac Standalone.

Avant de construire le jeu, cliquons sur le boutons Player Settings. Un menu apparaît dans l'inspecteur:
Ce menu permet d'ajuster avec plus de détails l'aspect visuel de l'exécutable du jeu, de sélectionner les tailles d'écran, si par défaut le jeu démarre en plein écran, etc. L'encadré Default Icon et le menu Icon change l'icône de votre exécutable. Donc, au lieu d'avoir l'icône d'Unity sur votre fichier .exe, vous aurez votre propre icône.
C'est tellement plus jolie et professionnel!
Le menu Splash Image vous permet d'ajouter une image qui va apparaître dans la fenêtre qui ouvre juste après avoir exécuté le jeu:
La fenêtre de démarrage d'un jeu en Unity.
Une fois que vous avez terminé avec les paramètres, appuyez sur Build ou Build and Run. Unity va demander l'emplacement de l'exécutable.

Important:
Unity crée un exécutable et un dossier de données. Dans mon exemple, j'utilise un projet nommé Space Junk. Lorsque je construis l'exécutable, je me retrouve avec le fichier Space Junk.exe et le dossier Space Junk_Data.
Si vous voulez distribuer votre jeu, vous devez fournir l'exécutable et le dossier de données. J'ai appris cette leçon à la dure. Je voulais montrer mon démo Space Junk à un ami. J'ai partagé l'exécutable sur Dropox. Dans ma tête, un exécutable contient le jeu en entier. Je pensais en terme d'installateur à l'époque. Unity ne crée par un installateur, mais un jeu avec ces données. Mon ami l’essaie et ne réussit pas à l'exécuter. J’essaie sur une machine différente et rien ne fonctionne. J'ai finalement découvert le problème en cherchant sur le Web.

lundi 7 mai 2012

Truc pour organiser le code C#

Le C# (et aussi le C++) offre une instruction de précompilation fort utile pour organiser le code: #region. Cette commande spécifie un bloc de code que vous pouvez réduire ou développer en cliquant sur le - ou + à la gauche de la page d'édition. Une région se termine par la commande #endregion. Voici un exemple dans MonoDevelop:
Cliquer sur l'image pour l'agrandir.
Une région Logical Operators a été créée avec les commandes #region et #endregion. Un petit symbole - entouré d'un carré est à la gauche de la commande #region et permet de réduire ce bloc de code. Une fois réduit, le code ressemble à ceci:
Cliquer sur l'image pour l'agrandir.
Les fonctions à l'intérieur de la région ont disparu et sont remplacées par une étiquette indiquant Logical Operators. MonoDevelop ajoute automatiquement dans le haut à droite une liste des régions définies par l'utilisateur.
La liste des régions.
En cliquant sur un élément de la liste, MonoDevelop place le curseur au début de la région. Très utile pour naviguer rapidement dans le code. De plus, MonoDevelop ajoute les régions dans le Document Outline et regroupe les fonctions de chaque région.

Il est possible d'imbriqués les régions. Par exemple, ajoutons une région Bidon qui entoure la région Logical Operators:
Cliquer sur l'image pour l'agrandir.
Deux icônes d'offuscation apparaissent comme attendu. Par contre, voici deux comportements inattendus et désagréables:
  1.  La liste des régions n'est pas imbriquée. La région Bidon apparaît après la région Logical Operators!
  2. Dans le Document Outline, la région Logical Operators a été remplacée par la région Bidon
MonoDevelop ne semble pas supporter complètement les régions imbriquées. Je ne sais pas quand le problème sera réglé. Si quelqu'un est au courant du développement de MonoDevelop et connaît la réponse, faites-le-moi savoir.

vendredi 4 mai 2012

Comment documenter les scripts dans Unity?

Les commentaires dans les scripts sont importants pour expliquer leur fonctionnement. L'utilisation des commentaires varie d'une école de pensée à une autre. Certains prônent l'utilisation massive de commentaires pour expliquer les intentions du code. D'autres croient que le code devrait être auto explicatif et libre de commentaires. Ma position sur le sujet est mitoyenne. J'utilise des noms de fonctions et de variables évocatrices, claires et indiquant leur fonction le plus souvent possible. Malgré tout, si je ne commente pas régulièrement, j'ai de la difficulté à saisir mon propre code après quelques mois. Alors, je documente.

L'ajout d'un commentaire se fait en ajoutant deux barres obliques "//":
//Un commentaire vraiment inutile.
string action = "Commente moi!"
ou pour un bloc de commentaires, avec "/*" et "*/":
/*
Il était une fois, dans l'ouest, une princesse prisonnière
dans une taverne miteuse gardée par des truands à la peau
tannée par le soleil et au regard sauvage, etc., etc., etc.
*/
string action = "Commente, mais commente égal!"
et aussi avec "///":
///Parce que // fait tellement 1990.
string action = "Commente moi!"

Dans MonoDevelop, si vous tapez "///" avant une entité (une variable, une fonction, une classe, etc.) vous obtenez:
/// 
/// Du XML???
/// 
private string action = "Commente moi!" 
MonoDevelop crée un commentaire avec un tag XML. Ce code XML est utilisé par un programme externe pour générer automatiquement la documentation. Les générateurs de documentation sont des programmes qui lisent le code source et les commentaires, l'analysent et retournent une documentation en format texte, HTML, XML, Latex, etc. qu'il est possible de consulter et d'effectuer des recherches. C'est très pratique pour se retrouver dans un gros projet avec des centaines de scripts. Mon générateur de documentation favori est Doxygen. Je vais parler de Doxygen dans un prochain billet.

L'utilisation du système de documentation avec l'XML amène un autre énorme avantage, en plus de la génération automatique de la documentation. Si vous glisser votre souris au-dessus d'une entité du code (une variable, une fonction, un enum, etc.), une info bulle jaune apparaît qui ressemble à ceci:

L'information de cet encadré n'est pas très utile. Nous apprenons que _selected est un booléen. Avec l'ajout d'un commentaire XML, voici le résultat:
Notre commentaire apparaît dans l'info bulle jaune! Cette description va apparaître si le curseur glisse au-dessus de la variable _selected quel que soit sa position dans le code. Si une variable publique est commentée, la description va aussi apparaître si la variable est utilisée dans une autre classe. Ce petit encadré peut sauver du temps. Imaginer que vous êtes dans le code. Vous travaillez sur une fonction, mais vous ne vous souvenez plus de l'intention d'une fonction ou de ce que stocke une variable. Normalement, vous naviguez dans le code, allez à la fonction ou à la variable, lisez la description et revenez à votre fonction. C'est long et inutile. Avec les commentaires XML, glisser le curseur sur la variable ou la fonction mystérieuse et voilà la description (si bien sur vous l'avez documentée).

Les commentaires en XML sont plus longs que les commentaires normaux, mais je recommande de les utiliser systématiquement pour permettre de générer la doc automatiquement et pour les info bulles. La longueur des commentaires n'est pas un problème réel avec les éditeurs modernes. Mono Develop permet de cacher les commentaires en appuyant sur le symbole +:
Avant.
Après.
Peu importe la longueur du commentaire, une fois refermé, il ne prendra qu'une ligne, vous permettant de lire le code sans obstruction.

lundi 16 avril 2012

Comment faire des captures d'images en Unity?

Vous avez ce jeu magnifique ou ce superbe effet et vous voulez le présenter au monde entier. Vous démarrez votre jeu et pressez la touche Print Screen (en mode Windowed, sinon l'image est noire). Vous allez dans Photoshop ou, comme moi dans GIMP et sélectionner l'écran de jeu et vous le copiez dans une nouvelle image. Vous avez votre capture d'écran. Jusqu'à maintenant, c'est la procédure que j'utilisais à défaut de connaître mieux.

Aujourd'hui, j'ai appris une commande magnifique: Application.CaptureScreen. Cette fonction prend une capture d'écran directement du jeu! Fini les manipulations dans GIMP! La fonction requière un argument obligatoire, le chemin d'accès au fichier comprenant le nom du fichier et la taille de l'image. La taille du fichier est un entier qui détermine combien de fois plus grande sera l'image. Si j'entre 3, la capture d'image sera 3 fois la taille de l'écran!

Voici le script que j'ai utilisé:
using UnityEngine;
using System.Collections;

public class ScreenShot : MonoBehaviour 
{

 // Update is called once per frame
 void Update () 
 {
  if ( Input.GetKeyUp( KeyCode.F3 ) )
  {
   Debug.Log("Screen captured.");
    Application.CaptureScreenshot("Screenshot.png"); 
  }
 }
}
et le résultat (j'ai utilisé le Prefab Big Tree dans Standard Asset/Tree Creator pour l'arbre):


J’utilise la touche F3 pour déclencher la capture d'image. La question maintenant: où est enregistrée l'image? Lors de l’exécution dans l'éditeur d'Unity, l'image est enregistrée dans le répertoire du projet. C'est le répertoire qui contient le répertorie Assets. Pour l'exécutable, c'est différent. La compilation du projet crée un .exe et un dossier nommé nom_du_exe_data. Par exemple, si mon .exe se nomme screenshot.exe, le répertoire sera screenshot_data. Ce dossier est le répertoire de base et c'est l'endroit où les captures d'images sont enregistrées. La fonction ne fait rien si déclencher à partir du lecteur Web.

vendredi 13 avril 2012

Comment ajouter un script n'importe où dans le menu Component d'Unity?

Par défaut, Unity ajoute les scripts dans le menu Component->Scripts. Ce sous-menu se remplit rapidement avec l'ajout de scripts et devient difficile à utiliser. Un peu de classification serait le bienvenu. La solution est de créer ses propres sous-menus dans le menu Component. Pour ce faire, l'attribut AddComponentMenu est ajouté avant la déclaration de la classe avec comme argument une chaîne de caractère contenant le nom du sous-menu suivi du nom du script:
[AddComponentMenu("SousMenuName/SousSousMenuName/NomDuScript")]
Reprenons le script RandomObjectsCreator.cs utilisé pour instancier des prefabs lorsque la touche "Espace" est pressée. Je veux que ce script apparaisse dans le menu Component->Mentalogicus->Creator. J'ajoute l'attribut AddComponentMenu de la manière suivante:
using UnityEngine;
using System.Collections;

[AddComponentMenu("Mentalogicus/Creator/RandomObjectsCreator")]
public class RandomObjectsCreator : MonoBehaviour 
{
 public Transform _prefab;
 public float _radius = 10;
 
 // Update is called once per frame
 void Update () 
 {
  if ( Input.GetKey("space") )
  {
   //Create a random position.
   //The insideUnitSphere return a random 
   //position inside a sphere of radius 1.
   Vector3 rndPos = Random.insideUnitSphere * _radius;
   
   //Create a random rotation.
   Quaternion rndRotation = Random.rotation;
   
   //Instantiate a new object at a random 
   //position with a random rotation.
   Transform newGameObj = Instantiate( _prefab, 
    rndPos, rndRotation) as Transform; 
  }
 }
}

RandomObjectsCreator.cs by Mentalogicus is licensed under a Creative Commons Attribution 3.0 Unported License.

Unity compile le script et le menu apparaît à l'endroit désiré!

jeudi 12 avril 2012

Sierpinski Cake

J'ai créé un gâteau de Sierpinski, qui est une fractale, dans Unity 3D. Une fois terminé et vue le résultat de face: je me suis demandé à quoi le gâteau de Sierpinski ressemble vue de l'intérieur. Pour ce faire, j'ai ajouté un petit script pour bouger la caméra à l'aide du clavier.
-->

Contrôle:

  • Flèche droite ou a: tourne vers la droite.
  • Flèche gauche ou d: tourne vers la gauche.
  • Flèche haut ou w: tourne vers le haut.
  • Flèche bas ou s: tourne vers le bas.
  • Barre espace: Accélération.
  • r: Repositionne la caméra à sa position initiale.

mardi 10 avril 2012

Comment faire une caméra orbitale en Unity?

Aujourd’hui, nous allons apprendre à faire une caméra orbitale en Unity. Une caméra orbitale est une caméra qui orbite un objet choisi comme cible. La caméra est tournée au moyen de la souris en appuyant sur le bouton gauche. Les mouvements de la souris sont transformés en rotations de la caméra. La caméra tourne en fixant constamment la cible choisie. De plus, je veux que la caméra zoom et dé zoom à volonté. Comme le dicton dit: une image vaut mille mots et un vidéo vaut au moins 24 images par secondes (pour 24 000 mots/s!!!). Voici le résultat final de ce que je veux implémenter:
Unity Web Player | WebPlayer -->
Principe de bases Lorsque le bouton droit de la souris est pressé, je convertis le mouvement du curseur en degrés selon les coordonnées x et y. J’utilise ces angles pour indiquer la rotation de la caméra. La rotation de la caméra autour du château implique deux mouvements: la rotation de la caméra sur elle-même et son déplacement dans l’espace.


Rotation de la caméra sur elle-même
Commençons par réglé le cas de la rotation de la caméra sur elle-même. Bougeons la caméra d’un angle a:

Sur le diagramme, la caméra du bas est la caméra dans sa position initiale. La caméra du haut est la caméra qui a tourné d’un angle a. La caméra ne pointe plus sur la cible. Pour corriger, la caméra doit être bougé d’un angle b. Les angles a et b sont alternes-internes; par conséquent, a=b. Vive la géométrie!

Unity utilise les quaternions pour représenter les rotations. Les quaternions sont des nombres hyper complexes (c’est-à-dire une extension des nombres complexes). Pour ceux intéresser à approfondir leur connaissance en math, je recommande chaudement Khan Academy. Il n’est pas essentiel de connaître les quaternions pour les utiliser. Je le mentionne pour que vous pensiez de convertir vos angles en quaternion. Les rotations en degrés sont converties en quaternion comme suit (aide Unity pour Quaternion.Euler):
 
Quaternion rotation = Quaternion.Euler( a,b,0 );
où a et b sont les angles de rotation par rapport à l’axe des x et y respectivement. L’axe des x est l’horizontale, l’axe y la verticale et l’axe z est la profondeur. Pourquoi le nom Euler et non pas Angle comme nom de fonction? Il y a une fonction Angle dans Unity. Elle retourne l’angle minimal entre deux rotations. Ici, il faut entrer 3 angles. C’est Leonhard Euler qui a introduit l’utilisation des 3 angles pour décrire l’orientation d’un corps solide. Le nom est resté. La caméra est tournée en modifiant la propriété rotation du composant transform:
 
transform.rotation = rotation;
Deux commandes, et la caméra est tournée!

Déplacement de la caméra
La caméra est à une distance d la cible et doit maintenir cette distance lors de la rotation. La caméra tourne comme attacher à une corde fixée à la cible.

La position de la caméra est égale à la somme des vecteurs \( \vec{p} \) et \( \vec{r} \). Le vecteur \( \vec{p} \) est connu, c’est la position de la cible. La longueur du vecteur \( \vec{r} \) est d, la distance de la caméra à la cible. Le changement de direction du vecteur \( \vec{r} \) est donné par la rotation de la caméra. Posons \( Fr \) comme une fonction de la rotation. La position de la caméra est: \( Fr(\vec{r}) + \vec{p} \). Ok, mais c’est quoi \( Fr \)? Vous vous rappelez les quaternions? Et bien, une de leurs fantastiques propriétés est que lorsqu’ils sont multipliés avec un vecteur, ils retournent un vecteur ayant subit la bonne rotation. Posons \(  Q \) comme étant le quaternion. La position de la caméra est fourni par l’équation: \[  Q \vec{r} + \vec{p} \] Une multiplication suivi d’une addition. Le code C# pour la position de la caméra est:
 
transform.position = rotation * r + p;
La combinaison du code pour la rotation de la caméra sur elle-même et le code pour modifier la position est:
 
void Rotate( float x, float y )
 {
  Quaternion rotation = Quaternion.Euler(y,x,0.0f);

  Vector3 position = 
   rotation * _distanceVector + _target.position;

  transform.rotation = rotation;
  transform.position = position;
 }

Les contrôles
La rotation de la caméra est activée en pressant le bouton gauche de la souris. La méthode GetButton de la classe statique Input  est ce qu’il nous faut. Cette fonction accepte une chaîne de caractère en entrée qui détermine le type de bouton et retourne un booléen qui indique si le bouton a été pressée. La chaîne de caractère pour le bouton gauche de la souris est  Fire1 dans Unity (pour plus de détails voir ici). Le code pour détecter que le bouton gauche a été pressée est:

void RotateControls()
 {
  if ( Input.GetButton("Fire1") )
  {
  
  }
 }
Maintenant, la position du curseur détermine l’angle de rotation. Pour déterminer la position du curseur, la méthode GetAxis de la classe Input est utilisée. Cette fonction retourne le mouvement de la souris suivant un axe en pixel. L’axe doit être spécifié. Pour la souris, les axes sont nommés Mouse X et Mouse Y. Le code est inséré à l’intérieur du If comme suit:
 
void RotateControls()
 {
  if ( Input.GetButton("Fire1") )
  {
   _x += Input.GetAxis("Mouse X") * _xSpeed;
   _y += -Input.GetAxis("Mouse Y") * _ySpeed;

   this.Rotate(_x,_y);
  }
 }
L’appel à la fonction Rotate, vu précédemment, complète le code pour la rotation. 

Zoom
Pour le zoom, j’utilise la roulette de la souris. Pour déterminer si la roulette a été tournée, la méthode GetAxis est appelée en utilisant la chaîne de caractères: Mouse ScrollWheel. Selon la direction de rotation, j’incrémente ou je décrémente la distance de la caméra à la cible. Ensuite, j’appelle la fonction Rotate pour repositionner la caméra au bon endroit. 

Le code complet est fourni ci-dessous.

OrbitCamera.cs

   
using UnityEngine;
using System.Collections;

/**
 * Change the camera into an orbital camera. An orbital is a camera
 * that can be rotated and that will automatically reorient itself to
 * always point to the target.
 * 
 * The orbit camera allow zooming and dezooming with the mouse wheel.
 * 
 * By clicking the mouse and dragging on the screen, the camera is moved. 
 * The angle of rotation  correspond to the distance the cursor travelled. 
 *  
 * The camera will keep the angular position when the button is pressed. To
 * rotate more, simply repress the mouse button et move the cursor.
 *
 * This script must be added on a camera object.
 *
 * @author Mentalogicus
 * @date 11-2011
 */
public class OrbitCamera : MonoBehaviour
{
 
 //The target of the camera. The camera will always point to this object.
 public Transform _target;
 
 //The default distance of the camera from the target.
 public float _distance = 20.0f;
 
 //Control the speed of zooming and dezooming.
 public float _zoomStep = 1.0f;
 
 //The speed of the camera. Control how fast the camera will rotate.
 public float _xSpeed = 1f;
 public float _ySpeed = 1f;
 
 //The position of the cursor on the screen. Used to rotate the camera.
 private float _x = 0.0f;
 private float _y = 0.0f;
 
 //Distance vector. 
 private Vector3 _distanceVector;
 
 /**
  * Move the camera to its initial position.
  */
 void Start ()
 {
  _distanceVector = new Vector3(0.0f,0.0f,-_distance);
  
  Vector2 angles = this.transform.localEulerAngles;
  _x = angles.x;
  _y = angles.y;
    
  this.Rotate(_x, _y);
  
 }

 /**
  * Rotate the camera or zoom depending on the input of the player.
  */
 void LateUpdate()
 {
  if ( _target )
  {
   this.RotateControls();
   this.Zoom();
  }
 }
 
 /**
  * Rotate the camera when the first button of the mouse is pressed.
  * 
  */
 void RotateControls()
 {
  if ( Input.GetButton("Fire1") )
  {
   _x += Input.GetAxis("Mouse X") * _xSpeed;
   _y += -Input.GetAxis("Mouse Y")* _ySpeed;
     
   this.Rotate(_x,_y);
  }
 
 }
 
 /**
  * Transform the cursor mouvement in rotation and in a new position
  * for the camera.
  */
 void Rotate( float x, float y )
 {
  //Transform angle in degree in quaternion form used by Unity for rotation.
  Quaternion rotation = Quaternion.Euler(y,x,0.0f);
  
  //The new position is the target position + the distance vector of the camera
  //rotated at the specified angle.
  Vector3 position = rotation * _distanceVector + _target.position;
    
  //Update the rotation and position of the camera.
  transform.rotation = rotation;
  transform.position = position;
 }
 
 /**
  * Zoom or dezoom depending on the input of the mouse wheel.
  */
 void Zoom()
 {
  if ( Input.GetAxis("Mouse ScrollWheel") < 0.0f )
  {
   this.ZoomOut();
  }
  else if ( Input.GetAxis("Mouse ScrollWheel") > 0.0f )
  {
   this.ZoomIn();
  }

 }
 
 /**
  * Reduce the distance from the camera to the target and
  * update the position of the camera (with the Rotate function).
  */
 void ZoomIn()
 {
  _distance -= _zoomStep;
  _distanceVector = new Vector3(0.0f,0.0f,-_distance);
  this.Rotate(_x,_y);
 }
 
 /**
  * Increase the distance from the camera to the target and
  * update the position of the camera (with the Rotate function).
  */
 void ZoomOut()
 {
  _distance += _zoomStep;
  _distanceVector = new Vector3(0.0f,0.0f,-_distance);
  this.Rotate(_x,_y);
 }
 
} //End class

OrbitCamera.cs by Mentalogicus is licensed under a Creative Commons Attribution 3.0 Unported License.

mardi 3 avril 2012

Logiciel de modélisation de corps humain.

Modéliser un corps humain réaliste en 3D est une tâche difficile à accomplir qui demande beaucoup de talents et de connaissances. Le maillage doit refléter les caractéristiques du corps humain pour se déformer correctement lors des animations. La connaissance de l'anatomie humaine est extrêmement utile dans ce processus. Si vous êtes déjà un modeleur, modeler un corps humain ne vous pose pas d'obstacle. Si vous êtes comme moi, c'est-à-dire,  un néophyte de la modélisation, modéliser le corps humain est simplement or de votre portée. Ou peut-être pas...

Internet pour un greffon pour Blender  de génération de modèle humain, j'ai trouvé... exactement ce que je cherchais. J'ai trouvé: MakeHuman. Le titre du logiciel explique exactement ce que le logiciel fait: il fait des humains! Le logiciel est Open Source et les modèles générés peuvent être utilisés dans des jeux commerciaux! Le projet est actif et l'équipe publie régulièrement des mises à jour du logiciel (chaque jour, ce sont des "Nightly Build"). Mise en garde: le logiciel est en phase alpha, présente plusieurs bogues et manque des fonctionnalités. Malgré ces limitations, MakeHuman est un outil prometteur.

Voici un écran de MakeHuman avec un modèle mâle généré aléatoirement:

Modèle 3D d'un homme généré par MakeHuman.
Le logiciel offre plusieurs contrôles classés par onglets permettant de modifier les différentes parties du corps humain selon vos désirs. Vous pouvez changer la grandeur, le poids, le tonus musculaire, la forme des yeux, du nez, de la bouche, du cou, de la tête, des bras, etc. L'onglet Pose vous permet de modifier la position du corps, de faire sourire votre personnage ou de le rendre triste. MakeHuman ajoute automatiquement un squelette à votre modèle, ce qui est fantastique pour animer par la suite.

Les modèles générés par MakeHuman sont importables dans Blender à travers le format MHX. Les modèles importés comportent le maillage, le squelette compatible avec Rigify, des cheveux, la carte UV pour la texture de la peau, des yeux, des cils, etc. Bref, le modèle est prêt pour l'animation. Cet utilitaire sauve littéralement des dizaines d'heures de modélisation.Voici un modèle que j'ai fait avec MakeHuman et modifier avec Blender, et ce, en quelques heures:

Le même travail, fait complètement dans Blender, m'aurait pris des semaines de travail et d'apprentissage pour un résultat moindre. Bravo et merci à l'équipe de MakeHuman pour cet outil.

dimanche 4 mars 2012

Les tableaux en C#

En programmation, les tableaux ou Array en anglais sont des outils puissants. Un tableau contient plusieurs valeurs qui sont accessibles par un index.

Pour faire une analogie, prenons une rue avec des maisons. Les maisons sont les espaces mémoire. Il est possible de nommer chaque maison par un nom, disons, celui de son propriétaire:
Le nom assigné à chaque maison est une variable dans un programme. En utilisant le nom de la maison, je peux accéder à la maison et savoir ce qu'il y a dedans. Dans le cas présent, nous allons y mettre des humains. La variable va contenir le nombre d'humains dans chaque maison. Je ne parle pas encore de tableau, j'y viens plus tard. Pour l'instant, je n'ai pas indiqué le nombre de personnes dans chaque maison. Je dois assigner un nombre de personnes pour chaque maison. Le pseudo-code est:
  
 Rambo = 1;
 Lili = 1;
 John = 1;
 Ada = 1;
 Linus = 1;
 Steve = 1;
C'est assez long! Imaginer maintenant qu'il y a une centaine de personnes sur cette rue. Toutes les maisons sont occupées par des célibataires, donc, une personne par maison. Vous allez devoir manuellement indiquer qu'il y a une personne dans chaque maison! Ce n'est pas très pratique. Heureusement, il y a les tableaux.

Reprenons notre exemple. Nommons la rue Tapaga. La rue est une analogie pour un tableau. Une rue contient des maisons. De même, un tableau contient une liste d'espace mémoire. Chaque maison a une adresse qui est l'équivalent de l'index du tableau.
Pour accéder à une maison, je dis 0 Rue Tabaga, 1 rue Tabaga, etc. Pour accéder à un tableau en C#, la syntaxe est:
 Rambo = Tabaga[0];
Je mémorise la valeur de l'index 0 du tableau Tabaga dans la variable Rambo. Remarquer que les caractères [] sont utilisés au lieu des parenthèses habituelles utilisées en mathématique. En mathématique, pour accéder à un élément d'un vecteur, la notation est a(1), a(2), etc. En C# et pour tous les langages de la famille du C, les crochets sont utilisés. En Fortran, la notation mathématique est utilisée.

Le premier élément d'un tableau est à l'index 0 et non 1. Donc, si je veux accéder au dixième élément d'un tableau, l'index à utiliser est 9 et non dix. Le code est:
 Rambo = Tabaga[9];
La question que vous vous posez sûrement est: pourquoi les concepteurs du C (l'ancêtre du C++ et du C#) n'ont-ils pas commencé les tableaux à 1, comme dans le vrai monde (ou comme en Fortran). Les éléments d'un tableau C sont enregistrés dans des espaces mémoire contigus. En sachant l'adresse du premier élément, les autres éléments sont atteints en additionnant l'index du tableau. Ainsi, le tableau n'a pas besoin de mémoriser l'adresse mémoire de tous les éléments, mais seulement du premier, sauvant de la mémoire. Un index en C est un décalage par rapport au premier élément du tableau. Le tableau contient l'adresse du premier élément du tableau. Disons que l'adresse du premier élément est 987. L'adresse du premier élément est: 987 + 0, le second est 987 + 1, le troisième est 987 + 2, le nième est 987 + i où i est l'index du tableau.

L'écrire dans un tableau est l'inverse de la lecture. Il suffit de mettre le tableau à gauche de l'égalité. Le code est:
 
 Tabaga[1] = 1;
Il est désormais possible de dire que chaque maison de la rue Tabaga possède une seule personne. En C#, le code est:
for each maison in Tabaga
{
 maison = 1;
} 
L'instruction for each est une boucle. Une boucle répète l'action entre les {} plusieurs fois. Dans le cas d'une boucle for each, la boucle se répète tant qu'il y a des éléments dans Tabaga. maison est une variable de la boucle for each avec l'adresse d'une maison de la rue Tabaga. En assignant 1 à maison, j'assigne 1 à un élément de Tabaga. La boucle visitant toutes les maisons, le résultat final est que chaque maison à désormais 1 d'assigné. Une boucle est un peu comme le facteur. Le facteur visite chaque maison une à la fois et délivre le courrier en fonction de l'adresse de la maison.

Les boucles démontrent la puissance de la notation par tableau. La boucle initialisera tous les éléments de Tabaga à 1, peu importe la taille de Tabaga. Donc, si le nombre de maisons sur Tabaga change, le code, lui, reste le même! Il n'y a rien à changer!

Bon, maintenant que nous avons expliqué les tableaux, il est temps d'apprendre la notation pour les créer en C#. Pour créer un tableau unidimensionnel d'entier en C#, la notation est:
 int[] Tabaga;
 Tabaga = new int[5];
ou en une seule instruction:
 int[] Tabaga = new int[5];
Ici encore, le C# est contre-intuitif. La notation la plus naturelle serait:
 int Tabaga(5) = new int(5);
Pour les admirateurs de cette notation, sachez que Visual Basic ressemble un peu à cela. De même que Fortran.

Mettons-nous dans la perspective du C#. Une variable est déclarée en donnant son type et le nom de la variable. Le type est ce que la variable contient comme int, double, float ou un objet comme Vector3 ou Person, etc. Maintenant, je veux déclarer un tableau de int. Le type de la variable est donc int[] où [] représente un tableau. Dans ce cas, il est logique que le int[] soit en premier et la notation d'un tableau est compatible avec celle des variables.

L'instruction:
 int[] Tabaga;
crée un pointeur à un tableau de int. Un pointeur est une variable qui contient une adresse mémoire qui, elle, contient des données. Dans le cas présent, le pointeur ne contient pas d'adresse. Pour revenir à notre analogie, Tabaga est une rue vide, sans maison. La rue est vide, mais je peux construire des maisons par la suite.

Analysons l'instruction suivante:
 Tabaga = new int[5];
L'instruction new indique aux programmes de créer un tableau de int contenant 5 éléments. new crée l'espace mémoire nécessaire au tableau et retourne l'adresse mémoire qui est enregistrée dans Tabaga. Pour revenir à notre analogie, la commande new construit des maisons dans notre rue.

Si vous avez d'autres questions sur les tableaux en C#, n'hésitez pas à me le demander.

samedi 3 mars 2012

Comment insérer des équations dans Blogger? (Revisité)

Dans le passé, j'ai indiqué comment ajouter des équations à Blogger en ajoutant des images générées par Microsoft Office ou Libre Office. Cette méthode n'est pas idéale. Si je veux créer une page Web contenant des équations et du contenu interactif, deux logiciels minimum sont utilisés: Libre Office ou Microsoft Office et un éditeur Web. Par la suite, les images des équations sont générées et collées comme image sur la page Web. Pour un site Web avec beaucoup d'équations, ce processus est très long. Si une ou plusieurs équations sont erronées, les images sont modifiées dans le traitement de texte, transformées en images et collées à nouveau dans la page Web. Ouf! Il y a surement une façon plus rapide. Effectivement, en cherchant, il y en a une. Il existe une application JavaScript nommée MathJax qui transforme du code LaTeX en magnifique équation sur le Web. Comme je suis un admirateur inconditionnel de LaTeX, je ne pouvais pas être plus heureux! Si vous n'êtes pas fou de TeX ou LaTeX, MathJax n'est probablement pas pour vous. J'aime LaTeX, car je peux écrire une équation au clavier plus vite qu'avec la souris et une interface graphique.

L'installation de MathJax dans Blogger est simple. Dans le Tableau de Bord de Blogger, aller dans l'onglet Présentation. Choississer le sous-onglet Modifier le code HTML. Chercher pour la balise < /head > . Juste avant cette balise, insérer le code suivant:

Enregistrer le modèle et vous êtes prêt pour insérer des équations avec MathJax. Comment fait-on pour insérer des équations? Commençons avec une équation bien simple: \[ \alpha + \beta = \gamma \] Le code pour cette équation est:
\[ \alpha + \beta = \gamma \]
Les symboles \[ et \] servent de balises à MathJax. Ce n'est pas du LaTeX. Le code à l'intérieur des balises est du code LaTeX. Essayons maintenant d'afficher une équation plus complexe, comme les équations de Maxwell: \[ \begin{aligned} \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\ \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\ \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\ \nabla \cdot \vec{\mathbf{B}} & = 0 \end{aligned} \] Fantastique! La qualité graphique est sublime. Le code LaTeX pour cette équation est:
\[
\begin{aligned}
\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\   
\nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
\nabla \cdot \vec{\mathbf{B}} & = 0 
\end{aligned}
\]
N'importe quel code LaTeX pour équation fonctionne. N'oubliez pas d'entourer votre code des balises \[ et \], sinon MathJax ne fera rien. Pour plus d'informations, je vous conseille d'aller visiter le site de MathJax.