Le background est un élément important, puisque c'est lui donnera la principale ambiance. Il peut être noir, coloré, ou composé de lignes (layers) qui peuvent donner une impression de profondeur. Dans tout les cas, à partir du moment ou le background effectue un scrolling, il faut s'arranger pour qu'il soit toujours visible.
Prenons un exemple de Background (tiré de Lionheart):
![]() |
![]() |
![]() |
![]() |
En utilisant les méthodes fournies par le moteur (Sprite, AnimatedSprite) il est facile de charger le Backdrop, de charger les Clouds et de les découper en fonction du nombre de nuage, de charger la Mountain, et de composer le tout pour obtenir le Background.
La vitesse de déplacement est primordiale pour maximiser les effets de profondeur. Plus l'élément est sensé être loin du sujet, plus il doit se déplacer lentement. A l'inverse, plus il se trouve proche du sujet, plus il son déplacement doit être rapide. Notre background étant découpé en plusieurs niveaux, il est facile d'attribuer une vitesse à chaque ligne le composant.
Au niveau de la programmation cela revient à la chose suivante: (Code donné purement à titre illustratif, ce n'est pas l'implémentation finale !)
//On va supposer que la resolution du jeu est en 320*240
// Il faut rendre accessible les ressources du sous-jeu en tout point du code
Sprite backdrop = null;
Sprite mountain = null;
AnimatedSprite clouds = null;
double cloudDatas[6][3];
/* Fonction de chargement du sous-jeu */
void load() {
backdrop = new Sprite("backdrop.png");
backdrop.resize(320, 240); // redimensionnement pour remplir l'ecran
mountain = new Sprite("mountains.png");
// Le ciel est compose de 6 lignes,
// on va donc le decouper en 1 frame en largeur, et 6 images en hauteur
clouds = new AnimatedSprite("clouds.png", 1, 6);
for (int i = 0; i < 6; i++) {
cloudData[i][0] = 0.0;
cloudData[i][1] = i * 26; // la hauteur fait 160, pour 6 lignes, soit 160 / 6
cloudData[i][2] = 3 - i * 0.5; // le premier nuage va vite, le dernier moins vite
}
}
/* Fonction de mise a jour du sous-jeu (avec extrapolation) */
void update(double extrp) {
double speed = 1.0 * extrp;
mountain.move(speed, 0.0);
if (mountain.getX() > screenWidth()) {
mountain.place(0 - mountain.getWidth(), mountain.getY());
}
for (int i = 0; i < 6; i++) {
cloudData[i][0] += cloudData[i][2];
if (cloudData[i][0] > screenWidth()) {
cloudData[i][0] = screenWidth();
}
}
}
void render(Graphics2D out) {
// Rendu du fond degrade
backdrop.render(out, 0, 0);
int x = mountain.getX();
int y = 240 - mountain.getHeight() // on place mountain de telle sorte qu'il soit en bas de l'ecran
mountain.render(out, x - mountain.getWidth(), y); // mountain a gauche (-160 - 0)
mountain.render(out, x, y); // mountain au centre (0 - 160)
mountain.render(out, x + mountain.getWidth(), y); // mountain a droite (160 - 320)
// On affiche chaque nuage a ses coordonnees, calculees precedement
for (int i = 0; i < 6; i++) {
clouds.render(cloudData[i][0], cloudData[i][1]);
}
}
Évidement, ce programme n'est pas flexible du tout, et il est fixé pour 6 lignes de nuages. Mais rien ne vous empêche de faire ça pour un cas plus général. Si je l'ai mis sous cette forme, c'est pour bien comprendre le principe, tant sur le l'aspect théorique que pratique. Rendez-vous compte de tout ce qui est caché grâce au moteur de jeu !
Il faut avoir en tête que lors du rendu, on peut tout à fait déborder de l'écran. C'est d'ailleurs recommandé pour éviter d'avoir des zones vide lors du scrolling. Voici une petite illustration pour mieux comprendre:
Par contre, dans notre programme de test, on ne gère qu'un sens de mouvement. Il faudra faire la même chose dans le cas où l'abscisse de l'élément est inférieur à '0 - element.getWidth()' (soit lorsque l'élément aura complètement quitté la zone visible à l'écran). C'est pour cela que lors du rendu, il faudra prévoir un élément de plus étant donné qu'il y aura toujours un moment où un élément aura une partie qui quittera la zone visible à l'écran (à droite ou à gauche).
Lire la suite: Les Jeux de Plateforme - La Map