Création de mondes isométriques guide d'introduction pour les développeurs de jeux

Dans ce tutoriel, je vais vous donner un aperçu général de ce que vous devez savoir pour créer des mondes isométriques. Vous apprendrez ce qu'est la projection isométrique et comment représenter les niveaux isométriques sous forme de tableaux 2D. Nous allons formuler des relations entre la vue et la logique, de sorte que nous puissions facilement manipuler des objets à l'écran et gérer la détection de collision basée sur des mosaïques. Nous examinerons également le tri en profondeur et l'animation des personnages..

Pour accélérer le développement de votre jeu, vous pouvez trouver une gamme d’actifs de jeu isométriques sur Envato Market, prêts à être utilisés dans votre jeu..

Ressources de jeu isométriques sur le marché Envato Articles Similaires

Vous voulez encore plus de conseils sur la création de mondes isométriques? Découvrez le post suivant, Création de mondes isométriques - Notions élémentaires pour Gamedevs, suite, et le livre de Juwal, Starling Game Development Essentials.


1. Le monde isométrique

Vue isométrique est une méthode d'affichage utilisée pour créer une illusion de 3D pour un jeu autrement 2D - parfois appelé pseudo 3D ou 2.5D. Ces images (tirées de Diablo 2 et Age of Empires) illustrent ce que je veux dire:

Diablo 2 Age of Empires

L'implémentation d'une vue isométrique peut être réalisée de plusieurs manières, mais par souci de simplicité, je vais me concentrer sur une à base de carreaux approche la plus efficace et la plus largement utilisée. J'ai superposé chaque capture d'écran ci-dessus avec une grille en losange indiquant la division du terrain en tuiles.


2. Jeux basés sur des tuiles

Dans l'approche basée sur les mosaïques, chaque élément visuel est décomposé en éléments plus petits, appelés mosaïques, de taille standard. Ces tuiles seront organisées pour former le monde du jeu en fonction de données de niveau prédéterminées - généralement un tableau 2D..

Articles Similaires
  • Les tutoriels de Tony Pa sur des tuiles.

Par exemple, considérons une vue 2D descendante standard avec deux carreaux - un carreau d'herbe et un carreau de mur - comme illustré ci-dessous:

Quelques tuiles simples

Ces tuiles ont chacune la même taille et sont chacune carrée, de sorte que la hauteur et la largeur de la tuile sont identiques..

Pour un niveau avec des prairies entourées de murs de tous les côtés, le tableau 2D des données de niveau se présentera comme suit:

[[1,1,1,1,1,1], [1,0,0,0,0,1], [1,0,0,0,0,1], [1,0,0, 0,0,1], [1,0,0,0,0,1], [1,1,1,1,1,1]]

Ici, 0 dénote une tuile d'herbe et 1 désigne une dalle murale. Organiser les tuiles en fonction des données de niveau produira l'image ci-dessous:

Un niveau simple, affiché dans une vue de haut en bas.

Nous pouvons améliorer cela en ajoutant des carreaux de coin et des carreaux de mur verticaux et horizontaux distincts, nécessitant cinq carreaux supplémentaires:

[[3,1,1,1,1,4], [2,0,0,0,0,2], [2,0,0,0,0,2], [2,0,0, 0,0,2], [2,0,0,0,0,2], [6,1,1,1,1,5]]
Niveau amélioré avec les numéros de tuile

J'espère que le concept de l'approche basée sur les carreaux est maintenant clair. Ceci est une implémentation de grille 2D simple, que nous pourrions coder comme ceci:

for (i, boucle à travers les lignes) pour (j, boucle à travers les colonnes) x = j * largeur de la tuile y = i * hauteur de la tuile tileType = levelData [i] [j] placetile (tileType, x, y)

Ici, nous supposons que la largeur et la hauteur des carreaux sont égales (et identiques pour tous les carreaux) et correspondent aux dimensions des images de carreaux. Ainsi, la largeur et la hauteur de la tuile de cet exemple sont 50px, ce qui correspond à la taille totale du niveau de 300x300px, c'est-à-dire six rangées et six colonnes de tuiles de 50x50px chacune.

