samedi 14 janvier 2012

GUI Multi-Resolution & Bandes noires

Voici une technique que l'on peut utiliser afin de fabriquer une GUI qui soit valide quelque soit la résolution et le ratio, ce qui permet de faire des applications compatibles web/ios/android sans avoir trop à gerer les différences de ratio entre toutes les résolutions possibles (surtout pour Android).

Tout d'abord il faut choisir une resolution "idéale" dans laquelle il faut designer toute la gui (prendre de preference la résolution la plus élevée parmis toutes celles que vous souhaitez supporter).
Pour l'exemple 1024*768

On va utiliser une classe qui va tracer des bandes noires sur les cotés Gauche/Droite ou Haut/Bas en fonction du ratio courant et qui va seter la matrice de GUI (GUI.matrix) afin que celle ci s'adapte à la résolution courante.

On va utiliser une classe "cGUIRatio" (voir le code plus bas).
Dans un callback OnGUI() on va appeller notre fonction cGUIRatio.DrawBandesNoires().
Toute la gui tracée apres cet appel sera redimentionnée et replacée en fonction de la résolution.
Notons que tous les elements de gui doivent etres positionnés comme si vous etiez dans la resolution native (1024*768 pour notre exemple).


void OnGUI()
{
cGUIRatio.DrawBandesNoires();
.... le reste de la gui...
}



Voila la classe cGUIRatio:


using UnityEngine;
using System.Collections;

public class cGUIRatio
{
//Resolution native de la gui
const int WIDTH = 1024;
const int HEIGHT = 768;

//-----------------------------------------
//
//-----------------------------------------
public static void DrawBandesNoires()
{
Texture2D white = Resources.Load("Textures/white") as Texture2D; //texture blanche 8*8 dans "Resources/Textures"
float sw = Screen.width;
float sh = Screen.height;
float refw = WIDTH;
float refh = HEIGHT;
float rCurrentRatio = sw / sh;
float guiScale;
if (rCurrentRatio > (refw/refh))
{
//Bandes Droite/Gauche guiScale = sh / refh;
}
else
{
//Bandes Haut/Bas
guiScale = sw / refw;
}
float cw = refw * guiScale;
float ch = refh * guiScale;
Vector3 v = new Vector3((sw - cw) / 2, (sh - ch) / 2, 0);

GUI.matrix = Matrix4x4.identity;
if (v.x > 0)
{
//Bandes sur les cotés
GUI.color = Color.black;
GUI.DrawTexture(new Rect(0, 0, v.x, Screen.height), white);
GUI.DrawTexture(new Rect(Screen.width - v.x, 0, v.x, Screen.height), white);
}
if (v.y > 0)
{
//Bandes Haut et Bas
GUI.color = Color.black;
GUI.DrawTexture(new Rect(0, 0, Screen.width, v.y), white);
GUI.DrawTexture(new Rect(0, Screen.height - v.y, Screen.width, v.y), white);
}
GUI.color = Color.white;

//Setup matrix for next gui draw
Vector3 translation = new Vector3(v.x,v.y,0);
GUI.matrix = Matrix4x4.TRS(translation, Quaternion.identity, Vector3.one*guiScale);
}
}



vendredi 13 janvier 2012

Augmented Reality SDKs

Pas spécifiquement de l'Unity mais peut etre utile tout de meme.
http://www.icg.tugraz.at/Members/gerhard/augmented-reality-sdks

Serialisation XML

.Net permet de faire de la sérialisation XML sur n'importe quelle classe.
Exemple la classe Settings:

using System.Xml.Serialization;
using System.IO;
using System.Collections.Generic;

public class Settings
{
public int val0; //Ce membre sera serialisé car il est public
private int val1; //Ce membre ne sera pas sérialisé car il est private

 public List<MyObjectDesc> MyObjectList; //Les listes sont supportées


public Settings()
{
}
public Settings(int v0, int v1)
{
val0 = v0;
val1 = v1;
}
}

Voici le code pour la serialiser:
public static Settings mSettings = new Settings(0,1);
...

TextWriter writer = new StreamWriter(Application.persistentDataPath+"/mySettings.xml");
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
serializer.Serialize(writer, mSettings);
writer.Close();


Voici le code pour la deserialiser:
TextReader reader = new StreamReader(Application.persistentDataPath+"/mySettings.xml");
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
mSettings = (Settings)serializer.Deserialize(reader);
reader.Close();

Une variante lorsqu'on recupere le fichier xml dans les resources:

TextAsset GameAsset = Resources.Load(filename, typeof(TextAsset)) as TextAsset;
TextReader reader = new StringReader(GameAsset.text);
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
Settings mSettings = ( Settings )serializer.Deserialize(reader) as Settings;
reader.Close();


On a le possibilisté d'influer sur le format du xml.
Pour spécifier le root dans le xml (par defaut il prend le nom de la classe):
[XmlRoot("RootSettings")] public class Settings
....

