Dessiner le Labyrinthe
Objectif
L'objectif de cette séance est de comprendre et d'appliquer les structures de données pour dessiner le labyrinthe de notre jeu Pacman.
Nous allons apprendre :
- à représenter le labyrinthe sous forme de tableau (array) en JavaScript,
- et à afficher le tableau du labyrinthe à l'écran avec Expo.
Notions théoriques
Pour représenter notre labyrinthe, nous allons utiliser un tableau à 2 dimensions.
Chaque élément du tableau correspondra à une case du labyrinthe : un mur ou un chemin.
Les tableaux en Javascript
Les tableaux en JavaScript sont des collections ordonnées de valeurs qui peuvent être de différents types.
Les tableaux en JavaScript :
- sont flexibles,
- peuvent changer de taille dynamiquement,
- et offrent des méthodes pour effectuer des opérations telles que l'itération et la transformation des éléments.
Exemple d'un tableau simple :
let fruits = ["pomme", "banane", "cerise"];
Exemple d'un tableau de tableaux (aussi appelé tableau multidimensionnel) :
let grille = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
En JavaScript, un tableau à 2 dimensions peut être vu comme un tableau de tableaux.
Voici un exemple de représentation d'un petit labyrinthe :
const labyrinthe = [
[1, 1, 1, 1, 1],
[1, 0, 0, 0, 1],
[1, 0, 1, 0, 1],
[1, 0, 1, 0, 1],
[1, 1, 1, 1, 1]
];
- Dans ce tableau,
1
représente un mur et0
un chemin. - Ce tableau représente un labyrinthe avec un contour de murs et un chemin à l'intérieur.
let
ou const
En JavaScript, let
et const
sont des mots-clés utilisés pour déclarer des variables.
let
permet de déclarer des variables dont la valeur peut changer,- tandis que
const
est utilisé pour des variables dont la valeur ne peut pas changer.
Exemple pratique
Pour transformer notre tableau en un véritable labyrinthe visuel,
nous pouvons utiliser le composant View
de React Native.
-
Voici un exemple de fonction pour dessiner une case :
function Case(props) {
const estMur = props.estMur;
const styleCase = {
width: 40,
height: 40,
backgroundColor: estMur ? 'black' : 'blue',
};
return <View style={styleCase} />;
}Explications des lignes de la fonction
Case
.-
Déclaration de la fonction :
Case(props)
déclare une fonction qui prend un objetprops
en paramètre.props
est un objet standard en React qui permet de passer des données et des configurations aux composants. -
Extraction de la propriété
estMur
: La ligneconst estMur = props.estMur;
extrait la propriétéestMur
de l'objetprops
. Cette propriété est utilisée pour déterminer si la case à dessiner est un mur ou non. -
Définition du style de la case : La constante
styleCase
est un objet qui définit les styles CSS pour le composantView
qui sera retourné. Les styles définissent la largeur et la hauteur de la case (width
etheight
sont tous deux de 40 pixels). La couleur de fond (backgroundColor
) est conditionnelle : siestMur
est vrai (c'est-à-dire si la case représente un mur), la couleur de fond sera noire ('black'
), sinon elle sera bleue ('blue'
). -
Rendu du composant : La fonction retourne un élément
View
avec le style appliqué via l'attributstyle
. En React Native,View
est un composant conteneur qui peut être utilisé pour envelopper d'autres composants. Il est similaire à unediv
en HTML.
En résumé, la fonction
Case
retourne une case de 40 pixels de côté qui sera soit noire si elle représente un mur, soit bleue sinon. -
-
Voici un exemple de fonction pour dessiner toutes les cases, en faisant appel à la fonction Case() pour chaque case du labyrinthe :
function Labyrinthe(props) {
const labyrinthe = props.labyrinthe;
return (
<View style={{ flexDirection: 'column' }}>
{labyrinthe.map(function(ligne, indexLigne) {
return (
<View key={indexLigne} style={{ flexDirection: 'row' }}>
{ligne.map(function(caseLabyrinthe, indexColonne) {
return (
<Case
key={indexColonne}
estMur={caseLabyrinthe === 1}
/>
);
})}
</View>
);
})}
</View>
);
}Explications des lignes de la fonction
Labyrinthe
.La fonction
Labyrinthe
prend un seul argument,props
, qui est un objet contenant toutes les propriétés (props) passées au composantLabyrinthe
lorsqu'il est utilisé dans un autre composant.Voici une explication détaillée de ce que fait chaque partie du code :
-
Extraction de
labyrinthe
depuisprops
: La première ligne à l'intérieur de la fonction extrait la propriétélabyrinthe
de l'objetprops
. Cette propriétélabyrinthe
est supposée être un tableau à deux dimensions représentant le labyrinthe, où chaque sous-tableau représente une ligne du labyrinthe. -
Rendu du composant
View
racine: Le composant retourne un élémentView
de React Native, qui est l'équivalent d'unediv
dans le web. Cet élémentView
est configuré pour disposer ses enfants en colonne avec le styleflexDirection: 'column'
. -
Itération sur les lignes du labyrinthe: À l'intérieur de cet élément
View
, le code itère sur chaque ligne du labyrinthe en utilisant la méthodemap
. Pour chaque ligne, une fonction est appelée qui prend la ligne actuelle et son index (indexLigne
) en tant qu'arguments. -
Rendu des lignes du labyrinthe: Pour chaque ligne, un autre élément
View
est retourné, qui représente une ligne du labyrinthe. Cet élémentView
est configuré pour disposer ses enfants en ligne avec le styleflexDirection: 'row'
. La clé (key
) est utilisée pour aider React à identifier quels éléments ont changé, ont été ajoutés ou sont restés les mêmes lors des mises à jour de la liste. -
Itération sur les cases de chaque ligne: À l'intérieur de chaque ligne, une autre itération est faite sur chaque case de cette ligne avec la méthode
map
. Pour chaque case, une fonction est appelée qui prend la valeur de la case (caseLabyrinthe
) et l'index de la colonne (indexColonne
) en tant qu'arguments. -
Rendu des cases individuelles: Pour chaque case de la ligne, le composant
Case
est retourné. Ce composant est probablement un autre composant personnalisé qui sait comment dessiner une case individuelle du labyrinthe. Il reçoit une propestMur
qui est un booléen déterminé par la conditioncaseLabyrinthe === 1
. Cela suggère que dans le tableaulabyrinthe
, une valeur de1
représente un mur et toute autre valeur représente un espace vide. Lekey
est également utilisé ici pour les mêmes raisons d'optimisation des performances.
En résumé, la fonction
Labyrinthe
prend un tableau à deux dimensions représentant un labyrinthe et le transforme en une représentation visuelle à l'aide de composants React Native. Chaque ligne est rendue comme une série deView
en ligne, et chaque case est rendue par le composantCase
, qui change d'apparence en fonction de savoir si elle est un mur ou un espace vide. -
Dans ce code, nous définissons 2 fonctions :
Case
pour dessiner une case individuelle,- et
Labyrinthe
pour dessiner le labyrinthe entier en utilisantCase
.
Elements de code à connaître
Array.map()
: Cette méthode crée un nouveau tableau avec les résultats de l'appel d'une fonction fournie sur chaque élément du tableau appelant.React Native Views
: LesView
sont des conteneurs qui supportent le style et le layout. Ils sont l'équivalent desdiv
en HTML.
Test de mémorisation/compréhension
TP : Dessiner le labyrinthe
Votre mission
Votre mission consiste à dessiner un labyrinthe (avec des chemins et des murs).
Etapes
Voici un exemple de code qui permet dessiner un labyrinthe :
-
Créez un nouveau fichier
GenererationLabyrinthe.js
à la racine de votre projet (à côté deApp.js
).// GenererationLabyrinthe.js
-
Ajoutez la fonction
genererLabyrinthe(rows, cols)
dans le fichierGenererationLabyrinthe.js
:function genererLabyrinthe(rows, cols) {
const labyrinthe = [
...
];
return labyrinthe;
} -
Ajoutez le tableau
labyrinthe
fourni ci-dessous, dans votre fichierGenererationLabyrinthe.js
, pour représenter le labyrinthe de base :const labyrinthe = [
[1, 1, 1, 1, 1, 1, 1, 1, 1], // Mur supérieur
[1, 0, 0, 0, 0, 0, 0, 0, 1], // Chemin horizontal
[1, 0, 1, 1, 1, 1, 1, 0, 1], // 2 passages sur la ligne 3
[1, 0, 0, 0, 0, 0, 0, 0, 1], // Chemin horizontal
[1, 1, 1, 1, 0, 1, 1, 1, 1], // 1 passage sur la ligne 5
[1, 0, 0, 0, 0, 0, 0, 0, 1], // Chemin horizontal
[1, 1, 1, 0, 1, 1, 1, 0, 1], // 2 passages sur la ligne 7
[1, 0, 0, 0, 0, 0, 0, 0, 1], // Chemin horizontal
[1, 1, 1, 1, 1, 1, 1, 1, 1] // Mur inférieur
];- Dans ce tableau,
1
représente un mur et0
un chemin. - Ce tableau représente un labyrinthe avec un contour de murs et un chemin à l'intérieur.
- Dans ce tableau,
-
Ajoutez l'export de la fonction
genererLabyrinthe
à la fin du fichierGenererationLabyrinthe.js
:export default genererLabyrinthe;
-
Importez le composant
genererLabyrinthe
, dans votreApp.js
, pour accéder au tableaulabyrinthe
:import genererLabyrinthe from './GenererationLabyrinthe';
-
Créez un nouveau fichier
Labyrinthe.js
à la racine de votre projet (à côté deApp.js
).// Labyrinthe.js
import React from 'react';
import { View } from 'react-native';
const tailleCase = 40;
function Case(props) {
...
}
function Labyrinthe(props) {
...
} -
Implémentez la fonction
Case
pour afficher une case du labyrinthe....
function Case(props) {
const estMur = props.estMur;
const styleCase = {
width: tailleCase,
height: tailleCase,
backgroundColor: estMur ? 'black' : 'blue',
};
return <View style={styleCase} />;
} -
Implémentez la fonction
Labyrinthe
pour afficher toutes les cases du labyrinthe....
function Labyrinthe(props) {
const labyrinthe = props.labyrinthe;
return (
<View style={{ flexDirection: 'column' }}>
{labyrinthe.map(function(ligne, indexLigne) {
return (
<View key={indexLigne} style={{ flexDirection: 'row' }}>
{ligne.map(function(caseLabyrinthe, indexColonne) {
return (
<Case
key={indexColonne}
estMur={caseLabyrinthe === 1}
/>
);
})}
</View>
);
})}
</View>
);
} -
Ajoutez l'export de la fonction
Labyrinthe
à la fin du fichierLabyrinthe.js
:export default Labyrinthe;
-
Effacez la ligne d'import de la Barre de statut, inutile, dans le fichier
App.js
.Voici la ligne inutile, à effacer :
import { StatusBar } from 'expo-status-bar';
-
Importez le composant
Labyrinthe
, dans votreApp.js
, pour rendre la fonctionLabyrinthe
accessible dans l'application :import Labyrinthe from './Labyrinthe';
-
Créez une variable (constante)
labyrinthe
pour stocker le tableau du labyrinthe dans le programme principalApp.js
.import Labyrinthe from './Labyrinthe';
const labyrinthe = genererLabyrinthe(11, 11);
export default function App() {
-
Effacez les lignes inutiles dans le composant
View
du fichierApp.js
.Voici les 2 lignes inutiles, à effacer :
<Text>Open up App.js to start working on your app!</Text>
<StatusBar style="auto" /> -
Ajoutez le composant
Labyrinthe
dans le composantView
pour afficher le labyrinthe :return (
<View style={styles.container}>
<View>
<Labyrinthe labyrinthe={labyrinthe} />
</View>
</View>
); -
Testez votre application pour vous assurer que le labyrinthe s'affiche correctement :
Une solution
Vous devez être connecté pour voir le contenu.
Vous pouvez inspecter le code HTML généré par l'exécution du code Javascript par votre navigateur (clic droit + Inspecter) :
Vous êtes invités à créer un autre tableau pour dessiner un autre labyrinthe :
const labyrinthe = [
...
];
et à actualiser la page dans votre navigateur pour découvrir le nouveau labyrinthe et tester votre application.
Pour générer un tableau de façon aléatoire.
Vous devez être connecté pour voir le contenu.