Dans une approche normale basée sur des tuiles, nous implémentons une vue de haut en bas ou une vue de côté; pour une vue isométrique, nous devons mettre en œuvre la projection isométrique.


3. Projection isométrique

La meilleure explication technique de ce que "projection isométrique" signifie, à ma connaissance, est tirée de cet article de Clint Bellanger:

Nous inclinons notre caméra selon deux axes (en la faisant pivoter de 45 degrés, puis de 30 degrés vers le bas). Cela crée une grille en forme de losange (losange) où les espaces de grille sont deux fois plus larges que hauts. Ce style a été popularisé par les jeux de stratégie et les RPG d'action. Si nous regardons un cube dans cette vue, trois côtés sont visibles (le haut et les deux côtés en regard).

Bien que cela semble un peu compliqué, la mise en œuvre de cette vue est simple. Ce que nous devons comprendre, c’est la relation entre l’espace 2D et l’espace isométrique - c’est-à-dire la relation entre les données de niveau et la vue; la transformation des coordonnées "cartésiennes" descendantes en coordonnées isométriques.

Grille cartésienne vs grille isométrique.

(Nous n'envisageons pas une technique basée sur les carreaux hexagonaux, qui est une autre façon de mettre en œuvre des mondes isométriques.)

Poser des tuiles isométriques

Laissez-moi essayer de simplifier la relation entre les données de niveau stockées sous forme de tableau 2D et la vue isométrique - c’est-à-dire comment nous transformons les coordonnées cartésiennes en coordonnées isométriques..

Nous allons essayer de créer la vue isométrique pour nos données au niveau des prairies murées:

[[1,1,1,1,1,1], [1,0,0,0,0,1], [1,0,0,0,0,1], [1,0,0, 0,0,1], [1,0,0,0,0,1], [1,1,1,1,1,1]]

Dans ce scénario, nous pouvons déterminer une zone praticable en vérifiant si l’élément de tableau est 0 à cette coordonnée, indiquant ainsi l'herbe. L'implémentation de la vue 2D du niveau ci-dessus était une simple itération avec deux boucles, plaçant des carreaux carrés compensant chacun avec les valeurs de hauteur et de largeur de carreaux fixes..

for (i, boucle à travers les lignes) pour (j, boucle à travers les colonnes) x = j * largeur de la tuile y = i * hauteur de la tuile tileType = levelData [i] [j] placetile (tileType, x, y)

Pour la vue isométrique, le code reste le même, mais le placeTile () changements de fonction.

Pour une vue isométrique, nous devons calculer les coordonnées isométriques correspondantes à l'intérieur des boucles..
Les équations à faire sont les suivantes: isoX et isoY représenter les coordonnées x et y isométriques, et cartX et cartY Représentent les coordonnées cartésiennes x et y:

// Cartésien à isométrique: isoX = cartX - cartY; isoY = (cartX + cartY) / 2;
// isométrique à cartésien: cartX = (2 * isoY + isoX) / 2; cartY = (2 * isoY - isoX) / 2;

Ces fonctions montrent comment vous pouvez convertir d'un système à un autre:

fonction isoTo2D (pt: Point): Point var tempPt: Point = nouveau Point (0, 0); tempPt.x = (2 * pt.y + pt.x) / 2; tempPt.y = (2 * pt.y - pt.x) / 2; return (tempPt); 
function twoDToIso (pt: Point): Point var tempPt: Point = nouveau Point (0,0); tempPt.x = pt.x - pt.y; tempPt.y = (pt.x + pt.y) / 2; return (tempPt); 

Le pseudocode de la boucle ressemble alors à ceci:

for (i, boucle à travers les lignes) pour (j, boucle à travers les colonnes) x = j * largeur de tuile y = i * hauteur de tuile tileType = levelData [i] [j] placetile (tileType, twoDToIso (nouveau Point (x, y) ))
Nos prairies murées dans une vue isométrique.

A titre d'exemple, voyons comment une position 2D typique est convertie en une position isométrique:

Point 2D = [100, 100]; // twoDToIso (point 2D) sera calculé comme suit isoX = 100 - 100; // = 0 isoY = (100 + 100) / 2; // = 100 Iso point == [0, 100];

De même, une entrée de [0, 0] aura pour résultat [0, 0], et [10, 5] va donner [5, 7.5].

La méthode ci-dessus nous permet de créer une corrélation directe entre les données de niveau 2D et les coordonnées isométriques. Nous pouvons trouver les coordonnées de la tuile dans les données de niveau à partir de ses coordonnées cartésiennes en utilisant cette fonction:

fonction getTileCoordinates (pt: Point, tileHeight: Number): Point var tempPt: Point = nouveau Point (0, 0); tempPt.x = Math.floor (pt.x / tileHeight); tempPt.y = Math.floor (pt.y / tileHeight); return (tempPt); 

(Ici, nous supposons essentiellement que la hauteur et la largeur des carreaux sont égales, comme dans la plupart des cas.)

Par conséquent, à partir d'une paire de coordonnées d'écran (isométriques), nous pouvons trouver les coordonnées de tuile en appelant:

getTileCoordinates (isoTo2D (point d'écran), hauteur de la tuile);

Ce point d'écran peut être, par exemple, une position de clic de souris ou une position de prise.

Pointe: Une autre méthode de placement est le modèle Zigzag, qui adopte une approche totalement différente..

Déplacement en coordonnées isométriques

Le mouvement est très simple: vous manipulez les données de votre monde de jeu en coordonnées cartésiennes et utilisez les fonctions ci-dessus pour les mettre à jour à l'écran. Par exemple, si vous voulez faire avancer un caractère dans la direction positive, vous pouvez simplement incrémenter sa y puis convertissez sa position en coordonnées isométriques:

y = y + vitesse; placetile (twoDToIso (nouveau point (x, y)))

Tri en profondeur

En plus du placement normal, nous devrons nous occuper de tri en profondeur pour dessiner le monde isométrique. Cela garantit que les éléments les plus proches du joueur sont placés au-dessus des éléments les plus éloignés.

La méthode de tri en profondeur la plus simple consiste simplement à utiliser la valeur de coordonnée y cartésienne, comme indiqué dans cette astuce: plus l'objet est haut sur l'écran, plus son tracé est rapide. Cela fonctionne bien tant que nous n’avons pas d’image-objet occupant plus d’un espace..

Le moyen le plus efficace de trier en profondeur pour les mondes isométriques consiste à fractionner toutes les mosaïques en dimensions standard à mosaïque unique et à ne pas autoriser des images plus grandes. Par exemple, voici une mosaïque qui ne correspond pas à la taille de mosaïque standard - voyez comment nous pouvons la diviser en plusieurs mosaïques qui correspondent toutes à leurs dimensions:

Une grande image est divisée en plusieurs mosaïques de dimensions isométriques standard

4. Créer l'art

L'art isométrique peut être un pixel, mais ce n'est pas obligé. Dans le cas du pixel art isométrique, le guide de RhysD vous indique presque tout ce que vous devez savoir. Une théorie peut également être trouvée sur Wikipedia.

Lors de la création d’art isométrique, les règles générales sont les suivantes:

  • Commencez avec une grille isométrique vierge et respectez la précision parfaite des pixels.
  • Essayez de briser l’art en une seule image isométrique.
  • Essayez de vous assurer que chaque tuile est soit praticable ou non praticable. Ce sera compliqué si nous devons accueillir une seule tuile contenant à la fois des zones accessibles à pied et non piétonnières..
  • La plupart des tuiles devront être reliées de manière transparente dans une ou plusieurs directions.
  • Les ombres peuvent être délicates à mettre en œuvre, sauf si vous utilisez une approche en couches où nous dessinons des ombres sur la couche de sol, puis le héros (ou des arbres ou d’autres objets) sur la couche supérieure. Si l’approche que vous utilisez n’est pas multicouche, assurez-vous que les ombres tombent à l’avant pour éviter qu’elles ne tombent sur, par exemple, le héros qui se tient derrière un arbre..
  • Si vous devez utiliser une image de mosaïque plus grande que la taille de mosaïque isométrique standard, essayez d’utiliser une dimension multiple de la taille de la mosaïque iso. Il est préférable d’avoir une approche en couches dans de tels cas, où l’on peut diviser l’art en différentes pièces en fonction de sa hauteur. Par exemple, un arbre peut être divisé en trois parties: la racine, le tronc et le feuillage. Cela facilite le tri des profondeurs car nous pouvons dessiner des pièces dans les couches correspondantes qui correspondent à leurs hauteurs..

Les mosaïques isométriques plus grandes que les dimensions d'une mosaïque créeront des problèmes de tri en profondeur. Certaines des questions sont discutées dans ces liens:

Articles Similaires
  • Tuiles plus grandes.
  • Fractionnement et algorithme de Painter.
  • Le post d'Openpace sur des moyens efficaces de diviser de plus grandes tuiles.

5. Caractères isométriques

L'implémentation des caractères en vue isométrique n'est pas compliquée, car cela peut sembler. L'art du personnage doit être créé selon certaines normes. Tout d'abord, nous devrons déterminer le nombre de directions de mouvement autorisées dans notre jeu. Habituellement, les jeux offrent un mouvement à quatre ou à huit..

Directions de navigation à huit voies dans les vues de haut en bas et isométriques.

Pour une vue de haut en bas, nous pourrions créer un ensemble d'animations de personnages faisant face dans une direction et les faire pivoter pour toutes les autres. Pour l’art des personnages isométriques, nous devons retransformer chaque animation dans chacune des directions autorisées. Par conséquent, pour un mouvement à huit directions, nous devons créer huit animations pour chaque action. Pour faciliter la compréhension, nous désignons généralement les directions Nord, Nord-Ouest, Ouest, Sud-Ouest, Sud, Sud-Est, Est et Nord-Est, dans le sens inverse des aiguilles d'une montre, dans cet ordre.

Un personnage isométrique faisant face dans différentes directions.

Nous plaçons les personnages de la même manière que nous plaçons les tuiles. Le mouvement d'un caractère est accompli en calculant le mouvement en coordonnées cartésiennes puis en le convertissant en coordonnées isométriques. Supposons que nous utilisons le clavier pour contrôler le personnage.

Nous allons définir deux variables, dX et dY, sur la base des touches directionnelles appuyées. Par défaut, ces variables seront 0, et sera mis à jour selon le tableau ci-dessous, où U, , R et L dénoter le Up, Vers le bas, Droite et La gauche touches fléchées, respectivement. Une valeur de 1 sous une touche représente que cette touche est enfoncée; 0 implique que la touche n'est pas enfoncée.

 Clé Pos UDRL dX dY =============== 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 -1 0 1 0 1 0 1 1 0 0 1 1 -1 1 0 1 1 0 1 0 1 1 0 1

Maintenant, en utilisant les valeurs de dX et dY, nous pouvons mettre à jour les coordonnées cartésiennes comme suit:

newX = currentX + (dX * speed); newY = currentY + (dY * speed);

Alors dX et dY représenter le changement dans les positions x et y du caractère, en fonction des touches enfoncées.

Nous pouvons facilement calculer les nouvelles coordonnées isométriques, comme nous l'avons déjà expliqué:

Iso = twoDToIso (nouveau Point (newX, newY))

Une fois que nous avons la nouvelle position isométrique, nous devons bouge toi le personnage à cette position. Sur la base des valeurs que nous avons pour dX et dY, nous pouvons décider de la direction à laquelle le personnage fait face et utiliser l'art du personnage correspondant.

Détection de collision

La détection de collision est effectuée en vérifiant si la mosaïque à la nouvelle position calculée est une mosaïque qui ne peut pas être parcourue. Donc, une fois que nous avons trouvé la nouvelle position, nous ne déplaçons pas immédiatement le personnage là-bas, mais nous vérifions d'abord quelle mosaïque occupe cet espace..

coordonnée de tuile = getTileCoordinates (isoTo2D (point iso), hauteur de tuile); if (isWalkable (coordonnée de tuile)) moveCharacter ();  else // ne fait rien; 

Dans la fonction isWalkable (), nous vérifions si la valeur du tableau de données de niveau à la coordonnée donnée est une tuile accessible à pied ou non. Nous devons prendre soin de mettre à jour la direction dans laquelle le personnage est confronté - même s'il ne bouge pas, comme dans le cas de lui frapper une tuile non praticable.

Tri en profondeur avec caractères

Considérons un personnage et une tuile d'arbre dans le monde isométrique.

Pour bien comprendre le tri en profondeur, nous devons comprendre que chaque fois que les coordonnées x et y du caractère sont inférieures à celles de l’arbre, l’arbre chevauche le caractère. À chaque fois que les coordonnées x et y du caractère sont supérieures à celles de l'arbre, il se superpose à l'arbre..

Quand ils ont la même coordonnée x, nous décidons uniquement en fonction de la coordonnée y: celle qui a la plus haute coordonnée chevauche l’autre. Quand ils ont la même coordonnée y, nous décidons en fonction de la coordonnée x seule: celle qui a la coordonnée x la plus élevée chevauche l’autre.

Une version simplifiée consiste à dessiner séquentiellement les niveaux à partir de la tuile la plus éloignée - c’est-à-dire, dalle [0] [0] - dessinez ensuite toutes les tuiles de chaque rangée une par une. Si un personnage occupe une tuile, nous dessinons d'abord la tuile terre, puis nous rendons la tuile du personnage. Cela fonctionnera bien, car le personnage ne peut pas occuper une dalle murale..

Le tri en profondeur doit être effectué chaque fois qu'une des tuiles change de position. Par exemple, nous devons le faire chaque fois que les personnages bougent. Nous mettons ensuite à jour la scène affichée, après avoir effectué le tri de profondeur, pour refléter les changements de profondeur..


6. essayer!

Utilisez maintenant vos nouvelles connaissances en créant un prototype fonctionnel, avec des commandes au clavier, un tri en profondeur et une détection des collisions. Voici ma démo:

Cliquez pour activer le fichier SWF, puis utilisez les touches de direction. Cliquez ici pour la version complète.

Vous pouvez trouver cette classe d’utilité utile (je l’ai écrite en AS3, mais vous devriez pouvoir la comprendre dans n’importe quel autre langage de programmation):

package com.csharks.juwalbose import flash.display.Sprite; import flash.geom.Point; public class IsoHelper / ** * convertit un point isométrique en 2D * * / fonction statique publique isoTo2D (pt: Point): Point // gx = (2 * isoy + isox) / 2; // gy = (2 * isoy-isox) / 2 var tempPt: Point = nouveau Point (0,0); tempPt.x = (2 * pt.y + pt.x) / 2; tempPt.y = (2 * pt.y-pt.x) / 2; return (tempPt);  / ** * convertir un point 2d en fonction statique isométrique * * / public static twoDToIso (pt: Point): Point // gx = (isox-isoxy; // gy = (isoy + isox) / 2 var tempPt: Point = nouveau point (0,0); tempPt.x = pt.x-pt.y; tempPt.y = (pt.x + pt.y) / 2; return (tempPt); / ** * convertir un 2d pointez sur une rangée / colonne de tuile spécifique * * / fonction statique publique getTileCoordinates (pt: Point, tileHeight: Number): Point var tempPt: Point = nouveau Point (0,0); tempPt.x = Math.floor (pt.x / tileHeight); tempPt.y = Math.floor (pt.y / tileHeight); return (tempPt); / ** * convertir une rangée / colonne en mosaïque spécifique en 2d point * * / fonction statique publique get2dFromTileCoordinates (pt: Point, tileHeight: Number): Point var tempPt: Point = nouveau Point (0,0); tempPt.x = pt.x * tileHeight; tempPt.y = pt.y * tileHeight; return (tempPt);

Si vous êtes vraiment bloqué, voici le code complet de ma démo (sous forme de code de chronologie Flash et AS3):

// Utilise la classe KeyObject de senocular // http://www.senocular.com/flash/actionscript/?file=ActionScript_3.0/com/senocular/utils/KeyObject.as import flash.display.Sprite; importer des fichiers com.csharks.juwalbose.IsoHelper; import flash.display.MovieClip; import flash.geom.Point; import flash.filters.GlowFilter; import flash.events.Event; import com.senocular.utils.KeyObject; importer flash.ui.Keyboard; import flash.display.Bitmap; import flash.display.BitmapData; import flash.geom.Matrix; import flash.geom.Rectangle; var levelData = [[1,1,1,1,1,1], [1,0,0,2,0,1], [1,0,1,0,0,1], [1,0 , 0,0,0,1], [1,0,0,0,0,1], [1,1,1,1,1,1]]; var tileWidth: uint = 50; var borderOffsetY: uint = 70; var borderOffsetX: uint = 275; var face: String = "south"; var currentFacing: String = "south"; var hero: MovieClip = new herotile (); hero.clip.gotoAndStop (en regard); var heroPointer: Sprite; var key: KeyObject = new KeyObject (stage); // Classe KeyObject Sénoculaire var heroHalfSize: uint = 20; // les tuiles var grassTile: MovieClip = new TileMc (); grassTile.gotoAndStop (1); var wallTile: MovieClip = new TileMc (); wallTile.gotoAndStop (2); // la toile var bg: Bitmap = new Bitmap (new BitmapData (650,450)); addChild (bg); var rect: Rectangle = bg.bitmapData.rect; // pour gérer la profondeur var overlayContainer: Sprite = new Sprite (); addChild (overlayContainer); // pour gérer le mouvement de direction var dX: Number = 0; var dY: nombre = 0; var idle: Boolean = true; vitesse var: uint = 5; var heroCartPos: Point = nouveau Point (); var heroTile: Point = nouveau Point (); // ajouter des éléments au niveau de départ, ajouter une fonction de boucle de jeu createLevel () var tileType: uint; pour (var i: uint = 0; i 

Points d'inscription

Accordez une attention particulière aux points d’enregistrement des tuiles et du héros. (Les points d'enregistrement peuvent être considérés comme les points d'origine de chaque sprite en particulier.) Ceux-ci ne tombent généralement pas dans l'image, mais se situent plutôt dans le coin supérieur gauche du cadre de sélection du sprite..

Nous devrons modifier notre code de dessin pour fixer correctement les points d’enregistrement, principalement pour le héros..

Détection de collision

Un autre point intéressant à noter est que nous calculons la détection de collision en fonction du point où le héros est.

Mais le héros a du volume et ne peut pas être représenté avec précision par un seul point. Nous devons donc représenter le héros sous forme de rectangle et vérifier les collisions à chaque coin de ce rectangle afin d'éviter tout chevauchement avec d'autres tuiles et donc aucun artefact de profondeur..

Raccourcis

Dans la démo, je redessine simplement la scène chaque image en fonction de la nouvelle position du héros. Nous trouvons la tuile occupée par le héros et dessinons le héros au-dessus de la tuile au sol lorsque les boucles de rendu atteignent ces tuiles..

Mais si nous regardons de plus près, nous constaterons qu'il n'est pas nécessaire de parcourir toutes les tuiles dans ce cas. Les tuiles d'herbe et les tuiles des murs supérieur et gauche sont toujours dessinées avant que le héros ne soit dessiné. Nous n'avons donc pas besoin de les redessiner du tout. De plus, les carreaux muraux inférieur et droit sont toujours devant le héros et sont donc dessinés. après le héros est dessiné.

Dans l’essentiel, il suffit donc d’effectuer un tri en profondeur entre le mur de la zone active et le héros, c’est-à-dire deux tuiles. Le fait de noter ces types de raccourcis vous permettra d’économiser beaucoup de temps de traitement, ce qui peut être crucial pour la performance..


Conclusion

A présent, vous devriez avoir une bonne base pour créer vos propres jeux isométriques: vous pouvez restituer le monde et ses objets, représenter des données de niveau dans de simples tableaux 2D, convertir des coordonnées cartésiennes et isométriques et gérer des concepts tels que le tri en profondeur. et animation de personnage. Profitez de la création de mondes isométriques!

Articles Similaires
  • Création de mondes isométriques: guide d'introduction aux jeux sur ordinateur, suite
  • Conseil éclair: Niveaux isométriques bon marché et faciles
  • Tuiles isométriques Math
  • 6 guides incroyablement détaillés sur le développement et la conception de jeux pour les débutants