Annuaire › Code ouvert
Code publié par son auteur, en lecture seule. « Copier dans mon espace » crée un nouveau site à vous (non publié) que vous pouvez modifier librement.
js/fond.js · 7.3 Ko
/* ============================================================
fond.js - le décor qui défile (effet de PARALLAXE)
============================================================
La parallaxe donne une impression de profondeur : les éléments
LOINTAINS défilent lentement, les éléments PROCHES défilent vite.
Ici, 4 couches (de la plus lente à la plus rapide) :
1. l'eau profonde (image fixe, dessinée une seule fois) ;
2. de petites vaguelettes lointaines (x 0,25 de la vitesse) ;
3. de grandes traînées d'écume (x 0,6) ;
4. les rives avec les palmiers (x 1,0 : au plus près).
Le point de vue est « vu du ciel » : tout défile vers le BAS,
comme si la pirogue avançait vers le haut de l'écran.
============================================================ */
class Fond {
/* constructor : prépare l'image de l'eau et les éléments décoratifs. */
constructor() {
this.decalage = 0; // distance totale "parcourue" par le décor
this.periode = HAUTEUR_JEU + 120; // hauteur au bout de laquelle un élément reboucle
// createGraphics(l, h) crée un 2e canevas invisible : on y dessine UNE fois
// le dégradé de l'eau, puis on le colle à chaque image avec image().
// C'est bien plus rapide que de redessiner 320 rectangles 60 fois par seconde.
this.eau = createGraphics(LARGEUR_JEU, HAUTEUR_JEU);
this.eau.pixelDensity(1);
this.dessinerEau();
// Éléments décoratifs placés au hasard (une seule fois).
this.vaguelettes = this.creerMotifs(26, 14, 26);
this.ecumes = this.creerMotifs(12, 34, 60);
// Palmiers : 6 par rive. `dx` = position entre le bord de l'écran (0) et le sable (1).
this.palmiers = [];
for (let cote = 0; cote < 2; cote++) { // 0 = gauche, 1 = droite
for (let i = 0; i < 6; i++) {
this.palmiers.push({
cote: cote,
y: i * (this.periode / 6) + random(-25, 25),
dx: random(0.15, 0.7),
taille: random(17, 26)
});
}
}
}
/* ----------------------------------------------------------
dessinerEau()
But : dessiner le dégradé vertical dans l'image this.eau.
On appelle les fonctions de dessin SUR l'objet : this.eau.fill()
au lieu de fill() ; sinon on dessinerait sur le canevas principal.
---------------------------------------------------------- */
dessinerEau() {
const haut = color(COULEURS.eauHaut);
const bas = color(COULEURS.eauBas);
this.eau.noStroke();
for (let y = 0; y < HAUTEUR_JEU; y += 2) {
this.eau.fill(lerpColor(haut, bas, y / HAUTEUR_JEU));
this.eau.rect(0, y, LARGEUR_JEU, 2);
}
}
/* ----------------------------------------------------------
creerMotifs(nombre, lMin, lMax)
But : fabriquer des positions aléatoires pour des motifs d'eau.
Retour : tableau d'objets {x, y, l} (l = largeur du motif).
---------------------------------------------------------- */
creerMotifs(nombre, lMin, lMax) {
const liste = [];
for (let i = 0; i < nombre; i++) {
liste.push({
x: random(RIVE + 6, LARGEUR_JEU - RIVE - 6),
y: random(this.periode),
l: random(lMin, lMax)
});
}
return liste;
}
/* ----------------------------------------------------------
mettreAJour(dt, vitesse)
But : faire avancer le décor.
Paramètres : dt = secondes écoulées ; vitesse = unités par seconde
(distance = vitesse x temps)
---------------------------------------------------------- */
mettreAJour(dt, vitesse) {
this.decalage += vitesse * dt;
}
/* ----------------------------------------------------------
positionY(yMonde, facteur)
But : convertir la position "dans le monde" d'un motif en position
à l'écran, pour une couche de vitesse `facteur`.
L'opérateur % (modulo) fait revenir le motif en haut quand il
sort par le bas : le défilement est infini.
---------------------------------------------------------- */
positionY(yMonde, facteur) {
return ((yMonde + this.decalage * facteur) % this.periode) - 60;
}
/* ----------------------------------------------------------
dessiner()
But : dessiner toutes les couches, de l'arrière vers l'avant.
---------------------------------------------------------- */
dessiner() {
// Couche 1 : eau profonde
image(this.eau, 0, 0);
// Couche 2 : petites vaguelettes (lentes)
noFill();
stroke(255, 255, 255, 60);
strokeWeight(2);
for (const m of this.vaguelettes) {
const y = this.positionY(m.y, 0.25);
// arc(x, y, largeur, hauteur, angleDébut, angleFin) : un morceau d'ellipse
arc(m.x, y, m.l, m.l * 0.4, PI + 0.3, TWO_PI - 0.3);
}
// Couche 3 : traînées d'écume (moyennes)
stroke(255, 255, 255, 90);
strokeWeight(3);
for (const m of this.ecumes) {
const y = this.positionY(m.y, 0.6);
arc(m.x, y, m.l, m.l * 0.3, PI + 0.2, TWO_PI - 0.2);
}
// Couche 4 : rives et palmiers (rapides = les plus proches)
this.dessinerRive(0); // gauche
this.dessinerRive(1); // droite
for (const p of this.palmiers) {
this.dessinerPalmier(p);
}
}
/* ----------------------------------------------------------
dessinerRive(cote)
But : dessiner une bande de sable à bord ondulé.
Paramètre : cote = 0 (gauche) ou 1 (droite)
beginShape()/vertex()/endShape() : on trace un polygone
point par point. Le bord ondule avec sin().
---------------------------------------------------------- */
dessinerRive(cote) {
// On dessine deux fois : d'abord l'écume (un peu plus large), puis le sable.
const couches = [
{ extra: 9, couleur: color(255, 255, 255, 110) },
{ extra: 0, couleur: color(COULEURS.sable) }
];
noStroke();
for (const c of couches) {
fill(c.couleur);
beginShape();
vertex(cote === 0 ? 0 : LARGEUR_JEU, -10);
for (let y = -10; y <= HAUTEUR_JEU + 10; y += 16) {
// Le bord dépend de (y - décalage) : il "glisse" vers le bas avec le décor.
const bord = RIVE + c.extra + 5 * sin((y - this.decalage) * 0.025);
vertex(cote === 0 ? bord : LARGEUR_JEU - bord, y);
}
vertex(cote === 0 ? 0 : LARGEUR_JEU, HAUTEUR_JEU + 10);
endShape(CLOSE);
}
}
/* ----------------------------------------------------------
dessinerPalmier(p)
But : dessiner un palmier vu du dessus (étoile de palmes).
Paramètre : p = objet {cote, y, dx, taille}
push()/translate()/rotate()/pop() : on déplace le repère sur
le palmier, on tourne pour chaque palme, puis on restaure.
---------------------------------------------------------- */
dessinerPalmier(p) {
const y = this.positionY(p.y, 1.0);
// Position horizontale : entre le bord de l'écran et le sable.
const xGauche = 6 + p.dx * (RIVE - 22);
const x = p.cote === 0 ? xGauche : LARGEUR_JEU - xGauche;
push();
translate(x, y);
noStroke();
fill(COULEURS.palmeFonce);
for (let k = 0; k < 7; k++) {
push();
rotate(k * TWO_PI / 7 + p.y); // +p.y : chaque palmier est tourné différemment
ellipse(0, -p.taille * 0.5, p.taille * 0.35, p.taille);
pop();
}
fill(COULEURS.palmeClair);
circle(0, 0, p.taille * 0.35);
pop();
}
}