Une méthode simple pour afficher une grille 2D en 3D isométrique, avec le moteur de jeu Löve 2D. Le but est de passer d’une grille au format tableau à ce type de rendu :

La logique
Quand nous dessinons une grille en vu du dessus, les cellules sont affichées côte à côte, comme Advance wars ou Fire emblem. Sur une vue en 3D isométrique, FF Tactics ou Tactics Ogre, les cellules sont affichées avec un léger décalage de leur axe X et Y. Les tiles au format 3D isométrique sont des cases carrées classiques, avec des zones en transparence permettant leur superposition.

Les cases sont décalées de la moitié de leur taille sur l’axe X (de gauche à droite) et d’un quart de leur taille sur l’axe Y (vers le bas).

L’implémentation
Pré-requis
La fonction
fromImageToQuadsdans le fichierutils.tsest une implémentation simple d’un système de tileset. Le tutoriel n’abordant ce sujet, je ne décrirais pas son fonctionnement.
├── assets
│ └── sprite.png
├── conf.lua
├── main.lua
└── utils.luaArborescence
function love.conf(t)
t.version = "11.5" -- The LÖVE version this game was made for (string)
t.window.title = "iso tutoriel" -- The window title (string)
t.window.icon = nil -- Filepath to an image to use as the window's icon (string)
t.window.width = 260 -- The window width (number)
t.window.height = 260 -- The window height (number)
endconfig.ts
function fromImageToQuads(tilesheet, tileWidth, tileHeight)
local tiles = {}
local imageWidth = tilesheet:getWidth()
local imageHeight = tilesheet:getHeight()
for i = 0, imageHeight - 1, tileHeight do
for j = 0, imageWidth - 1, tileWidth do
table.insert(
tiles,
love.graphics.newQuad(
j, i, tileWidth, tileHeight, imageWidth, imageHeight
)
)
end
end
return tiles
endutils.ts
sprite.png
![]()
Le code
function love.load()
require "utils"
love.graphics.setDefaultFilter("nearest") -- no bluring on tiles
WINDOWWIDTH, WINDOWHEIGHT = love.window.getMode()
TILESIZE = 32
Tilesheet = love.graphics.newImage('assets/sprite.png')
Tiles = fromImageToQuads(Tilesheet, 32, 32)
end
function love.update(dt)
end
function love.draw()
love.graphics.scale(2, 2) -- zoom x2
love.graphics.translate(WINDOWWIDTH/4-16,WINDOWHEIGHT/4-32) -- Center map
endmain.lua
On commence par écrire notre template de base avec tout ce qu’il nous faut pour commencer :
- La fonction
setDefaultFilter()va changer le filtre appliqué sur les tiles pour éviter un rendu flou - Les constantes
WINDOWWIDTHetWINDOWHEIGHTseront utilisées dans la fonctiontranslate()pour centrer notre rendu - Les variables
TilesheetetTilessont initialisées avec notre Tilesheet et les quads correspondant (Plus d’info…) - La fonction
scale()va “agrandir” nos assets 32x32 en 64x64
Ensuite, nous allons initialiser notre grille dans un tableau suivant notre tilesheet :

Pour rappel, l’index des tableaux en Lua commence par 1 et non 0. La premiere tile a donc l’index 1.
function love.load()
...
GRID = {
{ 2, 2, 2 },
{ 2, 2, 7 },
{ 2, 2, 1 }
}
endmain.lua
Maintenant nous allons devoir parcourir le tableau pour afficher une par une les cellules de notre grille. Habituellement, nous définirions l’axe X et Y de nos cases par ce genre de formule : x = X_index * 32, y = Y_index * 32. Ainsi, la cellule {1,1} serait affichée aux coordonnées x=32,y=32, la {0,2} a x=0,y=64, etc…
Dans notre cas, nous allons devoir prendre en compte le décalage sur les axes X et Y (voir commentaire du code).
function love.draw()
...
for x, table in ipairs(GRID) do
for y, cell in ipairs(table) do
local isox = 0
- (x * (TILESIZE / 2)) -- On décale la coordonnées X vers la gauche suivant l'index X de la case dans le tableau
+ (y * (TILESIZE / 2)) -- On décale la coordonnées X vers la droite suivant l'index Y de la case dans le tableau
local isoy = 0
+ (x * (TILESIZE / 4)) -- On décale la coordonnées Y vers le bas suivant l'index X de la case dans le tableau
+ (y * (TILESIZE / 4)) -- On décale la coordonnées Y vers le bas suivant l'index Y de la case dans le tableau
local tileNumber = cell
love.graphics.draw(Tilesheet, Tiles[tileNumber], isox, isoy)
end
end
endmain.lua
A ce stade, nous devrions avoir ce genre de rendu :

Maintenant nous avons besoin de rajouter la notion de niveau, l’équivalent d’un axe Z en 3D. Nous allons modifier notre grille en conséquence, en ajoutant un troisième niveau servant de calque.
function love.load()
...
GRID = {
{
{ 2, 2, 2 },
{ 2, 2, 7 },
{ 2, 2, 1 }
},
{
{ 5, 4, 4 },
{ 3, 1, 1 },
{ 3, 8, 1 }
}
}
endmain.lua
Pour afficher notre second calque, nous allons décaler l’axe de chaque case. Cette fois l’axe Y, de moitié vers le haut, selon la position de son calque.
function love.draw()
...
for index, layer in ipairs(GRID) do
for x, table in ipairs(layer) do
for y, cell in ipairs(table) do
local isox = 0
- (x * (TILESIZE / 2))
+ (y * (TILESIZE / 2))
local isoy = 0
+ (x * (TILESIZE / 4))
+ (y * (TILESIZE / 4))
local tileNumber = cell
love.graphics.draw(Tilesheet, Tiles[tileNumber], isox, isoy - ((index - 1) * (TILESIZE / 2)))
end
end
end
endmain.ts
Et voici le rendu final :
