TD15 : Interface Web
L'objectif de ce TD est de réaliser une interface Web pour notre jeu, en s'appuyant sur les technologies web frontend : HTML5, CSS3 et JavaScript (ES6). Comme il serait fastidieux de re-coder notre bibliothèque game en JavaScript, nous allons utiliser la technologie Web Assembly (ou Wasm) pour traduire automatiquement notre bibliothèque game en JavaScript.
Technologies Web
Afin de bien commencer, voici une présentation rapide des technologies Web, dont nous allons avoir besoin pour réaliser ce TP.
HTML, CSS et JavaScript
Commencez par lire attentivement le tutoriel ci-dessous sur les technologies HTML, CSS et JavaScript qui sont le cœur du Web.
👉 Réalisez les exercices préliminaires présents dans ce tutoriel, afin de vérifier votre bonne compréhension de ces technologies.
Canvas
Afin de faire un dessin 2D dans une page Web, il existe une balise HTML
particulière appelée <canvas>
. Pour dessiner dans un canvas (et interagir
avec), il faut programmer en JavaScript...
La première étape consiste à ajouter un élément <canvas>
de la taille
souhaitée dans sa page HTML.
<canvas id='mycanvas' width="400px" height="400px"></canvas>
On donnera à cet élément un identifiant unique, comme mycanvas
, afin de faire
référence à cet élément dans le code JavaScript. Dessinons par exemple un carré
jaune de taille 40x40 pixels, à la position (100,100) dans le canvas :
var canvas = document.getElementById('mycanvas');
var ctx = canvas.getContext('2d');
ctx.save();
ctx.fillStyle = 'yellow';
ctx.fillRect(100, 100, 40, 40);
ctx.restore();
Plutôt qu'un long discours, voici le code d'une petite démo : canvas/ que vous pouvez tester en chargeant le fichier demo.html.
Vous noterez que cette démo est interactive et redessine l'image à chaque clic
souris gauche ou droit, en changeant la position de l'image mario ou du carré
jaune... Le dessin est effectué par la fonction drawCanvas()
, qui utilise le
contexte ctx
, pour définir le style courant à appliquer au dessin d'une figure
(couleur, épaisseur trait, ...). Il convient de restaurer ce contexte après le
dessin de chaque figure. Notre fonction drawCanvas()
est appelée une première
fois au chargement de la page web (event load
de window
), puis à nouveau
après chaque évènement souris (event click et contextmenu de canvas
). Vous
remarquerez que la fonction drawCanvas()
prend soin d'effacer le canvas
entièrement à chaque nouvel appel : ctx.clearRect(0, 0, width, height)
.
👉 Reprenez votre page demo.html
et ajoutez un canvas avec un petit dessin
personnel.
Un peu de documentation sur les canvas :
- API : https://developer.mozilla.org/fr/docs/Web/API/Canvas_API
- Tutoriel : https://developer.mozilla.org/fr/docs/Web/API/Canvas_API/Tutorial
Web Assembly
Nous allons maintenant nous intéresser à la technologie WebAssembly (ou Wasm), et en particulier à l'outil Emscripten afin de traduire automatiquement notre bibliothèque game (écrite en C) en un module JavaScript portable, et utilisable sur le web !
Nous allons commencer par considérer le petit exemple wasm/, qui définit deux fonctions en langages C :
int int_add(int x, int y)
{
return x + y;
}
int int_mul(int x, int y)
{
return x * y;
}
Nota Bene : Même s'il est possible d'utiliser des types complexes, nous nous limiterons ici à l'usage de types simples.
Afin de pouvoir utiliser ces fonctions dans une page Web, il faut commencer par
annoter ce code avec le mot-clé EMSCRIPTEN_KEEPALIVE
pour déclarer les
fonctions que l'on souhaite exporter dans notre module JavaScript (cf.
math.c).
Vous pouvez ensuite compiler ce code avec le compilateur emcc
, ce qui va ainsi
produire notre module math.js
(ainsi que le fichier associé math.wasm
).
emcc math.c -o math.js -s EXPORTED_RUNTIME_METHODS=ccall,cwrap
Avertissement : Hélas, le compilateur emcc
n'est pas disponible sur les
machines du CREMI (actuellement en Debian 10). Pour compiler ce module, il vous
faudra utiliser le serveur boursouflet du CREMI, qui dispose d'un système
Ubuntu plus récent (22.04) avec ce compilateur.
Vous pouvez néanmoins installer emcc
sur les systèmes Debian/Ubuntu récents,
comme ceci :
sudo apt install emscripten
Afin d'utiliser nos deux fonctions int_add()
et int_mul()
dans une page web,
nous allons devoir charger le script math.js
généré précédemment. Les
fonctions sont alors accessibles en JavaScript via la variable globale Module
,
sous le nom _int_add()
et _int_mul()
, comme ceci :
<script>
var x = 10; var y = 20;
var result = Module._int_add(x, y);
</script>
<script src="math.js"></script>
Afin de rendre notre exemple plus interactif, nous allons utiliser des balises
<input>
de types number
et button
, pour respectivement saisir les entiers
x
et y
et choisir la fonction add
ou mul
à invoquer.
Vous pouvez consulter le code de cette démo dans wasm/, et la
tester en chargeant le fichier test.html. Il ne faut pas
oublier de copier les fichiers math.js
et math.wasm
dans le même répertoire
que la page web qui les utilise. En revanche, il n'est pas utile de mettre sur
le web le code source math.c
. De plus, il n'est pas nécessaire de recompiler
ces fichiers si vous changer de machine, car JavaScript et Wasm sont entièrement
portables.
Avertissement : Attention, si vous testez cette démo en local, il faut
absolument utiliser le protocole http://
et non file://
, ce qui provoquerait
sinon un échec !
Retrouvez le code cette démo sur GitHub : https://github.com/orel33/demo-wasm.
👉 Reprenez votre page demo.html
et complétez-la d'une petite fonction en C,
qui calcule le terme N
de la suite de Fibbonnaci.
Un peu de documentation en complémentaire :
- https://emscripten.org/docs/getting_started/Tutorial.html
- https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html
- https://developer.mozilla.org/fr/docs/WebAssembly/C_to_wasm
Interface Web pour notre jeu
Voici une proposition de plan de travail pour réaliser une interface web à notre jeu :
- Considérons la démo suivante : game-web/, qui va servir de point de départ pour votre interface web.
- Dans votre dépôt GitLab, créez un répertoire
web
et recopiez les fichiers disponibles dans l'archive make-game-web.zip. - Étudiez le code fourni... En particulier, vous remarquerez dans le fichier
demo.js le code ci-dessous qui indique d'appeler la
fonction
start()
lorsque l'évènementonRuntimeInitialized
survient, c'est-à-dire lorsque notre module Wasm est entièrement chargé. Il ne convient pas d'appeler les fonctions duModule
avant.
Module.onRuntimeInitialized = () => { start(); }
function start() {
console.log("call start routine");
/* ... */
}
- Dans le sous-répertoire
web/src/
, vous trouvez la version enseignante (V2) du jeu, sans le fichiergame_tools.h
. - Si vous le souhaitez, vous pouvez remplacer les fichiers sources par ceux de
votre propre bibliothèque game. Dans ce cas supprimez les fichiers dans
src/
et ajoutez des liens symboliques (ln -s
) vers vos propres fichiers sources (.c
et.h
). - Si besoin, éditez le fichier
wrapper.c
pour définir précisément les fonctions que vous souhaitez exposer dans le module JavaScript... En particulier, il est utile de rajouter les fonctionsgame_random()
etgame_solve()
pour rendre votre jeu web plus intéressant ! - Vous pouvez ensuite compiler ce module sur le serveur
boursouflet
du CREMI en utilisant leMakefile
fourni.
$ ssh boursouflet
$ make
emcc -I src -c wrapper.c -o wrapper.o
emcc -I src -c src/game_aux.c -o src/game_aux.o
emcc -I src -c src/game.c -o src/game.o
emcc -I src -c src/game_ext.c -o src/game_ext.o
emcc -I src -c src/game_private.c -o src/game_private.o
emcc -I src -c src/queue.c -o src/queue.o
emar rcs libgame.a src/game_aux.o src/game.o src/game_ext.o src/game_private.o src/queue.o
emcc wrapper.o libgame.a -o game.js -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_RUNTIME_METHODS=ccall,cwrap
- Vérifiez que la démo
game-web
fonctionne sur le serveur web du CREMI, comme ceci : demo. - Renommez le fichier
demo.html
engame.html
, et modifiez-le pour afficher la grille du jeu par défaut dans un canvas HTML. On se limitera pour commencer à un affichage simple pour une grille carrée de taille fixe, qui occupe tout le canvas. - Prenez ensuite en compte les événements clicks sur le canvas pour permettre à un utilisateur d'interagir et de jouer à notre jeu...
- Pour améliorer votre jeu, ajoutez des boutons restart, undo, redo, solve et random.
- Vous pouvez également améliorer l'affichage de votre interface web, en ajustant dynamiquement la taille du canvas en fonction de la taille de la fenêtre. Dans ce cas, il vous sera nécessaire de réagir à l'évènement resize de l'objet window...
- Pour aller plus loin, rendez votre interface web responsive afin qu'elle s'affiche correctement à la fois sur votre écran d'ordinateur et sur votre smartphone...
Rendu Moodle : Finalement, effectuez le rendu de votre travail sur Moodle, en respectant les consignes indiquées.