this._cube.SetSize(size);
this._cube.SetPosition(position);
La transformation étant obtenue par l'intermédiaire de la propriété Transformation. Celle-ci se calcule automatiquement à partir des méthodes SetRotation, SetSize et SetPosition.
//modulolong iTime = (long)(gameTime.TotalGameTime.TotalMilliseconds % 2000f);//passage en radianfloat fAngle = iTime * (2.0f * MathHelper.Pi) / 2000.0f; this._cube.SetSize(size);this._cube.SetPosition(position); //la transformatio en elle mêmeMatrix world = Matrix.CreateRotationY(fAngle) * Matrix.CreateRotationX(fAngle) * this._cube.Transformation;
effect.Parameters["xWorld"].SetValue(world);
A l'exécution vous obtenez pourtant une application dont le rendu est complètement différent de notre précédent tutoriel :

En fait on peut avoir l'impression au premier abord d'avoir regressé. Au contraire nous avons fait un grand pas en avant ! Notre cube est plus petit tout simplement parcequ'il possède désormais une taille unitaire (1 pour chacun de ses arrètes contre 2 précédemment), afin de facilement spécifier des tailles précises. Il possède une couleur pâle. Ceci est du au fait que nous affectons à chaque vertex la couleur spécifiée par l'utilisateur avant l'appel à la méthode Load du cube. Reportez vous au code de la méthode InitializeVertices pour mieux comprendre :
private void InitializeVertices(){ VertexPositionColor[] vertices = new VertexPositionColor
; vertices[0].Position = new Vector3(0f, 0f, 0f); vertices[0].Color = this.Color; vertices[1].Position = new Vector3(0f, 0f, 1f); vertices[1].Color = this.Color; vertices[2].Position = new Vector3(1f, 0f, 1f); vertices[2].Color = this.Color; vertices[3].Position = new Vector3(1f, 0f, 0f); vertices[3].Color = this.Color; vertices[4].Position = new Vector3(1f, 1f, 1f); vertices[4].Color = this.Color; vertices[5].Position = new Vector3(1f, 1f, 0f); vertices[5].Color = this.Color; vertices
.Position = new Vector3(0f, 1f, 0f);// vertices
.Color = this.Color; vertices[7].Position = new Vector3(0f, 1f, 1f); vertices[7].Color = this.Color; this._vertexBuffer = new VertexBuffer( this._device, typeof(VertexPositionColor), 8, ResourceUsage.WriteOnly, ResourceManagementMode.Automatic); this._vertexBuffer.SetData(vertices);
}
Enfin pour terminer, le cube ne semble pas tourner sur lui-même mais par rapport à un point correspondant au vertice 0. Là encore tout est normal, le vertice 0 se trouve à l'origine dans le repère 3D à l'intérieur duquel nous créer notre cube (0f, 0f, 0f). C'est par rapport à l'origine d'un objet 3D que se calculent toutes les transformations qui lui sont appliquées. Notre cube se voit rotaté sur lui-même, cette rotation s'effectuera donc par rapport à ce vertice.
Pour terminer ce point il est plus que necessaire de lire l'article qui se trouve ici, pour bien comprendre l'importance de l'ordre de la multiplication des matrices de transformation (rotation, redimentionnement, translation) pour modifier l'affichage d'un objet à l'écran afin de vous éviter toute surprise et bug de rendu incompréhensible.
La théorie étant acquise, un exercice peaufinera notre pratique : Maintenant que nous avons une classe clé en main pour créer un cube et l'afficher simplement, essayez d'en afficher plusieurs à l'écran.
Vous pouvez télécharger le sample, les deux moteurs et les exercices ici.
New York !
Si vous avez reussi l'exercice précédent, notre premier monde de jeu sera une formalité pour vous. La ville que nous allons créer ici sera plus que rudimentaire. Le sol sera un cube avec une hauteur de 1, les trottoires des pavés applatis avec une hauteur de 5, enfin les batiments seront eux aussi des cubes dont la hauteur sera variable. A ce stade de nos connaissances nous ne pouvons pas ajouter de lumières d'ambiance, pas de texture aux immeubles et pas d'optimisation d'affichage. C'est pourtant un avantage certain puisque dans les articles qui suivront nous améliorerons le code dans ce sens et pourrons mesurer facilement l'importances des notions que nous allons acquerir. Cette ville nous offre aussi la possibilité de bien comprendre comment placer ses objets à l'écran.
Les seules modifications que nous apporterons au programme que nous venons de faire vont porter sur la classe Game1. Nous venons d'énumérer trois types de cube. Commencez donc à déclarer ces cubes au tout début de cette classe :
private Cube _ground;
private Cube[] _trottoires;
private Cube[] _batiments;
Ajoutez de même un ensemble de constantes qui nous éviterons de remplir notre code de valeur numériques incompréhensibles :
private static int NumberOfBatimentsOnASide = 5;
private static int BatimentSize = 50;
private static int TrottoireSize = 70;
private static int TrottoireHeight = 5;
private static int RoadWidth = 40;
private static int BatimentMinimalHeight = 50;
private static int BatimentMaximalHeight = 450;
Celles ci sont assez explicites pour ne pas avoir à être présentées. Le constructeur instanciera tous les cubes.
_ground = new Cube();
_trottoires = new Cube[NumberOfBatimentsOnASide * NumberOfBatimentsOnASide];
_batiments = new Cube[NumberOfBatimentsOnASide * NumberOfBatimentsOnASide];
Et la méthode InitializeCubes les chargera en mémoire
private void InitializeCubes(){ TerrainSize = (RoadWidth + TrottoireSize) * NumberOfBatimentsOnASide + RoadWidth; Random random = new Random(); //sol this._ground.Color = Color.Gray; this._ground.Load(this.graphics.GraphicsDevice); this._ground.SetSize(new Vector3( TerrainSize, TerrainSize, 1)); this._ground.SetPosition(new Vector3(-TerrainSize / 2, -TerrainSize / 2, 0)); //trottoires for (int i = 0; i < NumberOfBatimentsOnASide; i++) { for (int j = 0; j < NumberOfBatimentsOnASide; j++) { this._trottoires[i * NumberOfBatimentsOnASide + j] = new Cube(); this._trottoires[i * NumberOfBatimentsOnASide + j].Color = Color.LightGray; this._trottoires[i * NumberOfBatimentsOnASide + j].Load(this.graphics.GraphicsDevice); this._trottoires[i * NumberOfBatimentsOnASide + j].SetSize(new Vector3(TrottoireSize, TrottoireSize, TrottoireHeight)); _trottoires[i * NumberOfBatimentsOnASide + j].SetPosition(new Vector3(RoadWidth + j * (TrottoireSize + RoadWidth) - TerrainSize/2 , RoadWidth+(TrottoireSize-BatimentSize)/2 + BatimentSize + i * (TrottoireSize + RoadWidth) - TerrainSize/2 , 0)); } } //grattes ciels for (int i = 0; i < NumberOfBatimentsOnASide; i++) { for (int j = 0; j < NumberOfBatimentsOnASide; j++) { this._batiments[i * NumberOfBatimentsOnASide + j] = new Cube();