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/ciel.js · 12.9 Ko
/* ==========================================================================
* js/ciel.js — LE CIEL : soleil, lune, étoiles, nuages et oiseaux
* --------------------------------------------------------------------------
* Quatre fonctions fabriques indépendantes, assemblées par creerCiel() :
*
* creerAstres() le soleil (avec son halo) et la lune (avec ses cratères),
* qui décrivent un arc dans le ciel selon l'HEURE ;
* creerEtoiles() des points blancs visibles seulement la nuit ;
* creerNuages() des nuages (amas de sphères) qui dérivent et reviennent ;
* creerOiseaux() des oiseaux qui volent en cercle en battant des ailes.
*
* Le ciel est « dans » le diorama, derrière l'île (z négatif) : si on tourne
* la scène, on voit le soleil passer d'un côté ou de l'autre, comme sur une
* maquette. Le DÉGRADÉ de couleurs du fond, lui, est du CSS (voir main.js).
*
* Dépend de : Zdog, Baie.CONFIG, Baie.QUALITE, Baie.Palette.
* ========================================================================== */
(function () {
'use strict';
var TAU = Zdog.TAU;
// ---- Constantes réglables -------------------------------------------------
var ARC_RX = 150; // demi-largeur de l'arc du soleil (axe x)
var ARC_RY = 125; // hauteur de l'arc du soleil (axe y, vers le haut)
var ARC_Y0 = 8; // niveau du « centre » de l'arc : un peu SOUS la mer
var ARC_Z = -125; // profondeur : derrière l'île
var HORIZON = -4; // un astre dont y > -4 est sous l'horizon : on le cache
var NUAGE_LIMITE = 230; // un nuage qui sort à droite (x > 230) revient à gauche
/** Hasard reproductible (voir palmier.js pour l'explication). */
function hasardAvecGraine(graine) {
var a = graine >>> 0;
return function () {
a = (a + 0x6D2B79F5) >>> 0;
var t = a;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
// ==========================================================================
// 1) SOLEIL ET LUNE
// ==========================================================================
/**
* Construit le soleil et la lune.
* @param {Zdog.Anchor} parent Où les accrocher.
* @returns {Object} { placer(heure), appliquerPalette(p) }
*/
function creerAstres(parent) {
// Le SOLEIL : un Group contenant un grand halo transparent PUIS le disque.
// (Group = ordre de dessin garanti : le halo est dessiné en premier.)
var groupeSoleil = new Zdog.Group({ addTo: parent, translate: { z: ARC_Z } });
var halo = new Zdog.Shape({
addTo: groupeSoleil,
stroke: 58, // un point épais = un grand disque
color: 'rgba(255,230,120,0.3)', // rgba : le dernier nombre est l'opacité
});
var soleil = new Zdog.Shape({
addTo: groupeSoleil,
stroke: 26,
color: '#ffe36a',
});
// La LUNE : un disque et trois cratères plus sombres, légèrement devant.
var groupeLune = new Zdog.Group({ addTo: parent, translate: { z: ARC_Z } });
var lune = new Zdog.Shape({
addTo: groupeLune,
stroke: 22,
color: '#e8eefc',
});
new Zdog.Shape({ addTo: groupeLune, stroke: 5, translate: { x: -4, y: -3, z: 6 }, color: '#c3cbe0' });
new Zdog.Shape({ addTo: groupeLune, stroke: 3.5, translate: { x: 4, y: 3, z: 6 }, color: '#c3cbe0' });
new Zdog.Shape({ addTo: groupeLune, stroke: 3, translate: { x: 3, y: -5, z: 6 }, color: '#c3cbe0' });
/**
* Place les astres sur leur arc selon l'heure.
*
* Le soleil se lève à 6 h (à gauche), culmine à 12 h (en haut) et se
* couche à 18 h (à droite). On utilise un angle de 0 à π (demi-cercle) :
* angle = (heure - 6) / 12 × π
* x = -RX × cos(angle) y = Y0 - RY × sin(angle)
* La lune fait exactement l'inverse : elle est à l'opposé sur le cercle.
*
* @param {number} heure Heure décimale (0 à 24).
*/
function placer(heure) {
var angle = ((heure - 6) / 12) * Math.PI;
var c = Math.cos(angle);
var s = Math.sin(angle);
groupeSoleil.translate.x = -ARC_RX * c;
groupeSoleil.translate.y = ARC_Y0 - ARC_RY * s;
groupeSoleil.visible = groupeSoleil.translate.y < HORIZON; // caché sous l'horizon
groupeLune.translate.x = ARC_RX * c; // opposé du soleil
groupeLune.translate.y = ARC_Y0 + ARC_RY * s;
groupeLune.visible = groupeLune.translate.y < HORIZON;
}
/** Colore les astres selon la palette. */
function appliquerPalette(p) {
soleil.color = p.css.soleil;
halo.color = Baie.Palette.cssAlpha(p.rgb.soleil, 0.3);
lune.color = p.css.lune;
}
return { placer: placer, appliquerPalette: appliquerPalette };
}
// ==========================================================================
// 2) ÉTOILES
// ==========================================================================
/**
* Construit un champ d'étoiles (points blancs au loin, derrière la scène).
* @param {Zdog.Anchor} parent Où l'accrocher.
* @param {number} nombre Nombre d'étoiles (moins sur mobile).
* @returns {Object} { appliquerPalette(p) } — affiche ou cache selon la nuit.
*/
function creerEtoiles(parent, nombre) {
var hasard = hasardAvecGraine(42);
// Un seul Group : on peut le montrer / cacher d'un coup avec `visible`.
var groupe = new Zdog.Group({ addTo: parent, translate: { z: -150 } });
for (var i = 0; i < nombre; i++) {
new Zdog.Shape({
addTo: groupe,
// Répartition aléatoire dans le ciel (x de -200 à 200, y de -170 à -35).
translate: { x: (hasard() - 0.5) * 400, y: -35 - hasard() * 135 },
stroke: 1.5 + hasard() * 2, // des étoiles de tailles variées
color: '#ffffff',
});
}
function appliquerPalette(p) {
groupe.visible = p.etoiles > 0.35;
}
return { appliquerPalette: appliquerPalette };
}
// ==========================================================================
// 3) NUAGES
// ==========================================================================
/**
* Construit des nuages qui dérivent lentement de gauche à droite.
*
* Chaque nuage = un Group de 4 sphères (Shape sans path, stroke épais)
* de tailles différentes, côte à côte.
*
* @param {Zdog.Anchor} parent Où les accrocher.
* @param {number} nombre Nombre de nuages.
* @returns {Object} { mettreAJour(dt), appliquerPalette(p) }
*/
function creerNuages(parent, nombre) {
var hasard = hasardAvecGraine(9);
var nuages = []; // { groupe, vitesse }
var spheres = []; // toutes les sphères, pour les recolorer d'un coup
for (var n = 0; n < nombre; n++) {
var groupe = new Zdog.Group({
addTo: parent,
translate: {
x: -NUAGE_LIMITE + (n / nombre) * 2 * NUAGE_LIMITE + hasard() * 40, // étalés au départ
y: -(98 + hasard() * 36), // en altitude
z: -(30 + hasard() * 80), // derrière l'île
},
updateSort: true,
});
// Les 4 sphères : positions et diamètres fixes (relatifs au groupe).
var parts = [
{ x: -22, y: 3, d: 24 },
{ x: -6, y: -3, d: 34 },
{ x: 14, y: 0, d: 30 },
{ x: 30, y: 4, d: 20 },
];
for (var k = 0; k < parts.length; k++) {
spheres.push(new Zdog.Shape({
addTo: groupe,
translate: { x: parts[k].x, y: parts[k].y },
stroke: parts[k].d,
color: '#ffffff',
}));
}
nuages.push({ groupe: groupe, vitesse: 4 + hasard() * 5 }); // 4 à 9 unités/s
}
/**
* Fait dériver les nuages. dt = 0 en pause : les nuages s'arrêtent.
* @param {number} dt Temps depuis l'image précédente (secondes).
*/
function mettreAJour(dt) {
for (var i = 0; i < nuages.length; i++) {
var g = nuages[i].groupe;
g.translate.x += nuages[i].vitesse * dt;
if (g.translate.x > NUAGE_LIMITE) { g.translate.x = -NUAGE_LIMITE; } // on boucle
}
}
function appliquerPalette(p) {
for (var i = 0; i < spheres.length; i++) { spheres[i].color = p.css.nuage; }
}
return { mettreAJour: mettreAJour, appliquerPalette: appliquerPalette };
}
// ==========================================================================
// 4) OISEAUX
// ==========================================================================
/**
* Construit des oiseaux qui tournent autour de l'île en battant des ailes.
*
* Un oiseau = une ancre contenant un corps (point épais) et deux ailes.
* Chaque aile est une ancre (pour pouvoir la faire pivoter) contenant une
* ligne brisée en forme de « v » aplati.
*
* Repère de l'oiseau : il avance vers +x ; ses ailes s'étendent sur l'axe z.
* Battre des ailes = faire tourner chaque aile autour de l'axe x.
*
* @param {Zdog.Anchor} parent Où les accrocher.
* @param {number} nombre Nombre d'oiseaux.
* @returns {Object} { mettreAJour(t, dt), appliquerPalette(p) }
*/
function creerOiseaux(parent, nombre) {
var hasard = hasardAvecGraine(77);
var COULEUR = '#27283a';
// Un Group : on peut cacher tous les oiseaux d'un coup (la nuit).
var groupe = new Zdog.Group({ addTo: parent, updateSort: true });
var oiseaux = [];
for (var n = 0; n < nombre; n++) {
var corps = new Zdog.Anchor({ addTo: groupe });
new Zdog.Shape({ addTo: corps, stroke: 3.2, color: COULEUR }); // le corps
// Aile gauche (z négatif) et aile droite (z positif) : formes miroirs.
var aileG = new Zdog.Anchor({ addTo: corps });
new Zdog.Shape({
addTo: aileG,
path: [{ z: 0, y: 0 }, { z: -6, y: -2 }, { z: -12, y: 1 }],
closed: false, stroke: 1.6, color: COULEUR,
});
var aileD = new Zdog.Anchor({ addTo: corps });
new Zdog.Shape({
addTo: aileD,
path: [{ z: 0, y: 0 }, { z: 6, y: -2 }, { z: 12, y: 1 }],
closed: false, stroke: 1.6, color: COULEUR,
});
oiseaux.push({
corps: corps, aileG: aileG, aileD: aileD,
rayon: 55 + hasard() * 65, // rayon de son cercle de vol
altitude: -(88 + hasard() * 22), // hauteur de vol (y négatif)
vitesse: (0.22 + hasard() * 0.2) * (hasard() < 0.5 ? 1 : -1), // sens aléatoire
phase: hasard() * TAU, // position de départ sur le cercle
battement: 7 + hasard() * 3, // fréquence des battements d'ailes
});
}
var angleCourant = 0; // temps « de vol » cumulé (s'arrête en pause)
/**
* Fait voler les oiseaux.
* @param {number} t Temps total (pour les battements, déjà arrêté en pause).
* @param {number} dt Temps depuis l'image précédente.
*/
function mettreAJour(t, dt) {
angleCourant += dt;
for (var i = 0; i < oiseaux.length; i++) {
var o = oiseaux[i];
var a = o.phase + o.vitesse * angleCourant; // angle sur son cercle
var x = o.rayon * Math.cos(a);
var z = o.rayon * Math.sin(a);
o.corps.translate.x = x;
o.corps.translate.z = z;
o.corps.translate.y = o.altitude + 3 * Math.sin(t * 0.9 + o.phase); // petit ondoiement
// Cap : même formule que pour la pirogue (dérivée de la position).
var sens = o.vitesse > 0 ? 1 : -1;
o.corps.rotate.y = Math.atan2(-o.rayon * Math.cos(a) * sens, -o.rayon * Math.sin(a) * sens);
// Battement : les deux ailes montent et descendent en miroir.
var bat = 0.7 * Math.sin(t * o.battement + o.phase);
o.aileG.rotate.x = -bat;
o.aileD.rotate.x = bat;
}
}
function appliquerPalette(p) {
// On cache les oiseaux la nuit (ils dorment).
groupe.visible = p.etoiles < 0.5;
}
return { mettreAJour: mettreAJour, appliquerPalette: appliquerPalette };
}
// ==========================================================================
// ASSEMBLAGE
// ==========================================================================
/**
* Construit tout le ciel.
* @param {Zdog.Anchor} parent Où l'accrocher (le « monde »).
* @returns {Object} { placerAstres(heure), mettreAJour(t, dt), appliquerPalette(p) }
*/
function creerCiel(parent) {
var Q = Baie.QUALITE;
var astres = creerAstres(parent);
var etoiles = creerEtoiles(parent, Q.etoiles);
var nuages = creerNuages(parent, Q.nuages);
var oiseaux = creerOiseaux(parent, Q.oiseaux);
return {
placerAstres: astres.placer,
mettreAJour: function (t, dt) {
nuages.mettreAJour(dt);
oiseaux.mettreAJour(t, dt);
},
appliquerPalette: function (p) {
astres.appliquerPalette(p);
etoiles.appliquerPalette(p);
nuages.appliquerPalette(p);
oiseaux.appliquerPalette(p);
},
};
}
Baie.creerCiel = creerCiel;
})();