---> Introduction
---> Contexte
---> Explication
---> Solution
---> Conclusion
Introduction
Si vous êtes en train de lire cet article
c’est que vous avez fait un assez bon
parcours à travers mes précédents tutoriels sur la magnifique bibliothèque Libgdx,
maintenant nous allons présenter dans ce tutoriel un récapitulatif de la
majorité de ce qu’on a vu et cela en un seul code source.
Contexte
On va réaliser un jeu simple et connu qui
s’appelle Mario Sokobane. On va exploiter que les connaissances acquises dans
les précédents tutoriels. Donc, je vous conseille d’essayer de réaliser se jeu
en suivant les explications données, cela va vous faire un bon TP, et si vous
avez essayé mais sans succès, ne vous en fait pas. Vous pouvais lire et
comprendre le code source et ceci vous
sera très utile pour comprendre comment fonctionne les choses avec Libgdx
Explication
Le principe du Sokobane est connu, vous
avez des cases qu’il faut les mettre dans des objectives.
Une fois tous les objectives remplit vous
passez à un autre niveau
Il faut commencer par définir les
ressources à utilisées
Les images :
les images que vous allez utiliser sont
des images trouvées à l’aide de Google et modifiées pour avoir une taille de
32x32 ainsi qu’un fond transparent.
Case : case qui va être poussé par le
petit Mario, télécharger ici
Mur : mur infranchissable par mario, telecharger ici
Mariob : image de Mario se dirigeant vers
le bas, télécharger ici
Marioh : image de Mario se dirigeant vers
le haut, télécharger ici
Mariog : image de Mario se dirigeant vers
la gauche, télécharger ici
Mariod : image de Mario se dirigeant vers
la droite, télécharger ici
Haut : à clicker/presser pour
deplacer mario vers le haut, télécharger ici
Bas : à clicker/presser pour deplacer
mario vers le bas, télécharger ici
Droite : à clicker/presser pour
deplacer mario vers la droite, télécharger ici
Gauche : à clicker/presser pour
deplacer mario vers la gauche, télécharger ici
Les fichiers
textes :
Les fichiers textes représentent les
cartes de chaque niveau, et chaque fichier représentant un niveau contient des
chiffres qui représentent une image de la carte comme suite :
Une fois les ressources téléchargées, il faut bien sur les mettre dans le fichier assets des deux projets Android/Desktop que vous devez créer.
Le code source
Avant de commencer à voir comment sera
mon code source que vous allez vous-même développer sachez qu’il existe
plusieurs voir même meilleurs façons de réaliser ce jeu, donc si vous estimez capable de le réaliser
vous-même avec votre propre logique allez-y, sinon vous pouvez suivre ma
logique de faire les choses, c’est à vous de voir.
Commencez par déclarez les variables qui
vont définir les ressources à utiliser de type par exemple : Texture,
Spritebatch …
Puis initialisez les dans la méthode
create()
Créer une méthode qui charge un fichier
texte selon un niveau donnés en paramètre, pour cela utilisez le stockage
interne
Le déplacement de Mario se fera en 32px
par 32px cela va vous faciliter la gestion des mouvements de Mario
Utiliser un tableau à deux dimensions qui
contiendra la carte (le niveau) et qui va subir des modifications selon
l’action des utilisateurs
Créer une fonction qui se charge des
entrées utilisateurs
Créer une méthode qui se charge de
dessiner le contenu de tableau en respectant la correspondance de chaque
chiffre
C’était en grosso modo les grande étapes
à effectuées, mais bien sûr il reste pas mal de chose à régler, tel que la fin
de la partie, reprendre une partie, le menu du jeu…
La solution
Sachez que si vous n’avez pas réussi à
programmer tout le jeu ceci n’est pas grave, le plus important c’est d’essayer.
Voici maintenant le code source affiché :
Le code source est décomposé en 3 parties comme nous l’avons déjà
expliqué dans un tutoriel précédant.
* La partie Logique :
package my.works.LibgdxMarioSokobane;
import java.io.IOException;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
public class JeuMario implements ApplicationListener {
private SpriteBatch batch;
private Texture textureMariob;
private Texture textureMariod;
private Texture textureMariog;
private Texture textureMarioh;
private Sprite spriteMario;
private Texture textureBloc_ok;
private Texture textureBloc;
private Texture textureMur;
private Texture textureObjective;
private Texture textureBravo;
private Texture textureDroite;
private Texture textureGauche;
private Texture textureBas;
private Texture textureHaut;
//Variable
private int xMario;
private int yMario;
private int carte[][];
private float xDoight,yDoight;
private float stateTime1;
private boolean maintenu, jeuTermine;
private int niveauPartie ;
//******************************* Charger un niveau
****************************************/
public void chargerCarte(int niveau) throws IOException {
String fichier = null;
if(niveau ==1)
fichier = "carte1.txt";
if(niveau ==2)
fichier = "carte2.txt";
if(niveau==3)
fichier = "carte3.txt";
if(niveau==4)
fichier = "carte4.txt";
FileHandle file = Gdx.files.internal("texte/"+fichier);
String contenu= file.readString();
int k = 0;
for(int i=0 ; i<=11 ; i++)
{
for(int j=0 ; j<=9 ; j++)
{
carte[j][i] = Character.digit(contenu.charAt(k++),
10);
}
k+=2;
}
}
//************************Chercher
position Mario dans la carte ***************************/
public void chercherMario(){
for( int i = 0 ; i<=11 ; i++)
{
for(int j= 0 ; j<=9 ; j++)
if(carte[j][i] == 5)
{
xMario = j;
yMario = (11-i);
carte[j][i] =1;
}
}
}
//*****************************************************************************************/
@Override
public void create() {
batch = new SpriteBatch();
textureMariob = new Texture(Gdx.files.internal("texture/mariob.png"));
textureMariod = new Texture(Gdx.files.internal("texture/mariod.png"));
textureMariog = new Texture(Gdx.files.internal("texture/mariog.png"));
textureMarioh = new Texture(Gdx.files.internal("texture/marioh.png"));
textureBloc_ok = new Texture(Gdx.files.internal("texture/block_ok.png"));
textureBloc = new Texture(Gdx.files.internal("texture/block.png"));
textureMur = new Texture(Gdx.files.internal("texture/mur.png"));
textureObjective = new Texture(Gdx.files.internal("texture/objective.png"));
textureBravo = new Texture(Gdx.files.internal("texture/bravo.png"));
textureBas = new Texture(Gdx.files.internal("texture/bas.png"));
textureHaut = new Texture(Gdx.files.internal("texture/haut.png"));
textureDroite = new Texture(Gdx.files.internal("texture/droite.png"));
textureGauche = new Texture(Gdx.files.internal("texture/gauche.png"));
spriteMario = new Sprite(textureMariob, 0, 0,32, 32);
carte = new int [10][12];
jeuTermine = false;
niveauPartie = 1;
try {
chargerCarte(niveauPartie);
} catch (IOException e) {
e.printStackTrace();
}
chercherMario();
}
@Override
public void dispose() {
}
@Override
public void pause() {
}
//************************* Affichage Graphique de la carte*********************************/
public void chargerCarteGraphique(){
for(int i = 0; i<=9 ;
i++)
{
for(int j = 0 ; j<=11 ;
j++)
{
switch(carte[i][j])
{
case 0 :
batch.draw(textureMur,i*32,(11-j)*32+96);
break;
case 2 :
batch.draw(textureBloc,i*32,(11-j)*32+96);
break;
case 3 :
batch.draw(textureObjective,i*32,(11-j)*32+96);
break;
case 4 :
batch.draw(textureBloc_ok,i*32,(11-j)*32+96);
break;
}
}
}
batch.draw(textureBas, 210, 20);
batch.draw(textureHaut, 265, 20);
batch.draw(textureGauche, 10, 22);
batch.draw(textureDroite, 80, 20);
}
// *********************************Gestion des entrées ***********************************/
public void gestionDesEntrees()
{
Gdx.input.setInputProcessor(new InputProcessor() {
@Override
public boolean touchUp(int x, int y, int arg2, int arg3) {
maintenu =false;
return false;
}
@Override
public boolean touchMoved(int arg0, int arg1) {
return false;
}
@Override
public boolean touchDragged(int arg0, int arg1, int arg2) {
return false;
}
@Override
public boolean touchDown(int x, int y, int arg2, int arg3) {
xDoight = x;
yDoight = y;
maintenu = true;
return false;
}
@Override
public boolean scrolled(int arg0) {
return false;
}
@Override
public boolean keyUp(int arg0) {
return false;
}
@Override
public boolean keyTyped(char arg0) {
return false;
}
@Override
public boolean keyDown(int arg0) {
return false;
}
});
}
//****************************** Gestion deplacement Mario
*********************************/
public void DeplacementMario()
{
gestionDesEntrees();
// Gérer deplacement
Mario vers le haut
if(xDoight>260 && xDoight<324 && yDoight>384 && yDoight<480 && maintenu)
{ // si le doit est posé
sur la flèche haut faire :
maintenu = false;
if(yMario < 11)
{
switch(carte[xMario][11-(yMario)-1]){
case 0 :
break;
case 1 :
yMario++;
break;
case 3 :
yMario++;
break;
case 2 :
if(yMario< 10)
{
stateTime1 += Gdx.graphics.getDeltaTime();
if( stateTime1 > 0.008)
{
switch(carte[(xMario)][11-(yMario)-2]){
case 1:
carte[(xMario)][11-(yMario)-2]=2;
carte[(xMario)][11-(yMario)-1]=1;
yMario++;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
case 3:
carte[(xMario)][11-(yMario)-2]=4;
carte[(xMario)][11-(yMario)-1]=1;
yMario++;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
default :break;
}
}
}
break;
case 4:
if(xMario< 10)
{
stateTime1 += Gdx.graphics.getDeltaTime();
if( stateTime1 > 0.008)
{
switch(carte[(xMario)][11-(yMario)-2]){
case 1:
carte[(xMario)][11-(yMario)-2]=2;
carte[(xMario)][11-(yMario)-1]=3;
yMario++;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
case 3:
carte[(xMario)][11-(yMario)-2]=4;
carte[(xMario)][11-(yMario)-1]=3;
yMario++;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
default :break;
}
}
}
break;
}
}
spriteMario = new Sprite(textureMarioh, 0, 0,32, 32);
}
// Gerer deplacement
Mario vers le bas
if(xDoight>215 && xDoight<260 && yDoight>384 && yDoight<480 && maintenu)
{ // si le doit est posé
sur la flèche bas faire :
maintenu = false;
if(yMario >0)
{
switch(carte[xMario][11-(yMario)+1]){
case 0 :
break;
case 1 :
yMario--;
break;
case 3 :
yMario--;
break;
case 2 :
if(yMario>1)
{
stateTime1 += Gdx.graphics.getDeltaTime();
if( stateTime1 > 0.008)
{
switch(carte[(xMario)][11-(yMario)+2]){
case 1:
carte[(xMario)][11-(yMario)+2]=2;
carte[(xMario)][11-(yMario)+1]=1;
yMario--;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
case 3:
carte[(xMario)][11-(yMario)+2]=4;
carte[(xMario)][11-(yMario)+1]=1;
yMario--;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
default :break;
}
}
}
break;
case 4:
if(yMario>1)
{
stateTime1 += Gdx.graphics.getDeltaTime();
if( stateTime1 > 0.008)
{
switch(carte[(xMario)][11-(yMario)+2]){
case 1:
carte[(xMario)][11-(yMario)+2]=2;
carte[(xMario)][11-(yMario)+1]=3;
yMario--;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
case 3:
carte[(xMario)][11-(yMario)+2]=4;
carte[(xMario)][11-(yMario)+1]=3;
yMario--;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
default :break;
}
}
}
}
}
spriteMario = new Sprite(textureMariob, 0, 0,32, 32);
}
// Gerer le demplecement
de Mario vers la droite
if(xDoight>70 && xDoight<130 && yDoight>384 && yDoight<480 && maintenu)
{ // si le doit est posé
sur la flèche droite faire :
maintenu = false;
if(xMario < 9)
{
switch(carte[(xMario)+1][11-(yMario)]){
case 0 :
break;
case 1 :
xMario++;
break;
case 3 :
xMario++;
break;
case 2 :
if(xMario < 8)
{
stateTime1 += Gdx.graphics.getDeltaTime();
if( stateTime1 > 0.008)
{
switch(carte[(xMario)+2][11-(yMario)]){
case 1:
carte[(xMario)+2][11-(yMario)]=2;
carte[(xMario)+1][11-(yMario)]=1;
xMario++;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
case 3:
carte[(xMario)+2][11-(yMario)]=4;
carte[(xMario)+1][11-(yMario)]=1;
xMario++;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
default :break;
}
}
}
break;
case 4 :
if(xMario < 10)
{
stateTime1 += Gdx.graphics.getDeltaTime();
if( stateTime1 > 0.008)
{
switch(carte[(xMario)+2][11-(yMario)]){
case 1:
carte[(xMario)+2][11-(yMario)]=2;
carte[(xMario)+1][11-(yMario)]=3;
xMario++;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
case 3:
carte[(xMario)+2][11-(yMario)]=4;
carte[(xMario)+1][11-(yMario)]=3;
xMario++;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
default :break;
}
}
}
}
}
spriteMario = new Sprite(textureMariod, 0, 0,32, 32);
}
// Gerer deplacement
mario vers la gauche
if(xDoight>8 && xDoight<70 && yDoight>384 && yDoight<480 && maintenu)
{ // si le doit est posé
sur la flèche gauche faire :
maintenu =false;
if(xMario > 0)
{
switch(carte[(xMario)-1][11-(yMario)]){
case 0 :
break;
case 1 :
xMario--;
break;
case 3 :
xMario--;
break;
case 2 :
if(xMario>1)
{
stateTime1 += Gdx.graphics.getDeltaTime();
if( stateTime1 > 0.008)
{
switch(carte[(xMario)-2][11-(yMario)]){
case 1:
carte[(xMario)-2][11-(yMario)]=2;
carte[(xMario)-1][11-(yMario)]=1;
xMario--;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
case 3:
carte[(xMario)-2][11-(yMario)]=4;
carte[(xMario)-1][11-(yMario)]=1;
xMario--;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
default :break;
}
}
}
break;
case 4 :
if(xMario>1)
{
stateTime1 += Gdx.graphics.getDeltaTime();
if( stateTime1 > 0.008)
{
switch(carte[(xMario)-2][11-(yMario)]){
case 1:
carte[(xMario)-2][11-(yMario)]=2;
carte[(xMario)-1][11-(yMario)]=3;
xMario--;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
case 3:
carte[(xMario)-2][11-(yMario)]=4;
carte[(xMario)-1][11-(yMario)]=3;
xMario--;
stateTime1 = Gdx.graphics.getDeltaTime();
break;
default :break;
}
}
}
}
}
spriteMario = new Sprite(textureMariog, 0, 0,32, 32);
}
}
// ************************** Gérer la fin d'un niveau ***********************************//
public boolean partieFini(){
int cpt = 0;
for( int i = 0 ; i<=11 ;
i++)
{
for(int j= 0 ; j<=9 ;
j++)
if(carte[j][i] == 3)
{
cpt++;
}
}
if(cpt ==0)
return true;
else
return false;
}
//**************************** Gerer le Rechargement d'un niveau
***************************/
public void rechargerNiveau()
{
gestionDesEntrees();
if(yDoight<384)
try {
chargerCarte(niveauPartie);
chercherMario();
} catch (IOException e) {
e.printStackTrace();
}
}
/*******************************************************************************************/
@Override
public void render() {
Gdx.gl.glClearColor(1, 1,
1, 0);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
if(!jeuTermine)
{
batch.begin();
chargerCarteGraphique();
spriteMario.setPosition(xMario*32, yMario*32 + 96);
spriteMario.draw(batch);
batch.end();
DeplacementMario();
rechargerNiveau();
if(partieFini())
{
niveauPartie ++;
if(niveauPartie <=4)
{
try {
chargerCarte(niveauPartie);
chercherMario();
} catch (IOException e) {
e.printStackTrace();
}
}else
jeuTermine = true;
}
}
else
{
batch.begin();
batch.draw(textureBravo, 40, 100);
batch.end();
}
}
@Override
public void resize(int arg0, int arg1) {
}
@Override
public void resume() {
}
}
* La partie Desktop : elle contient le code lanceur de
l’application sous Desktop
package my.works.LibgdxMarioSokobane;
import com.badlogic.gdx.backends.jogl.JoglApplication;
public class Lanceur {
public static void main(String[] args)
{
new JoglApplication (new JeuMario(),"Mario
Sokobane",320,480,false);
}
}
* La partie Android : elle contient le code lanceur de
l’application sous Android
package my.works.LibgdxMarioSokobaneAndroid;
import com.badlogic.gdx.backends.android.AndroidApplication;
import my.works.LibgdxMarioSokobane.JeuMario;
import android.os.Bundle;
public class Main extends AndroidApplication {
@Override
public void onCreate(Bundle
savedInstanceState) {
super.onCreate(savedInstanceState);
initialize(new JeuMario(), false);
}
}
La partie logique se trouve dans le projet desktop avec le lanceur
de l’application, contrairement au lanceur Android qui se trouve dans un projet
appart. Ceci a été déjà bien expliqué dans un tutoriel précédant.
* Voici le résultat sous le Desktop
* Et voilà quelques captures du jeu que je l’ai lancé à travers mon Smartphone
Conclusion
Bon, voilà la fin de ce tutoriel ! Mais
sachez qu’il reste pas mal de chose à
explorer avec Libgdx et ce n’est que le début, et je répète encore une fois
l’important avec ce tutoriel c’est d’essayer de coder puis voir et comprendre
la solution si vous n’y arrivez pas.
Si vous avez des remarques ou des
questions n’hésitez pas à les poster en commentaire. Merci pour votre lecture.
Bonjour, j'aurais une question. Je n'ai pas vraiment compris la phrase "Les fichiers textes représentent les cartes de chaque niveau, et chaque fichier représentant un niveau contient des chiffres qui représentent une image de la carte", donc je ne sais pas comment faire ces cartes. Pourriez vous être plus précis dans la façon de les faire ?
RépondreSupprimerLes fichiers ne sont plus disponibles. Pouvez vous les réuploader s'il vous plait.
RépondreSupprimerMerci pour avoir remis les images. Par contre le code source distribué sur packupload n'est toujours pas disponible :(
RépondreSupprimerBonjour,
RépondreSupprimerJ'ai essayé tes sources pour voir le résultat et je ne sais pas si cela est normal mais une fois exécuté sur mon Nexus 7, la cartede ton sokoban à la taille d'un 3310 et les évènements sur les boutons ne fonctionnent pas...
Serait-il possible de ré-uploader le code source ce super tuto ? ( dropbox, mega , drive ... )
RépondreSupprimerMerci d'avance
BONJOUR MONSIEUR merci pour ce tuto il est bien tres interessant mais mon probleme que le jeux reste bloqhant il ne bouge pas pas de deplacement et merci
RépondreSupprimer