Minecraft est un jeu vidéo de type aventure « bac a sable » (construction complètement libre) développé par le Suédois Markus Persson, alias Notch, puis par la société Mojang Studios. Il s’agit d’un univers composé de voxels et généré de façon procédurale, qui intègre un système d’artisanat axé sur l’exploitation puis la transformation de ressources naturelles. Cet article montre la conception graphique à travers l’exemple de l’univers du jeu.
Le Projet
Comme cité dans l’introduction, cet article a pour but de montrer en détails la réalisation de notre conception graphique de l’univers de Minecraft. Pour ce projet, nous avons donc utilisé le module turtle, ainsi que le module random. Le script permettant d’exporter l’image générée a aussi été utilisé.
Le Code, en détails
Le code, dans son ensemble, est organisé avec chaque élément découpé et distinguable :
Tout d’abord, commençons par importation des librairies, ainsi que l’appel des fonctions :
from turtle import * from random import randint try: from PIL import Image pillow_installed = True except: print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :") print("https://nsi.xyz/py2png") pillow_installed = False titre = "Perspective - A Day In Minecraft" title(titre+" | Au lycée, la meilleure spécialité, c'est la spé NSI") setup(1280, 720) speed(0) hideturtle() flash = True if flash: wn = Screen() wn.tracer(0)
On ajoute donc les librairies turtle, ainsi que random, puis, une partie du script qui permet d’« exporter une image générée par turtle » est utilisée afin de vérifier que l’utilisateur a bien installé le module PIL (PILLOW), sinon un message d’erreur s’affichera et lui donnera un lien pour installer le module. Après que la vérification soit terminée, on met en place la partie du script qui permet de nommer la fenêtre qui va s’afficher avec le rendu ainsi que sa taille. Enfin on ajoute le type de couleurs utilisées (RGB par exemple), la vitesse de la tortue, et on dissimule cette dernière.
Apres cette étape, nous pouvons enfin commencer a coder avec turtle.
pour la première étape, nous avons le fond :
import turtle colormode(255) def ciel(): penup() # Valeurs initiales pour un bleu plus foncé rciel = 30 # Valeur de rouge gciel = 144 # Valeur de vert bciel = 255 # Valeur de bleu hauteur = -360 goto(-640, -358) pendown() # Durée totale pour le dégradé total_steps = 720 while hauteur <= 360: # Premier demi dégradé: Du bleu foncé au blanc if hauteur <= 0: r_incr = (255 - rciel) / (total_steps / 2) # Incrément de rouge g_incr = (255 - gciel) / (total_steps / 2) # Incrément de vert b_incr = (255 - bciel) / (total_steps / 2) # Incrément de bleu else: # Deuxième demi dégradé: Du blanc au bleu clair r_incr = (173 - 255) / (total_steps / 2) # Incrément de rouge vers bleu clair g_incr = (216 - 255) / (total_steps / 2) # Incrément de vert vers bleu clair b_incr = (230 - 255) / (total_steps / 2) # Incrément de bleu vers bleu clair pencolor(round(rciel), round(gciel), round(bciel)) forward(1280) hauteur += 1 goto(-640, hauteur) # Incrémenter les valeurs de couleur rciel += r_incr gciel += g_incr bciel += b_incr
Tout d’abord, le code commence configurer le mode de couleur pour utiliser le modèle RGB, où les valeurs des couleurs rouge, vert et bleu peuvent varier de 0 à 255. Une fonction nommée ciel est définie pour encapsuler tout le code de dessin. À l’intérieur de cette fonction, le stylo est levé au début, ce qui signifie que la tortue ne dessine pas lorsque elle se déplace à sa position initiale. Des variables sont initialisées pour représenter les valeurs de couleur du dégradé, avec une première couleur qui est un bleu plutôt clair. La tortue est ensuite déplacée à une position spécifique sur l’écran sans dessiner. Le code définit aussi une variable pour le nombre total d’étapes du dégradé, fixé à 720, ce qui représente le nombre d’itérations dans la boucle qui va dessiner le dégradé. Ensuite, une boucle commence et continuera tant que la hauteur de la tortue est inférieure ou égale à 360. Cette boucle est essentielle pour dessiner le dégradé de couleur. À chaque itération, il y a une condition qui vérifie si la tortue est dans la première moitié du dégradé (jusqu’à une hauteur de 0). Si c’est le cas, des incréments pour chaque composante de couleur sont calculés afin de passer du bleu foncé au blanc. Si la tortue est dans la seconde moitié du dégradé (au-dessus de 0), le code calcule de nouveaux incréments pour faire la transition du blanc vers une nuance de bleu clair. Dans chaque itération, la couleur actuelle du stylo est définie en fonction des valeurs calculées, puis la tortue se déplace en avant pour dessiner une ligne horizontale. Ensuite, la hauteur est augmentée pour passer à la ligne suivante, et la tortue est déplacée vers la nouvelle position. Enfin, à chaque étape de la boucle, les valeurs de couleur sont mises à jour en ajoutant les incréments précédemment calculés, permettant au dégradé de progresser progressivement à travers l’écran. Le résultat est un effet de ciel qui commence en bleu clair en haut, devient blanc au milieu, puis continue vers une couleur bleu clair en bas, recréant ainsi un effet visuel similaire à celui du ciel dans le jeu Minecraft.
Ensuite, la fonction créant les nuages est ajoutée :
def carre(x, y, size): # Dessine un carré de taille donnée aux coordonnées spécifiées penup() goto(x, y) pendown() color("white") begin_fill() for _ in range(4): forward(size) right(90) end_fill() def unnuage(x, y, size, grid_width, grid_height): # Dessine un nuage carré et homogène composé d'une grille de carrés for row in range(grid_height): for col in range(grid_width): # Positionne chaque carré dans une disposition en grille square_x = x + col * size square_y = y - row * size carre(square_x, square_y, size) def lesnuages(): # Place des nuages carrés et homogènes avec de grandes distances entre eux cloud_positions = [ (-500, 250), (-200, 300), (100, 310), (400, 310), (0, 180)] for x, y in cloud_positions: unnuage(x, y, size=20, grid_width=10, grid_height=5) # Taille et dimensions de chaque nuage # Appel des fonctions pour dessiner le ciel et les nuages lesnuages()
Le code utilise la bibliothèque turtle
de Python pour dessiner des nuages sous forme de carrés. La fonction carre(x, y, size)
dessine un carré d’une taille spécifiée aux coordonnées (x, y)
. Elle commence par lever le stylo pour ne pas dessiner en se déplaçant, puis elle se positionne au point donné, abaisse le stylo, et définit la couleur de remplissage à blanc. Ensuite, elle dessine le carré en avançant et en tournant à droite à chaque coin, avant de terminer le remplissage. La fonction unnuage(x, y, size, grid_width, grid_height)
crée un nuage en formant une grille de carrés. Elle utilise des boucles imbriquées pour parcourir les lignes et les colonnes de la grille, calculant les coordonnées de chaque carré en fonction de la taille spécifiée. Chaque carré est dessiné en appelant la fonction carre
. Enfin, la fonction lesnuages()
détermine où les nuages doivent être placés sur l’écran. Elle utilise une liste de positions prédéfinies pour placer plusieurs nuages avec un certain espacement entre eux. En appelant ces fonctions, le code dessine une série de nuages carrés et homogènes sur l’écran.
Pour la prochaine étape, nous coderons le soleil :
def draw_square(x, y, size,color): # Fonction pour dessiner un carré de couleur donnée aux coordonnées spécifiées penup() goto(x, y) pendown() turtle.color(color) begin_fill() for _ in range(4): forward(size) right(90) end_fill() def soleil(): # Centre du soleil (carré jaune) sun_size = 125 # Taille du carré central du soleil sun_x = -sun_size / 2 # Position X pour centrer le soleil sun_y = 250 # Position Y pour placer le soleil en haut de l'écran draw_square(sun_x, sun_y, sun_size, 'yellow') # Dessiner le soleil soleil()
Ce code utilise également la bibliothèque turtle
pour dessiner un soleil sous forme de carré coloré. La fonction draw_square(x, y, size, color)
permet de dessiner un carré aux coordonnées (x, y)
avec une taille spécifiée et une couleur donnée. Elle commence par lever le stylo pour éviter de dessiner en se déplaçant, puis elle se positionne à la coordonnée indiquée. Après avoir abaissé le stylo, elle définit la couleur du carré en utilisant turtle.color(color)
. Ensuite, elle commence à remplir la forme et dessine le carré en avançant et en tournant à droite de 90 degrés à chaque coin. Une fois le carré terminé, elle termine le remplissage avec end_fill()
. La fonction soleil()
est responsable du dessin du soleil, elle définit d’abord la taille du carré central (sun_size
) à 125 pixels et calcule les coordonnées pour centrer le carré en soustrayant la moitié de sa taille de la position X (sun_x
). La position Y (sun_y
) est fixée à 250 pour placer le soleil en haut de l’écran. Elle appelle ensuite la fonction draw_square
pour dessiner le carré représentant le soleil, en le remplissant de couleur jaune. Enfin, l’appel à soleil()
exécute cette fonction, ce qui dessine un grand carré jaune en haut de l’écran, représentant le soleil.
Puis, nous voyons le script permettant de de dessiner les blocs d’herbe :
def herbe(): # Configuration de l'herbe pixelisée penup() grass_height = 360 # Moitié de la hauteur de l'écran (720 / 2) pixel_size = 20 # Taille de chaque pixel (carré) # Dessiner des "pixels" de différentes nuances de vert dans la moitié inférieure for y in range(-360, 0, pixel_size): for x in range(-640, 640, pixel_size): # Choisir une nuance de vert aléatoire pour chaque carré green_value = random.randint(200, 255) # Valeur de vert entre 100 et 255 color = "#00%02x00" % green_value # Couleur hexadécimale pour une teinte de vert goto(x, y) turtle.color(color) begin_fill() for _ in range(4): forward(pixel_size) right(90) end_fill() # Dessin de l'herbe pixelisée, de la végétation et des fleurs herbe()
La fonction herbe()
est responsable du dessin de l’herbe pixelisée. Elle commence par lever le stylo avec penup()
pour éviter de dessiner en se déplaçant. Ensuite, elle définit la hauteur de l’herbe en spécifiant grass_height
à 360, ce qui correspond à la moitié de la hauteur de l’écran (720 pixels). La variable pixel_size
est définie à 20, ce qui détermine la taille de chaque « pixel » (carré) d’herbe. Le code utilise deux boucles imbriquées pour parcourir les coordonnées de la zone où l’herbe sera dessinée. La première boucle itère sur les coordonnées Y de -360 à 0 (la moitié inférieure de l’écran), et la seconde boucle itère sur les coordonnées X de -640 à 640, couvrant ainsi toute la largeur de l’écran. À l’intérieur de la boucle, une valeur de vert aléatoire est choisie pour chaque carré en utilisant random.randint(200, 255)
. Cela génère une nuance de vert qui sera plus claire, puisque les valeurs hexadécimales de vert vont de 200 à 255. La couleur hexadécimale est ensuite formatée en utilisant color = "#00%02x00" % green_value
, ce qui crée une chaîne de caractères représentant une couleur verte dans le format hexadécimal (ex. #00CC00
). Pour chaque pixel d’herbe, le curseur se déplace aux coordonnées (x, y)
correspondantes, et la couleur est définie. Ensuite, le remplissage commence avec begin_fill()
, et un carré est dessiné en avançant de pixel_size
et en tournant à droite de 90 degrés à chaque coin. Une fois le carré terminé, end_fill()
termine le remplissage de ce pixel.
Il ne reste plus que deux étapes, parmi celles-ci, on ajoute au code un script permettant de dessiner un arbre.
# Fonction pour dessiner un carré pour les blocs def draw_block(t, size, color): t.begin_fill() t.color(color) for _ in range(4): t.forward(size) t.left(90) t.end_fill() # Fonction pour dessiner un arbre style Minecraft def draw_minecraft_tree(x, y): block_size = 65 # Taille de chaque bloc pour un effet fidèle # Tronc de l'arbre en 4 blocs verticaux trunk = turtle.Turtle() trunk.hideturtle() trunk.speed("fastest") trunk.penup() trunk.goto(x, y) trunk.pendown() for i in range(4): # Tronc de 4 blocs de hauteur draw_block(trunk, block_size, "saddle brown") trunk.penup() trunk.goto(x, y + (i + 1) * block_size) trunk.pendown() # Feuillage de l'arbre en blocs foliage = turtle.Turtle() foliage.hideturtle() foliage.speed("fastest") foliage.penup() # Position des blocs de feuillage par rapport au tronc foliage_positions = [ (-1, 4), (0, 4), (1, 4), # Rangée supérieure (-1, 3), (0, 3), (1, 3), # Rangée au milieu (-2, 3), (2, 3), # Côtés de la rangée au milieu (-1, 2), (1, 2), (0, 2) # Rangée inférieure ] for pos in foliage_positions: foliage.goto(x + pos[0] * block_size, y + pos[1] * block_size) foliage.pendown() draw_block(foliage, block_size, "forest green") foliage.penup() # Dessiner l'arbre style Minecraft à gauche draw_minecraft_tree(-500, -100) draw_minecraft_tree(150, -100) draw_minecraft_tree(600, -200)
La fonction draw_block(t, size, color)
dessine un carré de couleur spécifiée avec une taille donnée. Elle commence le remplissage, définit la couleur, puis dessine les quatre côtés du bloc en tournant à gauche de 90 degrés après chaque côté, et termine le remplissage une fois le bloc dessiné. La fonction draw_minecraft_tree(x, y)
est responsable du dessin d’un arbre aux coordonnées (x, y)
. Elle fixe la taille de chaque bloc à 65 pixels et crée un Turtle pour dessiner le tronc. Ce Turtle se positionne aux coordonnées données et dessine quatre blocs verticaux pour former le tronc en utilisant une couleur marron. Après avoir dessiné chaque bloc, le Turtle se déplace vers le haut pour la prochaine position. Un second Turtle, destiné au feuillage, est également créé. Il est configuré pour dessiner des blocs de feuillage verts autour du tronc, avec des positions définies dans une liste pour donner une forme arrondie. Pour chaque position de feuillage, le Turtle se déplace aux coordonnées calculées et dessine un bloc de couleur verte. Enfin, la fonction draw_minecraft_tree
est appelée trois fois avec différentes coordonnées pour dessiner trois arbres à des endroits variés de l’écran. Ce code crée ainsi une représentation d’arbres en blocs de style Minecraft, composés de carrés pour le tronc et le feuillage.
Enfin, pour donner la touche finale qui va donner vie a notre conception graphique sur le thème de Minecraft, on ajoute un dernier script qui permet de dessiner un Creeper :
# Fonction pour dessiner un sous-bloc "pixel" def draw_pixel(t, size, color): t.begin_fill() t.color(color) for _ in range(4): t.forward(size) t.left(90) t.end_fill() # Fonction pour dessiner un bloc pixelisé avec un mélange de couleurs def draw_pixelated_block(t, x, y, block_size, pixel_size, colors): t.penup() for row in range(block_size // pixel_size): for col in range(block_size // pixel_size): t.goto(x + col * pixel_size, y - row * pixel_size) t.pendown() color = colors[(row + col + (row % 2)) % len(colors)] draw_pixel(t, pixel_size, color) t.penup() # Fonction pour dessiner un visage Creeper précis def draw_creeper_face(t, x, y, pixel_size): t.penup() # Yeux (4 pixels chacun) t.goto(x + pixel_size, y - 1 * pixel_size) draw_pixel(t, pixel_size, "black") t.goto(x + 2 * pixel_size, y - 1 * pixel_size) draw_pixel(t, pixel_size, "black") t.goto(x + pixel_size, y - 2 * pixel_size) draw_pixel(t, pixel_size, "black") t.goto(x + 2 * pixel_size, y - 2 * pixel_size) draw_pixel(t, pixel_size, "black") t.goto(x + 4 * pixel_size, y - 1 * pixel_size) draw_pixel(t, pixel_size, "black") t.goto(x + 5 * pixel_size, y - 1 * pixel_size) draw_pixel(t, pixel_size, "black") t.goto(x + 4 * pixel_size, y - 2 * pixel_size) draw_pixel(t, pixel_size, "black") t.goto(x + 5 * pixel_size, y - 2 * pixel_size) draw_pixel(t, pixel_size, "black") # Bouche (3x2 pixels pour la rangée supérieure, 2x1 pixels pour la rangée inférieure) t.goto(x + 15, y - 4 * pixel_size) for _ in range(4): draw_pixel(t, pixel_size, "black") t.forward(pixel_size) t.goto(x + 25, y - 3 * pixel_size) for _ in range(2): draw_pixel(t, pixel_size, "black") t.forward(pixel_size) t.goto(x + 15, y - 5 * pixel_size) for _ in range(4): draw_pixel(t, pixel_size, "black") t.forward(pixel_size) t.goto(x + 45, y - 6 * pixel_size) for _ in range(1): draw_pixel(t, pixel_size, "black") t.forward(pixel_size) t.goto(x + 15, y - 6 * pixel_size) for _ in range(1): draw_pixel(t, pixel_size, "black") t.forward(pixel_size) # Fonction pour dessiner un Creeper Minecraft pixelisé complet def draw_pixelated_creeper(x, y): block_size = 70 # Taille de chaque section principale du Creeper pixel_size = 10 # Taille de chaque pixel # Couleurs pour le visage et le corps du Creeper face_colors = ["#2E8B57", "#228B22", "#32CD32", "#006400"] body_colors = ["#228B22", "#006400", "#32CD32"] creeper = turtle.Turtle() creeper.hideturtle() creeper.speed("fastest") # Tête du Creeper avec visage draw_pixelated_block(creeper, x, y, block_size, pixel_size, face_colors) draw_creeper_face(creeper, x, y, pixel_size) # Corps du Creeper (2 blocs de hauteur sous la tête) for i in range(2): draw_pixelated_block(creeper, x, y - (i + 1) * block_size, block_size, pixel_size, body_colors) # Pieds du Creeper (2 blocs de largeur sous le corps) for i in range(2): draw_pixelated_block(creeper, x - block_size // 2, y - (i + 3) * block_size, block_size // 2, pixel_size, body_colors) draw_pixelated_block(creeper, x + block_size // 2, y - (i + 3) * block_size, block_size // 2, pixel_size, body_colors) # Dessiner le Creeper pixelisé à droite draw_pixelated_creeper(450, 50)
Et voila ! Nous voici avec une image qui représente (plus ou moins 😅) l’univers du jeu vidéo Minecraft, et cela, avec turtle et random !
Les difficultés du projet
Durant ce projet, nous avons eus quelques difficultés a savoir lors de la correction du code ou encore lorsqu’il fallait positionner certains éléments. Nous avons, bien que très peu, utilisés l’AI Chat GPT pour nous guider dans la correction ou encore quelques conceptions de script.