Difference between revisions of "Tutorial:Animation (Français)"
(create) |
m |
||
(3 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | + | Avant de commencer, ceci est un tutoriel pour les utilisateurs semi-avancés. Vous êtes censé connaître les tableaux, les boucles et les bases du dessin dans Löve2D. Et bien sûr, comment lancer un jeu Löve2D. | |
− | + | Bon, vous êtes prêt pour y aller ? | |
− | + | Je vais couvrir les bases de l'animation basée sur les sprites. Cela signifie que vous avez une série d'images qui sont affichées les unes après les autres. Si vous avez l'intention de créer votre propre spritesheet, veillez à laisser, au moins, 1px de transparence pure entre les sprites individuels sinon, vous pourriez voir des artefacts sur l'image suivante ou précédente et personne ne le souhaite! | |
− | == | + | == Chargement de l'animation == |
− | + | Pour ce tutoriel, nous utiliserons un vieux héros anonyme. Généralement mis à la disposition de tous par GrafxKid sur [https://opengameart.org/content/classic-hero OpenGameArt.org]. Téléchargez l'image ci-dessous et placez-la dans le même dossier que votre main.lua. | |
[[File:oldHero.png|200px|thumb|center|oldHero.png]] | [[File:oldHero.png|200px|thumb|center|oldHero.png]] | ||
− | + | Pour pouvoir créer plusieurs animations, nous voulons une fonction réutilisable qui nous fournit une table que nous pouvons utiliser pour tout ce qui concerne l'animation. | |
− | + | Nous commençons ceci en définissant une nouvelle fonction, en stockant notre image dans une variable et en créant une nouvelle table locale (la valeur de retour sera notre table. Nous ne voulons pas qu'une table d'animation aléatoire soit définie globalement ou pire écrase d'autres données!) | |
− | + | Les paramètres suivants seront nécessaires: | |
* image | * image | ||
− | + | Un objet image créé avec <code>love.graphics.newImage(filepath)</code> | |
* width | * width | ||
− | + | La largeur de chaque sprite individuel. Nous allons supposer que tous les sprites ont la même taille. | |
* height | * height | ||
− | + | La hauteur de chaque sprite individuel. | |
* duration | * duration | ||
− | + | Combien de temps dure l'animation avant de reboucler sur la première image. | |
<source lang="lua"> | <source lang="lua"> | ||
Line 34: | Line 34: | ||
</source> | </source> | ||
− | + | Vous pouvez essayer de dessiner cela directement avec <code>love.graphics.draw(animation.spriteSheet)</code>. Toutefois, nous aurions toutes les images dessinées les unes à côté des autres. Ce n'est certainement pas ce que nous voulons. C'est là où les [Quad | Quads]] sont très pratique! | |
− | + | Ils définissent une partie de l'image qui sera dessinée à la place de l'image entière. C'est exactement ce dont nous avons besoin! | |
− | + | Nous pouvons maintenant définir chaque quad individuellement. Mais cela va être très ennuyeux si nous avons plusieurs animations et des centaines de sprites! Nous allons donc le charger dans une boucle qui détecte le nombre de sprites dans une image. Pour cette configuration, il ne doit pas y avoir d'espaces vides et l'image doit contenir, exactement, une animation. | |
<source lang="lua"> | <source lang="lua"> | ||
Line 56: | Line 56: | ||
</source> | </source> | ||
− | + | Les 3 paramètres de la boucle for sont les suivants: | |
− | |||
− | + | 1. La valeur de départ pour y ou x. | |
− | + | 2. La valeur maximale (dans ce cas la largeur ou la hauteur totale de l'image de référence) | |
− | + | 3. Combien doit-on ajouté à chaque étape. Dans notre cas, la taille du sprite. | |
− | + | Avec cela, nous obtenons l'emplacement sur la feuille de sprite à partir de y et x! | |
− | + | Vous pourriez vous demander pourquoi faisons nous "image: getHeight () - height" au lieu de simplement tester la hauteur de l'image. C'est parce que nous voulons nous assurer qu'un autre sprite tient toujours sur la feuille de sprite. La feuille elle-même pourrait ne pas avoir la taille exacte souhaitée. Si elle était trop grande d'un pixel nous ne voudrions pas ajouter un autre quad (ce qui ne se traduirait par rien) mais plutôt l’ignorer. | |
− | + | Maintenant, nous avons 6 Quads allant de 1 à 6 dans le tableau. Impressionnant! | |
− | |||
− | + | Cependant, nous avons un autre problème avec ces 6 images que nous pouvons dessiner individuellement. Dans l'état actuel des choses, nous allons les dessiner les unes après les autres au fil du temps. | |
− | Duration ( | + | Mais, nous ne voulons pas forcément jouer cette animation aussi vite que possible et nous pourrions vouloir changer la vitesse de défilement. |
+ | |||
+ | Pour corriger ceci, nous ajoutons deux variables supplémentaires à notre table d'animation. | ||
+ | Duration (que nous attendons en tant que paramètre) et currentTime qui sert à mesurer le temps écoulé. | ||
<source lang="lua"> | <source lang="lua"> | ||
Line 95: | Line 96: | ||
</source> | </source> | ||
− | + | Ce qui conclut notre fonction de création d'animation! | |
− | == | + | == Mise à jour de l'animation == |
− | + | Nous devons charger notre table d'animation (appelez notre nouvelle fonction) et mettre à jour notre heure actuelle. | |
<source lang="lua"> | <source lang="lua"> | ||
Line 114: | Line 115: | ||
</source> | </source> | ||
− | + | La feuille de sprite de démonstration a une taille de sprite de 16 pixels de largeur et de 18 pixels de hauteur. Nous avons l'intention de jouer toutes les images sur une seconde. | |
− | + | Dans <code>love.update</code>, nous ajoutons simplement dt (delta time aka le temps écoulé depuis la dernière image) à notre heure actuelle. Maintenant, nous comptons continuellement! | |
− | + | Mais nous utiliserons l'heure actuelle pour déterminer quelle image doit être affichée. En tant que tel, nous voulons qu'il soit compris entre 0 et la valeur de "duration". Le if vérifie simplement si "currentTime" est plus grand que "duration", auquel cas on soustrait "duration". Vous pouvez simplement définir "currentTime" sur 0 au lieu de soustraire "duration". Cependant, des fractions de seconde sont perdues chaque fois que l'animation se termine et ralentissent donc légèrement la durée de l'animation. Ceci est facile à évité et doit l'être! | |
− | + | Maintenant, la partie vraiment intéressante! | |
− | == | + | == Dessiner l'animation == |
− | + | Comment pouvons-nous dessiner cela? | |
− | + | Bon. Nous avons la durée et l'heure actuelle. Avec cette information, nous pouvons calculer un pourcentage! Combien de l'animation est passée jusqu'à présent? | |
− | + | Si vous avez suivi ce tutoriel correctement jusqu'à présent, ''"currentTime / duration"'' vous fournira un nombre compris entre 0 et 1 ce qui représente le pourcentage. 0,25 signifie que 25% de l'animation est passée. | |
− | + | Compte tenu de cela, nous pouvons rechercher la bonne image à utiliser! Comme nous avons déjà un nombre entre 0 et 1, nous pouvons simplement multiplier ce pourcentage avec notre nombre total d'images et obtenir un nombre entre 0 et 6! | |
''currentTime / duration * #quads'' | ''currentTime / duration * #quads'' | ||
− | + | Toutefois, le résultat de l'opération va nous poser un problème car ce ne sera pas un entier hors, nos images sont stockées dans la table avec des entiers! Donc, essayer d'obtenir l'image à l'index "4.75" ne nous donnera rien. Mince! | |
− | + | Pas de panique, la solution n'est pas trop difficile. | |
− | |||
− | + | "currentTime" sera un nombre compris entre 0 et juste en dessous de "duration" (car nous réduisons "currentTime" s'il est plus grand ou ''égal'' à "duration"). Ce qui donnerait un nombre entre 0 et 5. | |
+ | |||
+ | Pour transformer cette valeur de notre valeur décimale à un nombre entier compris entre 1 et 6, il faut procéder comme suit: | ||
<source lang="lua"> | <source lang="lua"> | ||
Line 146: | Line 148: | ||
</source> | </source> | ||
− | "math.floor" | + | "math.floor" nous fournit l'entier inférieur le plus proche, ce qui signifie dans notre cas, un nombre compris entre 0 et 5. On y rajoute un et nous obtenons un nombre compris entre 1 et 6 ce qui correspond à ce au nombre de sprite que nous avons! |
− | + | Charmant! | |
− | + | Bien, il ne reste donc plus qu'à dessiner le quad adéquat! | |
− | + | Cela nécessite simplement de fournir <code>love.graphics.draw</code> avec la référence de l'image (notre spriteSheet) et le quad que nous voulons utiliser. Assez simple! | |
<source lang="lua"> | <source lang="lua"> | ||
Line 159: | Line 161: | ||
</source> | </source> | ||
− | |||
− | + | Et nous avons fini! Vous devriez avoir un gars dans le coin supérieur gauche de votre fenêtre lorsque vous exécutez ce code! | |
− | |||
− | + | Vous pouvez modifier où et comment il est dessiné en fournissant plus de paramètres à la fonction [[love.graphics.draw|draw function]]. | |
− | + | '''Avertissement: Ce code n'est pas prêt pour le jeu! Il est destiné à expliquer la logique de base des animations. Si vous recherchez un code prêt pour le jeu, consultez la liste des [[Libraries|Bibliothèques]] disponibles! ''' | |
− | + | == Tâche == | |
− | + | Pour améliorer ce code, essayez de réécrire la fonction update et draw pour pouvoir gérer plusieurs animations | |
− | + | Vous pouvez charger la même animation plusieurs fois et les stocker dans une table spéciale pour commencer tout de suite! | |
− | == | + | Aussi utile que cela puisse être fait: Vous pouvez également placer la logique de dessin dans la table d'animation. Et construisez-le de telle sorte que pour l'appeler on écrive "animation:draw (x, y, r, sx, sy, [...])" pour le dessiner à l'écran. |
+ | |||
+ | Bonne chance et amusez-vous bien! | ||
+ | |||
+ | == Code source intégral == | ||
<source lang="lua"> | <source lang="lua"> | ||
Line 215: | Line 219: | ||
{{#set:LOVE Version=}} | {{#set:LOVE Version=}} | ||
{{#set:Description=Basics of sprite based animation.}} | {{#set:Description=Basics of sprite based animation.}} | ||
− | == | + | |
+ | == Autres langues == | ||
{{i18n|Tutorial:Animation}} | {{i18n|Tutorial:Animation}} |
Latest revision as of 20:44, 17 August 2018
Avant de commencer, ceci est un tutoriel pour les utilisateurs semi-avancés. Vous êtes censé connaître les tableaux, les boucles et les bases du dessin dans Löve2D. Et bien sûr, comment lancer un jeu Löve2D.
Bon, vous êtes prêt pour y aller ?
Je vais couvrir les bases de l'animation basée sur les sprites. Cela signifie que vous avez une série d'images qui sont affichées les unes après les autres. Si vous avez l'intention de créer votre propre spritesheet, veillez à laisser, au moins, 1px de transparence pure entre les sprites individuels sinon, vous pourriez voir des artefacts sur l'image suivante ou précédente et personne ne le souhaite!
Contents
Chargement de l'animation
Pour ce tutoriel, nous utiliserons un vieux héros anonyme. Généralement mis à la disposition de tous par GrafxKid sur OpenGameArt.org. Téléchargez l'image ci-dessous et placez-la dans le même dossier que votre main.lua.
Pour pouvoir créer plusieurs animations, nous voulons une fonction réutilisable qui nous fournit une table que nous pouvons utiliser pour tout ce qui concerne l'animation. Nous commençons ceci en définissant une nouvelle fonction, en stockant notre image dans une variable et en créant une nouvelle table locale (la valeur de retour sera notre table. Nous ne voulons pas qu'une table d'animation aléatoire soit définie globalement ou pire écrase d'autres données!)
Les paramètres suivants seront nécessaires:
- image
Un objet image créé avec love.graphics.newImage(filepath)
- width
La largeur de chaque sprite individuel. Nous allons supposer que tous les sprites ont la même taille.
- height
La hauteur de chaque sprite individuel.
- duration
Combien de temps dure l'animation avant de reboucler sur la première image.
function newAnimation(image, width, height, duration)
local animation = {}
animation.spriteSheet = image;
return animation
end
Vous pouvez essayer de dessiner cela directement avec love.graphics.draw(animation.spriteSheet)
. Toutefois, nous aurions toutes les images dessinées les unes à côté des autres. Ce n'est certainement pas ce que nous voulons. C'est là où les [Quad | Quads]] sont très pratique!
Ils définissent une partie de l'image qui sera dessinée à la place de l'image entière. C'est exactement ce dont nous avons besoin!
Nous pouvons maintenant définir chaque quad individuellement. Mais cela va être très ennuyeux si nous avons plusieurs animations et des centaines de sprites! Nous allons donc le charger dans une boucle qui détecte le nombre de sprites dans une image. Pour cette configuration, il ne doit pas y avoir d'espaces vides et l'image doit contenir, exactement, une animation.
function newAnimation(image, width, height, duration)
local animation = {}
animation.spriteSheet = image;
animation.quads = {};
for y = 0, image:getHeight() - height, height do
for x = 0, image:getWidth() - width, width do
table.insert(animation.quads, love.graphics.newQuad(x, y, width, height, image:getDimensions()))
end
end
return animation
end
Les 3 paramètres de la boucle for sont les suivants:
1. La valeur de départ pour y ou x.
2. La valeur maximale (dans ce cas la largeur ou la hauteur totale de l'image de référence)
3. Combien doit-on ajouté à chaque étape. Dans notre cas, la taille du sprite.
Avec cela, nous obtenons l'emplacement sur la feuille de sprite à partir de y et x!
Vous pourriez vous demander pourquoi faisons nous "image: getHeight () - height" au lieu de simplement tester la hauteur de l'image. C'est parce que nous voulons nous assurer qu'un autre sprite tient toujours sur la feuille de sprite. La feuille elle-même pourrait ne pas avoir la taille exacte souhaitée. Si elle était trop grande d'un pixel nous ne voudrions pas ajouter un autre quad (ce qui ne se traduirait par rien) mais plutôt l’ignorer.
Maintenant, nous avons 6 Quads allant de 1 à 6 dans le tableau. Impressionnant!
Cependant, nous avons un autre problème avec ces 6 images que nous pouvons dessiner individuellement. Dans l'état actuel des choses, nous allons les dessiner les unes après les autres au fil du temps. Mais, nous ne voulons pas forcément jouer cette animation aussi vite que possible et nous pourrions vouloir changer la vitesse de défilement.
Pour corriger ceci, nous ajoutons deux variables supplémentaires à notre table d'animation. Duration (que nous attendons en tant que paramètre) et currentTime qui sert à mesurer le temps écoulé.
function newAnimation(image, width, height, duration)
local animation = {}
animation.spriteSheet = image;
animation.quads = {};
for y = 0, image:getHeight() - height, height do
for x = 0, image:getWidth() - width, width do
table.insert(animation.quads, love.graphics.newQuad(x, y, width, height, image:getDimensions()))
end
end
animation.duration = duration or 1
animation.currentTime = 0
return animation
end
Ce qui conclut notre fonction de création d'animation!
Mise à jour de l'animation
Nous devons charger notre table d'animation (appelez notre nouvelle fonction) et mettre à jour notre heure actuelle.
function love.load()
animation = newAnimation(love.graphics.newImage("oldHero.png"), 16, 18, 1)
end
function love.update(dt)
animation.currentTime = animation.currentTime + dt
if animation.currentTime >= animation.duration then
animation.currentTime = animation.currentTime - animation.duration
end
end
La feuille de sprite de démonstration a une taille de sprite de 16 pixels de largeur et de 18 pixels de hauteur. Nous avons l'intention de jouer toutes les images sur une seconde.
Dans love.update
, nous ajoutons simplement dt (delta time aka le temps écoulé depuis la dernière image) à notre heure actuelle. Maintenant, nous comptons continuellement!
Mais nous utiliserons l'heure actuelle pour déterminer quelle image doit être affichée. En tant que tel, nous voulons qu'il soit compris entre 0 et la valeur de "duration". Le if vérifie simplement si "currentTime" est plus grand que "duration", auquel cas on soustrait "duration". Vous pouvez simplement définir "currentTime" sur 0 au lieu de soustraire "duration". Cependant, des fractions de seconde sont perdues chaque fois que l'animation se termine et ralentissent donc légèrement la durée de l'animation. Ceci est facile à évité et doit l'être!
Maintenant, la partie vraiment intéressante!
Dessiner l'animation
Comment pouvons-nous dessiner cela?
Bon. Nous avons la durée et l'heure actuelle. Avec cette information, nous pouvons calculer un pourcentage! Combien de l'animation est passée jusqu'à présent?
Si vous avez suivi ce tutoriel correctement jusqu'à présent, "currentTime / duration" vous fournira un nombre compris entre 0 et 1 ce qui représente le pourcentage. 0,25 signifie que 25% de l'animation est passée.
Compte tenu de cela, nous pouvons rechercher la bonne image à utiliser! Comme nous avons déjà un nombre entre 0 et 1, nous pouvons simplement multiplier ce pourcentage avec notre nombre total d'images et obtenir un nombre entre 0 et 6!
currentTime / duration * #quads
Toutefois, le résultat de l'opération va nous poser un problème car ce ne sera pas un entier hors, nos images sont stockées dans la table avec des entiers! Donc, essayer d'obtenir l'image à l'index "4.75" ne nous donnera rien. Mince!
Pas de panique, la solution n'est pas trop difficile.
"currentTime" sera un nombre compris entre 0 et juste en dessous de "duration" (car nous réduisons "currentTime" s'il est plus grand ou égal à "duration"). Ce qui donnerait un nombre entre 0 et 5.
Pour transformer cette valeur de notre valeur décimale à un nombre entier compris entre 1 et 6, il faut procéder comme suit:
math.floor(currentTime / duration * #quads) + 1
"math.floor" nous fournit l'entier inférieur le plus proche, ce qui signifie dans notre cas, un nombre compris entre 0 et 5. On y rajoute un et nous obtenons un nombre compris entre 1 et 6 ce qui correspond à ce au nombre de sprite que nous avons!
Charmant!
Bien, il ne reste donc plus qu'à dessiner le quad adéquat!
Cela nécessite simplement de fournir love.graphics.draw
avec la référence de l'image (notre spriteSheet) et le quad que nous voulons utiliser. Assez simple!
local spriteNum = math.floor(animation.currentTime / animation.duration * #animation.quads) + 1
love.graphics.draw(animation.spriteSheet, animation.quads[spriteNum])
Et nous avons fini! Vous devriez avoir un gars dans le coin supérieur gauche de votre fenêtre lorsque vous exécutez ce code!
Vous pouvez modifier où et comment il est dessiné en fournissant plus de paramètres à la fonction draw function.
Avertissement: Ce code n'est pas prêt pour le jeu! Il est destiné à expliquer la logique de base des animations. Si vous recherchez un code prêt pour le jeu, consultez la liste des Bibliothèques disponibles!
Tâche
Pour améliorer ce code, essayez de réécrire la fonction update et draw pour pouvoir gérer plusieurs animations
Vous pouvez charger la même animation plusieurs fois et les stocker dans une table spéciale pour commencer tout de suite!
Aussi utile que cela puisse être fait: Vous pouvez également placer la logique de dessin dans la table d'animation. Et construisez-le de telle sorte que pour l'appeler on écrive "animation:draw (x, y, r, sx, sy, [...])" pour le dessiner à l'écran.
Bonne chance et amusez-vous bien!
Code source intégral
function love.load()
animation = newAnimation(love.graphics.newImage("oldHero.png"), 16, 18, 1)
end
function love.update(dt)
animation.currentTime = animation.currentTime + dt
if animation.currentTime >= animation.duration then
animation.currentTime = animation.currentTime - animation.duration
end
end
function love.draw()
local spriteNum = math.floor(animation.currentTime / animation.duration * #animation.quads) + 1
love.graphics.draw(animation.spriteSheet, animation.quads[spriteNum], 0, 0, 0, 4)
end
function newAnimation(image, width, height, duration)
local animation = {}
animation.spriteSheet = image;
animation.quads = {};
for y = 0, image:getHeight() - height, height do
for x = 0, image:getWidth() - width, width do
table.insert(animation.quads, love.graphics.newQuad(x, y, width, height, image:getDimensions()))
end
end
animation.duration = duration or 1
animation.currentTime = 0
return animation
end
Autres langues
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