Pour ignorer un membre public:
[XmlIgnore] public int val0;

Pour faire en sorte que le membre soit stocké en tant que parametre et non de node:
[XmlAttribute("myParam")] public int val0;


Pour specifier le nom du node:
[XmlElement(ElementName="marker")] public List<cPlane> marker;

--------------------
Plus d'information sur les methodes de load de data xml dans Unity:
http://www.mindthecube.com/blog/2009/11/reading-text-data-into-a-unity-game


Cacher un GameObject de la Hierarchiy


Pour cacher un GameObject de la Hierarchy:

GameObject mDumy = new GameObject("Dumy");
mDumy.hideFlags = HideFlags.HideInHierarchy;

jeudi 12 janvier 2012

Interpreter du JSON

Voici une lib tres pratique pour interpreter du JSON
Fonctionne sur toutes les plateformes.

https://bitbucket.org/darktable/jsonfx-for-unity3d/downloads

Cast int, float, string

Pas forcement simple a trouver comme information dans la doc, voici comment caster des string en int ou float.
C'est tout simple en fait:

string str = "128";
int vali = int.Parse(str);
float valf = float.Parse(str);
string str2 = vali.ToString()+","+valf.ToString()+" ou "+vali+","+valf;

Note: il y a également les fonctions de type  int.TryParse(...)

Lecture/Ecriture de fichiers

Classique, quelques lignes pour lire ou ecrire des fichiers.


using System.IO;

//Lecture
FileStream f = File.OpenRead(filename);
byte[] bt = new byte[f.Length];
f.Read(bt, 0, (int)f.Length);
f.Close();

//Ecriture
byte[] bt = ...
FileStream f = File.Create(Application.persistentDataPath+"/myFile.dat");
f.Write(bt, 0, bt.Length);
f.Close();


Au passage, pour savoir si un fichier existe:
if (File.Exist(filename)) ...

Lecture ecriture de textes:
http://www.csharphelp.com/2005/12/simple-text-file-operations-in-c/

Hastable

Avec les listes ArrayList les Hastable font partie des moyens de stockage qui peuvent etre tres pratiques.
Les hastable permettent d'associer une Key et une Value et de pouvoir ainsi retrouver une Value à partir de sa Key sans avoir à parser toute la liste et sans avoir à implementer une recherche dichotomique.
Tout ca est fait pour vous.

Creation:
Hastable mHastable = new Hastable();

Ajout:
if (!mHastable.ContainsKey("myKey"))
  mHastable.Add("myKey",myObject);

Acces direct à l'objet (ici une texture) en connaissant sa key:
if (mHastable.ContainsKey("myKey"))
  Texture2D tex = mHastable["myKey"] as Texture2D;

Nombre d'éléments:
mHastable.Count;

Tout effacer:
mHastable.Clear();

Parcours de toute la liste:
int i=0;
foreach (DictionaryEntry de in mHashtable)
{
Texture2D tex = de.Value as Texture2D;
Debug.Log(i+"  "+tex.name+"\n"+i+"  "+de.Key);
i++;
}

Qualcomm/Vuforia - Réalité Augmentée dans Unity iOS/Android

