Tutorial:Efficient Tile-based Scrolling (Français)
Ce tutoriel introduit la classe SpriteBatch(lot de sprites) pour un système de défilement basé sur des tuiles plus efficace. Pour un tutoriel sur un tel système de défilement plus basique, voir Tutorial:Tile-based_Scrolling.
Contents
Quads et SpriteBatch
La méthode love.graphics.draw peut dessiner une portion d'image spécifiée par un Quad. Si des Quads sont dessinés à partir de différentes parties d'une image unique, on peut render le système plus efficace en utilisant un SpriteBatch et en ne changeant pas les Quads à chaque frame.
Lorsqu'on créé un SpriteBatch, on doit spécifier l'image à partir de laquelle on prend des Quads, et le nombre maximum de Quads que l'on y ajoutera. En l’occurrence, ça sera le nombre maximum de tuiles visible en même temps sur l'écran.
tilesetBatch = love.graphics.newSpriteBatch(tilesetImage, tilesDisplayWidth * tilesDisplayHeight)
Initialisation de la carte
On initialise une carte avec le code suivant. Voir le Tutoriel de défilement basé sur des tuiles pour une explication
function love.load()
mapWidth = 60
mapHeight = 40
map = {}
for x=1,mapWidth do
map[x] = {}
for y=1,mapHeight do
map[x][y] = love.math.random(0,3)
end
end
mapX = 1
mapY = 1
tilesDisplayWidth = 26
tilesDisplayHeight = 20
zoomX = 1
zoomY = 1
end
Ajouter un SpriteBatch comme Tileset
Ensuite, nous chargeons un tileset, on créé les quads pour les tuiles que l'on souhaite utiliser, et on créé un objet Spritebatch pour contenir les tuiles. Comme exemple, nous allons utiliser un tilset gratuit de http://silveiraneto.net/....
function love.load()
... -- initialization de la carte
tilesetImage = love.graphics.newImage( "tileset.png" )
tilesetImage:setFilter("nearest", "linear") -- this "linear filter" removes some artifacts if we were to scale the tiles
tileSize = 32
-- l'herbe
tileQuads[0] = love.graphics.newQuad(0 * tileSize, 20 * tileSize, tileSize, tileSize,
tilesetImage:getWidth(), tilesetImage:getHeight())
-- carrelage de cuisine
tileQuads[1] = love.graphics.newQuad(2 * tileSize, 0 * tileSize, tileSize, tileSize,
tilesetImage:getWidth(), tilesetImage:getHeight())
-- parquet
tileQuads[2] = love.graphics.newQuad(4 * tileSize, 0 * tileSize, tileSize, tileSize,
tilesetImage:getWidth(), tilesetImage:getHeight())
-- milieu de tapis rouge
tileQuads[3] = love.graphics.newQuad(3 * tileSize, 9 * tileSize, tileSize, tileSize,
tilesetImage:getWidth(), tilesetImage:getHeight())
tilesetBatch = love.graphics.newSpriteBatch(tilesetImage, tilesDisplayWidth * tilesDisplayHeight)
end
On souhaite seulement ajouter au SpriteBatch les tuiles qui sont présentement visible. Pour faire cela, on créé une fonction qui met à jour le tileset et on l'appelle dès que la vue de la carte change. On l'appelle aussi une fois à l'initialisation
function updateTilesetBatch()
tilesetBatch:clear()
for x=0, tilesDisplayWidth-1 do
for y=0, tilesDisplayHeight-1 do
tilesetBatch:add(tileQuads[map[x+mapX][y+mapY]], x*tileSize, y*tileSize)
end
end
tilesetBatch:flush()
end
Enfin, pour dessiner le SpriteBatch, on l'envoie juste à love.graphics.draw
.
function love.draw()
love.graphics.draw(tilesetBatch)
end
Déplacement discret (discontinu)
Le déplacement sur la carte est effectué de la même façon que dans le Tutoriel de défilement basé sur des tuiles; On vérifie si une touche est enfoncée et on met à jour la carte en conséquence. On doit aussi se souvenir de mettre à jour le SpriteBatch.
-- central function for moving the map
function moveMap(dx, dy)
oldMapX = mapX
oldMapY = mapY
mapX = math.max(math.min(mapX + dx, mapWidth - tilesDisplayWidth), 1)
mapY = math.max(math.min(mapY + dy, mapHeight - tilesDisplayHeight), 1)
-- only update if we actually moved
if math.floor(mapX) ~= math.floor(oldMapX) or math.floor(mapY) ~= math.floor(oldMapY) then
updateTilesetBatch()
end
end
function love.keypressed(key)
if key == "up" then
moveMap(0, -1)
end
if key == "down" then
moveMap(0, 1)
end
if key == "left" then
moveMap(-1, 0)
end
if key == "right" then
moveMap(1, 0)
end
end
Mouvement continu
On peut render le mouvement un peu plus agréable en laissant mapX et mapY prendre des valeurs non-entières. Quand on ajoutera des Quads au SpriteBatch, on prendra seulement en compte la partie entière tandis que l'affichage décalera le SpriteBatch to prendre en compte la partie fractionnaire. On replace le callback love.keypressed avec un callback love.update et on déplace la carte par petits pas.
function love.update(dt)
if love.keyboard.isDown("up") then
moveMap(0, -0.2 * tileSize * dt)
end
if love.keyboard.isDown("down") then
moveMap(0, 0.2 * tileSize * dt)
end
if love.keyboard.isDown("left") then
moveMap(-0.2 * tileSize * dt, 0)
end
if love.keyboard.isDown("right") then
moveMap(0.2 * tileSize * dt, 0)
end
end
On ajoute un appel de la fonction partie entière (floor) à la mise à jour du SpriteBatch.
function updateTilesetBatch()
tilesetBatch:clear()
for x=0, tilesDisplayWidth-1 do
for y=0, tilesDisplayHeight-1 do
tilesetBatch:add(tileQuads[map[x+math.floor(mapX)][y+math.floor(mapY)]],
x*tileSize/2, y*tileSize/2)
end
end
tilesetBatch:flush()
end
Enfin, on décale le SpriteBatch par la partie fractionnaire.
function love.draw()
love.graphics.draw(tilesetBatch,
math.floor(-(mapX%1)*tileSize), math.floor(-(mapY%1)*tileSize))
love.graphics.print("FPS: "..love.timer.getFPS(), 10, 20)
end
Mise en commun
On a également ajouté des variables zoomX et zoomY dans ce code pour permettre, par exemple, de dessiner des tuiles de 16x16 en 32x32
local map -- stock les données des tuiles de la carte
local mapWidth, mapHeight -- largeur et hauteur de la carte en tuiles
local mapX, mapY -- vue x,y en tuiles. Peut aussi être une valeur fractionnaire comme 3.25.
local tilesDisplayWidth, tilesDisplayHeight -- Nombre de tuiles à afficher
local zoomX, zoomY
local tilesetImage
local tileSize -- taille des tuiles en pixels
local tileQuads = {} -- parties du tileset utilisées pour différentes tuiles
local tilesetSprite
function love.load()
setupMap()
setupMapView()
setupTileset()
love.graphics.setFont(12)
end
function setupMap()
mapWidth = 60
mapHeight = 40
map = {}
for x=1,mapWidth do
map[x] = {}
for y=1,mapHeight do
map[x][y] = love.math.random(0,3)
end
end
end
function setupMapView()
mapX = 1
mapY = 1
tilesDisplayWidth = 26
tilesDisplayHeight = 20
zoomX = 1
zoomY = 1
end
function setupTileset()
tilesetImage = love.graphics.newImage( "tileset.png" )
tilesetImage:setFilter("nearest", "linear") -- ce filtre "linear" permet d'enlever des artefacts visuels si on changeait l'échelle des tuiles.
tileSize = 32
-- l'herbe
tileQuads[0] = love.graphics.newQuad(0 * tileSize, 20 * tileSize, tileSize, tileSize,
tilesetImage:getWidth(), tilesetImage:getHeight())
-- carrelage de cuisine
tileQuads[1] = love.graphics.newQuad(2 * tileSize, 0 * tileSize, tileSize, tileSize,
tilesetImage:getWidth(), tilesetImage:getHeight())
-- parquet
tileQuads[2] = love.graphics.newQuad(4 * tileSize, 0 * tileSize, tileSize, tileSize,
tilesetImage:getWidth(), tilesetImage:getHeight())
-- milieu de tapis rouge
tileQuads[3] = love.graphics.newQuad(3 * tileSize, 9 * tileSize, tileSize, tileSize,
tilesetImage:getWidth(), tilesetImage:getHeight())
tilesetBatch = love.graphics.newSpriteBatch(tilesetImage, tilesDisplayWidth * tilesDisplayHeight)
updateTilesetBatch()
end
function updateTilesetBatch()
tilesetBatch:clear()
for x=0, tilesDisplayWidth-1 do
for y=0, tilesDisplayHeight-1 do
tilesetBatch:add(tileQuads[map[x+math.floor(mapX)][y+math.floor(mapY)]],
x*tileSize, y*tileSize)
end
end
tilesetBatch:flush()
end
-- fonction centrale pour déplacer la carte
function moveMap(dx, dy)
oldMapX = mapX
oldMapY = mapY
mapX = math.max(math.min(mapX + dx, mapWidth - tilesDisplayWidth), 1)
mapY = math.max(math.min(mapY + dy, mapHeight - tilesDisplayHeight), 1)
-- seulement si on a effectivement bougé
if math.floor(mapX) ~= math.floor(oldMapX) or math.floor(mapY) ~= math.floor(oldMapY) then
updateTilesetBatch()
end
end
function love.update(dt)
if love.keyboard.isDown("up") then
moveMap(0, -0.2 * tileSize * dt)
end
if love.keyboard.isDown("down") then
moveMap(0, 0.2 * tileSize * dt)
end
if love.keyboard.isDown("left") then
moveMap(-0.2 * tileSize * dt, 0)
end
if love.keyboard.isDown("right") then
moveMap(0.2 * tileSize * dt, 0)
end
end
function love.draw()
love.graphics.draw(tilesetBatch,
math.floor(-zoomX*(mapX%1)*tileSize), math.floor(-zoomY*(mapY%1)*tileSize),
0, zoomX, zoomY)
love.graphics.print("FPS: "..love.timer.getFPS(), 10, 20)
end
Autre langages
Dansk –
Deutsch –
English –
Español –
Français –
Indonesia –
Italiano –
Lietuviškai –
Magyar –
Nederlands –
Polski –
Português –
Română –
Slovenský –
Suomi –
Svenska –
Türkçe –
Česky –
Ελληνικά –
Български –
Русский –
Српски –
Українська –
עברית –
ไทย –
日本語 –
正體中文 –
简体中文 –
Tiếng Việt –
한국어
More info