Dans ce didacticiel, nous allons convertir un jeu Sokoban 2D basé sur des carreaux en vues isométrique et hexagonale. Si vous êtes novice dans les jeux isométriques ou hexagonaux, il peut être difficile au premier abord d’essayer de les suivre simultanément. Dans ce cas, je vous recommande de choisir d'abord l'isométrie, puis de revenir ultérieurement pour la version hexagonale..
Nous allons nous appuyer sur le didacticiel précédent d'Unity: le jeu Sokoban basé sur les tuiles 2D Unity. Veuillez commencer par le didacticiel, car la plupart du code reste inchangé et tous les concepts de base restent les mêmes. Je vais également créer un lien vers d'autres tutoriels expliquant certains des concepts sous-jacents.
L’aspect le plus important dans la création de versions isométriques ou hexagonales à partir d’une version 2D consiste à déterminer le positionnement des éléments. Nous allons utiliser des méthodes de conversion basées sur des équations pour convertir entre les différents systèmes de coordonnées.
Ce tutoriel comporte deux sections, l’une pour la version isométrique et l’autre pour la version hexagonale..
Passons directement à la version isométrique une fois que vous avez parcouru le didacticiel d'origine. L'image ci-dessous montre à quoi ressemblerait la version isométrique, à condition que nous utilisions les mêmes informations de niveau que dans le didacticiel d'origine..
La théorie isométrique, l'équation de conversion et la mise en œuvre sont expliquées dans plusieurs tutoriels sur Envato Tuts +. Une ancienne explication basée sur Flash peut être trouvée dans ce tutoriel détaillé. Je recommanderais ce tutoriel basé sur Phaser car il est plus récent et plus évolué.
Bien que les langages de script utilisés dans ces didacticiels soient respectivement ActionScript 3 et JavaScript, la théorie est applicable partout, indépendamment des langages de programmation. Cela revient essentiellement à ces équations de conversion qui doivent être utilisées pour convertir les coordonnées cartésiennes 2D en coordonnées isométriques ou inversement.
// 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;
Nous allons utiliser la fonction suivante d'Unity pour la conversion en coordonnées isométriques.
Vector2 CartesianToIsometric (Vector2 cartPt) Vector2 tempPt = new Vector2 (); tempPt.x = cartPt.x-cartPt.y; tempPt.y = (cartPt.x + cartPt.y) / 2; return (tempPt);
Nous allons utiliser les mêmes informations de niveau pour créer notre tableau 2D, levelData
, qui conduira la représentation isométrique. La plupart du code restera également le même, autre que celui spécifique à la vue isométrique.
L'art, cependant, doit avoir quelques changements en ce qui concerne les points de pivot. S'il vous plaît se référer à l'image ci-dessous et l'explication qui suit.
le IsometricSokoban
le script de jeu utilise des sprites modifiés comme heroSprite
, ballSprite
, et blockSprite
. L'image montre les nouveaux points de pivot utilisés pour ces sprites. Cette modification donne l'aspect pseudo 3D que nous visons avec la vue isométrique. BlockSprite est un nouveau sprite que nous ajoutons lorsque nous trouvons un invalidTile
.
Cela m'aidera à expliquer l'aspect le plus important des jeux isométriques, le tri en profondeur. Bien que le sprite ne soit qu'un hexagone, nous le considérons comme un cube 3D dont le pivot est situé au centre de la face inférieure du cube..
Veuillez télécharger le code partagé via le référentiel git lié avant de poursuivre. le CreateLevel
La méthode comporte quelques modifications relatives à l’échelle et au positionnement des carreaux et à l’ajout du blockTile
. L'échelle de la carrelage
, qui est juste une image en forme de losange représentant notre carreau de sol, doit être modifié comme ci-dessous.
tile.transform.localScale = new Vector2 (tileSize-1, (tileSize-1) / 2); // la taille est essentielle pour la forme isométrique
Cela reflète le fait qu'une tuile isométrique aura une hauteur de la moitié de sa largeur. le heroSprite
et le ballSprite
avoir une taille de Taille du carreau / 2
.
hero.transform.localScale = Vector2.one * (tileSize / 2); // nous utilisons la moitié de la taille de la tuile pour les occupants
Partout où nous trouvons un invalidTile
, nous ajoutons un blockTile
en utilisant le code suivant.
tile = new GameObject ("block" + i.ToString () + "_" + j.ToString ()); // créer une nouvelle tuile float rootThree = Mathf.Sqrt (3); float newDimension = 2 * tileSize / rootThree; tile.transform.localScale = new Vector2 (newDimension, tileSize); // nous devons définir une hauteur sr = tile.AddComponent(); // ajouter un moteur de rendu de sprite sr.sprite = blockSprite; // affecter un sprite de bloc sr.sortingOrder = 1; // cela nécessite également un ordre de tri plus élevé Color c = Color.gray; c.a = 0,9f; sr.color = c; tile.transform.position = GetScreenPointFromLevelIndices (i, j); // place dans la scène en fonction des indices de niveau occupants.Add (tile, new Vector2 (i, j)); // stocke les indices de niveau de bloc dans dict
L'hexagone doit être mis à l'échelle différemment pour obtenir l'aspect isométrique. Ce ne sera pas un problème lorsque l’art est traité par des artistes. Nous appliquons une valeur alpha légèrement inférieure à la blockSprite
afin que nous puissions voir à travers, ce qui nous permet de voir le tri en profondeur correctement. Notez que nous ajoutons ces tuiles à la les occupants
dictionnaire également, qui sera utilisé plus tard pour le tri en profondeur.
Le positionnement des carreaux se fait à l'aide du bouton GetScreenPointFromLevelIndices
méthode, qui utilise à son tour le Cartésien to isométrique
méthode de conversion expliquée plus tôt. le Y
axe pointe dans la direction opposée pour Unity, ce qui doit être pris en compte tout en ajoutant le middleOffset
pour positionner le niveau au milieu de l'écran.
Vector2 GetScreenPointFromLevelIndices (int row, int col) // conversion d'index en valeurs de position, col détermine x & row détermine y Vector2 tempPt = CartesianToIsometric (nouveau Vector2 (col * tileSize / 2, row * tileSize / 2)); // supprimé le '-' dans la partie y car la correction de l'axe peut se produire après la couverture tempPt.x- = middleOffset.x; // nous appliquons le décalage en dehors de la conversion de coordonnées pour aligner le niveau dans l'écran tempPt.y * = - 1; // unité y correction d'axe tempPt.y + = middleOffset.y; // nous appliquons le décalage en dehors de la conversion de coordonnées pour aligner le niveau dans le retour moyen de l'écran tempPt;
À la fin de CreateLevel
méthode ainsi qu'à la fin de la TryMoveHero
méthode, nous appelons le DepthSort
méthode. Le tri en profondeur est l’aspect le plus important d’une implémentation isométrique. Essentiellement, nous déterminons quelles tuiles vont derrière ou devant les autres tuiles du niveau. le DepthSort
la méthode est comme indiqué ci-dessous.
void privé DepthSort () int depth = 1; SpriteRenderer sr; Vecteur2 pos = nouveau vecteur2 (); pour (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) int val=levelData[i,j]; if(val!=groundTile && val!=destinationTile)//a tile which needs depth sorting pos.x=i; pos.y=j; GameObject occupant=GetOccupantAtPosition(pos);//find the occupant at this position if(occupant==null)Debug.Log("no occupant"); sr=occupant.GetComponent(); sr.sortingOrder = profondeur; // attribue une nouvelle profondeur profondeur ++; // incrémente profondeur
La beauté d'une implémentation basée sur un tableau 2D réside dans le fait que pour un tri de profondeur isométrique approprié, il suffit d'attribuer une profondeur plus élevée de manière séquentielle tout en analysant le niveau dans l'ordre, en utilisant des boucles séquentielles. Cela fonctionne pour notre niveau simple avec une seule couche de sol. Si nous avions plusieurs niveaux de sol à différentes hauteurs, le tri en profondeur pourrait se compliquer.
Tout le reste reste identique à l'implémentation 2D expliquée dans le tutoriel précédent..
Vous pouvez utiliser les mêmes commandes du clavier pour jouer au jeu. La seule différence est que le héros ne bougera pas verticalement ou horizontalement, mais isométriquement. Le niveau fini ressemblerait à l'image ci-dessous.
Découvrez comment le tri en profondeur est clairement visible avec notre nouveau blockTiles
.
Ce n'était pas difficile, n'est-ce pas? Je vous invite à modifier les données de niveau dans le fichier texte pour essayer de nouveaux niveaux. La prochaine version est la version hexagonale, qui est un peu plus compliquée, et je vous conseillerais de faire une pause pour jouer avec la version isométrique avant de continuer..
La version hexagonale du niveau Sokoban ressemblerait à l'image ci-dessous.
Nous utilisons l'alignement horizontal pour la grille hexagonale pour ce tutoriel. La théorie derrière la mise en œuvre hexagonale nécessite beaucoup de lecture supplémentaire. Veuillez vous référer à cette série de tutoriels pour une compréhension de base. La théorie est implémentée dans la classe d'assistance HexHelperHorizontal
, qui peut être trouvé dans le utils
dossier.
le HexagonalSokoban
game script utilise des méthodes pratiques de la classe d'assistance pour les conversions de coordonnées et d'autres fonctions hexagonales. La classe d'assistance HexHelperHorizontal
fonctionnera uniquement avec une grille hexagonale alignée horizontalement. Il comprend des méthodes pour convertir les coordonnées entre les systèmes offset, axial et cubique.
La coordonnée de décalage est la même coordonnée cartésienne 2D. Il comprend également un obtenir les voisins
méthode, qui prend une coordonnée axiale et renvoie un liste
avec tous les six voisins de cette coordonnée de cellule. L'ordre de la liste est dans le sens des aiguilles d'une montre, en commençant par la coordonnée de cellule du voisin nord-est..
Avec une grille hexagonale, nous avons six directions de mouvement au lieu de quatre, car l'hexagone a six côtés alors qu'un carré en a quatre. Nous avons donc six touches du clavier pour contrôler le mouvement de notre héros, comme le montre l'image ci-dessous..
Les touches sont disposées de la même manière qu'une grille hexagonale si vous considérez la touche du clavier S
comme la cellule du milieu, avec toutes les touches de contrôle comme ses voisins hexagonaux. Cela aide à réduire la confusion avec le contrôle du mouvement. Les modifications correspondantes du code d'entrée sont les suivantes:.
void privé ApplyUserInput () // nous avons 6 directions de mouvement contrôlées par e, d, x, z, a, w dans une séquence cyclique commençant par NE à NW si (Input.GetKeyUp (userInputKeys [0])) TryMoveHero (0); // nord est else if (Input.GetKeyUp (userInputKeys [1])) TryMoveHero (1); // est sinon if (Input.GetKeyUp (userInputKeys [2])) TryMoveHero (2) ; // south east else if (Input.GetKeyUp (userInputKeys [3])) TryMoveHero (3); // sud ouest else if (Input.GetKeyUp (userInputKeys [4])) TryMoveHero (4); / / west else if (Input.GetKeyUp (userInputKeys [5])) TryMoveHero (5); // nord ouest
Il n'y a pas de changement dans l'art, et il n'y a pas de changements de pivot nécessaires.
Je vais expliquer les modifications de code par rapport au didacticiel Sokoban 2D original et non à la version isométrique ci-dessus. Veuillez vous référer au code source lié pour ce tutoriel. Le fait le plus intéressant est que presque tout le code reste le même. le CreateLevel
méthode n’a qu’une modification, qui est la middleOffset
calcul.
middleOffset.x = cols * tileWidth + tileWidth * 0.5f; // ceci est modifié pour l'hexagone middleOffset.y = rows * tileSize * 3/4 + tileSize * 0.75f; // ceci est modifié pour l'isométrique
Un changement majeur est évidemment la façon dont les coordonnées de l'écran se trouvent dans le GetScreenPointFromLevelIndices
méthode.
Vector2 GetScreenPointFromLevelIndices (int row, int col) // conversion d'index en valeurs de position, col détermine x & row détermine y Vector2 tempPt = new Vector2 (row, col); tempPt = HexHelperHorizontal.offsetToAxial (tempPt); // conversion de décalage en axial // conversion de point axial en point d'écran tempPt = HexHelperHorizontal.axialToScreen (tempPt, sideLength); tempPt.x- = middleOffset.x-Screen.width / 2; // ajoute des décalages pour l'alignement moyen tempPt.y * = - 1; // unité de correction d'axe y tempPt.y + = middleOffset.y-Screen.height / 2; return tempPt;
Ici, nous utilisons la classe d'assistance pour convertir d'abord la coordonnée en axiale puis pour trouver la coordonnée d'écran correspondante. Veuillez noter l'utilisation de la longueur du côté
variable pour la seconde conversion. C'est la valeur de la longueur d'un côté de la tuile hexagone, qui est encore égale à la moitié de la distance entre les deux extrémités pointues de l'hexagone. Par conséquent:
sideLength = tileSize * 0.5f;
Le seul autre changement est le GetNextPositionAlong
méthode, qui est utilisée par le TryMoveHero
méthode pour trouver la prochaine cellule dans une direction donnée. Cette méthode a été complètement modifiée pour s'adapter à la nouvelle présentation de notre grille..
private Vector2 GetNextPositionAlong (Vector2 objPos, direction int) // cette méthode est complètement modifiée pour prendre en compte les différentes façons dont les voisins se trouvent dans la logique hexagonale objPos = HexHelperHorizontal.offsetToAxial (objPos); // conversion de offset en axialevoisins = HexHelperHorizontal.getNeothers (objPos); objPos = voisins [direction]; // la liste de voisins suit le même ordre. objPos = HexHelperHorizontal.axialToOffset (objPos); // reconvertit de retour axial à offset objPos;
En utilisant la classe d'assistance, nous pouvons facilement renvoyer les coordonnées du voisin dans la direction donnée.
Tout le reste reste identique à l'implémentation 2D originale. Ce n'était pas difficile, n'est-ce pas? Cela étant dit, comprendre comment nous sommes parvenus aux équations de conversion en suivant le didacticiel hexagonal, qui constitue le noeud de tout le processus, n'est pas facile. Si vous jouez et terminez le niveau, vous obtiendrez le résultat ci-dessous.
L'élément principal dans les deux conversions était les conversions de coordonnées. La version isométrique implique des modifications supplémentaires dans la technique avec leur point de pivot ainsi que la nécessité d'un tri en profondeur..
Je pense que vous avez trouvé à quel point il est facile de créer des jeux basés sur une grille en utilisant uniquement des données de niveau à base de tableaux bidimensionnelles et une approche basée sur des tuiles. Il existe des possibilités illimitées et des jeux que vous pouvez créer avec cette nouvelle compréhension..
Si vous avez compris tous les concepts dont nous avons discuté jusqu'à présent, je vous inviterais à changer la méthode de contrôle pour y puiser et ajouter une recherche de chemin. Bonne chance.