Fonctionant pour iOS et Android cette lib gratuite (pour l'instant) pour faire de la RA sur mobile est tres performante et facile d'utilisation.
Capable de detecter des plan comme des boites, partiellements cachés.
Notons tout de meme que le systeme pour fabriquer les tags est un systeme online et il faut donc imperativement passé par leur outil en ligne pour les génerer (un moyen de garder la main sur l'utilisation de la techno qui pourrait un jour devenir payante).

Pour l'avoir utilisé dans une application iPad2, c'est vraiment tres bien.

https://ar.qualcomm.at/qdevnet/sdk/ios

DrawLine dans la fenetre Game

Unity fournit une fonction Debug.DrawLine(...) tres pratique qui permet de tracer des lignes de couleur dans la fenetre Scene pour faire du debugage, mais ces lignes n'apparaissent pas dans la fenetre Game et on ne peut donc pas s'en servir pour autre chose que du debug.
Il y a pourtant un moyen de tracer des lignes dans Game (sans a voir a fabriquer de mesh pour cela), le voici.

Le tracé doit se faire dans le callback OnPostRender().
De ce fait ce code doit se trouver dans une classe de type MonoBehavior attachée à un objet de la scene.


static public Material lineMaterial;

void OnPostRender()
{
CreateLineMaterial();
lineMaterial.SetPass(0);
GL.Begin(GL.LINES);
Color col = Color.red;
GL.Color(col);
Vector3 v0 = new Vector3(0,0,0);
Vector3 v1 = new Vector3(10,10,10);
GL.Vertex3(v0.x, v0.y, v0.z);
GL.Vertex3(v1.x, v1.y, v1.z);
Debug.DrawLine(v0,v1,col); //Pour avoir le tracé aussi dans Scene
GL.End();
}


static public void CreateLineMaterial()
{
if( !lineMaterial )
{
lineMaterial = new Material( "Shader \"Lines/Colored Blended\" {" +
"SubShader { Pass { " +
"    Blend SrcAlpha OneMinusSrcAlpha " +
"    ZWrite Off Cull Off Fog { Mode Off } " +
"    BindChannels {" +
"      Bind \"vertex\", vertex Bind \"color\", color }" +
"} } }" );
lineMaterial.hideFlags = HideFlags.HideAndDontSave;
lineMaterial.shader.hideFlags = HideFlags.HideAndDontSave;
}
}

Barre de progression GUI

Toute petite classe pour afficher des barres de progression dans la GUI.
Pour faire des barre simplistes de ce style la (pas tres jolie, a vocation de debug):

C'est une classe statique qu'il faut donc appeller cGUIProgressBar.DrawGUI(...)
Ca utilise la GUI, il faut donc imperativement appeller cette fonction à partir des acallback unity OnGUI().

using UnityEngine;
using System.Collections;


public class cGUIProgressBar
{
private static Texture2D mWhiteTex;
private static GUIStyle mStyle;


//----------------------------------------
// A appeller dans un callback unity OnGUI()
//----------------------------------------
public static void DrawGUI(int x, int y, int count, int total, string prefix, int width)
{
if (mWhiteTex==null) mWhiteTex = Resources.Load("Textures/white") as Texture2D; //Texture 8*8 (ou autre) toute blanche à placer dans Resources/Textures
if (mStyle==null)
{
mStyle = new GUIStyle();
mStyle.alignment = TextAnchor.MiddleCenter;
mStyle.font = Resources.Load("Font/FontProgressBar") as Font; //Font Arial size 13 à placer dans Resources/Font
mStyle.fontStyle = FontStyle.Bold;
}
GUI.color = Color.black;
GUI.DrawTexture(new Rect(x, y, width+2, 18), mWhiteTex);
GUI.color = new Color(0.8f,0,0);
GUI.DrawTexture(new Rect(x+1,y+1,width,16), mWhiteTex);
GUI.color = Color.white;
float val = 1;
if (total!=0) val = ((count)/(float)total);
GUI.DrawTexture(new Rect(x+1, y+1, val*width, 16), mWhiteTex);
GUI.color = Color.black;
GUI.Label(new Rect(x, y, width+2, 18), prefix+" "+((int)(val*100))+"% ("+count+"/"+total+")", mStyle);
GUI.color = Color.white;
}
}

Les directives de compilation Unity

Grace aux directives de compilation d'unity on peut prévoir tel ou tel code et ca peut etre extremement pratique.

UNITY_EDITORDefine for calling Unity Editor scripts from your game code.
UNITY_STANDALONE_OSXPlatform define for compiling/executing code specifically for Mac OS (This includes Universal, PPC and Intel architectures).
UNITY_DASHBOARD_WIDGETPlatform define when creating code for Mac OS dashboard widgets.
UNITY_STANDALONE_WINUse this when you want to compile/execute code for Windows stand alone applications.
UNITY_WEBPLAYERPlatform define for web player content (this includes Windows and Mac Web player executables).
UNITY_WIIPlatform define for compiling/executing code for the Wii console.
UNITY_IPHONEPlatform define for compiling/executing code for the iPhone platform.
UNITY_ANDROIDPlatform define for the Android platform.
UNITY_PS3Platform define for running Play Station 3 code.
UNITY_XBOX360Platform define for executing XBbox 360 code.

    #if UNITY_EDITOR
      Debug.Log("Unity Editor");
    #endif
    #if UNITY_IPHONE
      Debug.Log("Iphone");
    #endif
    #if UNITY_STANDALONE_OSX
Debug.Log("Stand Alone OSX");
    #endif
    #if UNITY_STANDALONE_WIN
      Debug.Log("Stand Alone Windows");
    #endif


Elles sont toutes la: http://unity3d.com/support/documentation/Manual/Platform%20Dependent%20Compilation.html

Modification du logo de Load du WebPlayer

http://unity3d.com/support/documentation/Manual/Customizing%20the%20Unity%20Web%20Player%20loading%20screen.html

Les Thread en Unity

Dans unity pour faire accomplir une operation en tache de fond on peut utiliser le fameux Yield. Mais on peut aussi utiliser des thread classiques qui restent d'ailleurs beaucoup plus simples à utiliser.
using System; using System.Threading;
... Thread myThread = new Thread(new ThreadStart(ThreadLoop)); myThread.Start(); ... ... public static void ThreadLoop() { while (Thread.CurrentThread.IsAlive) { Thread.Sleep(500); // Attente de 500 ms Debug.Log("Je travaille..."); } }
http://emerica.developpez.com/csharp/threads/

Editeur de Shader Unity Strumpy



Quitter l'application en script


Pour quitter en script l'application unity: Application.Quit();
Ca n'a de sens évidement que dans un cadre d'application standalone.