Création d'une carte à base de tuiles
Bonjour à tous et à toutes(il y en a peu, mais ça existe :lol:)
Je suis me suis mis récemment au Java et à Android (j'étais sur C# avant), et j'ai pour but de réussir à coder proprement une application affichant une carte (perso) faite de tuiles(256x256).
En C# je faisais ça avec un Panel et des PictureBox (je sais, c'est pas bien), mais cette fois ci j'aimerai bien directement dessiner dans un Canva... (et si j'y arrive, je transpose le tout su la version C#, ça lui fera du bien)
J'ai stocké les tuiles dans le dossier drawable, et leur nom est de la forme suivante :
- x0y0z0, x0z1y0, ..., xNyNzU
Où N est un multiple de 256 (0 = 0, 1 = 256, ..., N = N*256) et U est le niveau de zoom (0, 1, etc...)
Pour ce qui est de la l'algo, j'imagine qui faudrait créer une collection de Bitmap, initialiser la capacité de la collection avec le nombre maximum de tuiles qui peut être affiché à l'écran :
Code:
1 2 3
| int nbTuilesX = (int)Math.floor((double)largeurEcran / 256) + 2;
int nbTuilesY = (int)Math.floor((double)hauteurEcran / 256) + 2;
int nbTuilesTotal = nbTuilesX * nbTuilesY ; |
Cette collection serait une sorte de SparseArray avec deux clés (abscisse, ordonnée) et pourrait à la volée ajouter et enlever des éléments...
Lors du déplacement de la carte, il faudrait vérifier que la collection contienne les tuiles appropriées et soit les afficher, soit les charger :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| // On prend les coordonnées des coins haut-gauche et bas-droite de l'écran par rapport à l'origine de la carte
int coinHautGaucheX = origineEcranX;
int coinHautGaucheY = origineEcranY;
int coinBasDroiteX = origineEcranX + largeurEcran;
int coinBasDroiteY = origineEcranY + hauteurEcran;
/* On calcul les origines(en multiples de 256) de la tuile la plus en haut à gauche
et la tuile la plus en bas à droite qui doivent être affichées */
int originePremiereTuileX = (int)Math.floor((double)coinHautGaucheX / 256);
int originePremiereTuileY = (int)Math.floor((double)coinHautGaucheY / 256);
int origineDerniereTuileX = (int)Math.floor((double)coinBasDroiteX / 256);
int origineDerniereTuileY = (int)Math.floor((double)coinBasDroiteY / 256);
// On trouve les origines de toutes les tuiles à afficher(en multiples de 256)
for(int x = originePremiereTuileX; x <= origineDerniereTuileX; x++) {
for(int y = originePremiereTuileY; y <= origineDerniereTuileY; y++) {
// Actions sur la tuile(x,y) dont la position est (x*256, y*256)
if(/* Test si la tuile(x,y) existe dans la collection */
// Afficher la tuile(x,y)
else
// Charger nouvelle tuile(x,y)
}
} |
A partir d'ici je suis un peu perdu...
Si vous avez la moindre idée ou le moindre conseil, je suis preneur :mrgreen:
J'aimerai vraiment aboutir à un code optimiser pour que la "glisse" soit la plus confortable possible(plus de fps et moins de ms quoi...) ^^
:calim2: Des idées ?
Orientation + Détection du touché
Pour ce qui est de l'orientation, il me semble que pour faire tourner la carte il faut utiliser des matrices de transformation ([ame="http://en.wikipedia.org/wiki/Transformation_matrix"]matrices de transformation[/ame]) (il me semble que c'est bien gérer par DirectX et OpenGL) mais ayant déjà du mal avec un affichage normal, je ne compte pas m'y pencher d'ici un certain temps ^^
En passant, voici un petit bout de code pour la détection du touché :
Code:
1 2 3 4 5 6 7 8 9 10
|
Display ecran = getWindowManager().getDefaultDisplay();
int largeurEcran = ecran.getWidth();
int hauteurEcran = ecran.getHeight();
// Origine de l'écran par rapport à l'origine de la carte
float origineEcranX = 0;
float origineEcranY = 0;
// Vecteur qui sauvegarde la position de la "touche" de l'écran
float vecteurPositionToucheX = 0;
float vecteurPositionToucheY = 0; |
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public boolean onTouchEvent(MotionEvent event) {
float positionToucheX = event.getX(); // Rien de bien compliqué ^^
float positionToucheY = event.getY();
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN: // Initialisation du vecteur
vecteurPositionToucheX = origineEcranX + positionToucheX;
vecteurPositionToucheY = origineEcranY + positionToucheY;
break;
case MotionEvent.ACTION_MOVE: // Modifications de l'origine de la carte en fonction du vecteur et de la position de la "touche"
float nouvelleOrigineEcranX = vecteurPositionToucheX - positionToucheX;
float nouvelleOrigineEcranY = vecteurPositionToucheY - positionToucheY;
if(nouvelleOrigineEcranX > 0 && (nouvelleOrigineEcranX + largeurEcran) < tailleCarteX) // Permet de ne pas scroller en dehors de la carte
origineEcranX = nouvelleOrigineEcranX;
if(nouvelleOrigineEcranY > 0 && (nouvelleOrigineEcranY + hauteurEcran) < tailleCarteY)
origineEcranY = nouvelleOrigineEcranY;
break;
}
} |
Ca fonctionne, mais ça lag un peu... quand même...
Rebonjour à tous,
je viens de terminer un premier jet pour la carte, ça fonctionne, mais ça lag un peu (certainement due au trois boucles imbriquées). J'aimerais votre avis
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Override
public boolean onTouchEvent(MotionEvent event) {
float positionEcranX = event.getX();
float positionEcranY = event.getY();
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
vecteurPositionEcranX = origineEcranX + positionEcranX;
vecteurPositionEcranY = origineEcranY + positionEcranY;
break;
case MotionEvent.ACTION_MOVE:
float nouvelleOrigineEcranX = vecteurPositionEcranX - positionEcranX;
float nouvelleOrigineEcranY = vecteurPositionEcranY - positionEcranY;
if(nouvelleOrigineEcranX > 0 && (nouvelleOrigineEcranX + largeurEcran) < 1536) // Largeur de la carte zoom n°1
origineEcranX = nouvelleOrigineEcranX;
if(nouvelleOrigineEcranY > 0 && (nouvelleOrigineEcranY + hauteurEcran) < 1024) // Hauteur de la carte zoom 1
origineEcranY = nouvelleOrigineEcranY;
initialisationTuiles();
break;
}
return true;
} |
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public void initialisationTuiles() {
int coinHautGaucheX = (int)origineEcranX;
int coinHautGaucheY = (int)origineEcranY;
int coinBasDroiteX = (int)origineEcranX + largeurEcran;
int coinBasDroiteY = (int)origineEcranY + hauteurEcran;
int originePremiereTuileX = (int)Math.floor((double)coinHautGaucheX / 256);
int originePremiereTuileY = (int)Math.floor((double)coinHautGaucheY / 256);
int origineDerniereTuileX = (int)Math.floor((double)coinBasDroiteX / 256);
int origineDerniereTuileY = (int)Math.floor((double)coinBasDroiteY / 256);
Iterator<Tile> TuilesIter;
for(int x = originePremiereTuileX; x <= origineDerniereTuileX; x++) { // Première boucle pour les X
for(int y = originePremiereTuileY; y <= origineDerniereTuileY; y++) { // Deuxième boucle pour les Y
boolean tuileExtiste = false;
if(!Tuiles.isEmpty()) {
TuilesIter = Tuiles.iterator();
while (TuilesIter.hasNext()){ // Troisième boucle pour les Tuiles T_T
Tile Tuile = TuilesIter.next();
int X = Tuile.X();
int Y = Tuile.Y();
if((X < originePremiereTuileX || X > origineDerniereTuileX) || (Y < originePremiereTuileY || Y > origineDerniereTuileY)) { // Hors champ : on vire
TuilesIter.remove();
}
else if (X == x && Y == y) { // Dans le champ : on affiche
Bitmap Image = Tuile.Image();
surface.drawBitmap(Image, (float)(X*256) - origineEcranX, (float)(Y*256) - origineEcranY, null);
tuileExtiste = true; // Puis on dit qu'elle existe pour pas recréer la même tuile plus bas...
}
}
}
if (!tuileExtiste) { // Si finalement la tuile n'existe pas, on la créer et affiche
String Nom = "x" + x + "y" + y + "z" + niveauZoom;
int Id = trouverDrawableId(Nom);
Bitmap Image = ((BitmapDrawable)Main.this.getResources().getDrawable(Id)).getBitmap();
Tile Tuile = new Tile(Image, x, y);
Tuiles.add(Tuile);
surface.drawBitmap(Image, (float)(x*256) - origineEcranX, (float)(y*256) - origineEcranY, null);
}
}
}
view.invalidate(); // Puis on actualise...
} |
Ca fait beaucoup de boucles imbriquées quand même... vous avez des idées ? peut être faire une partie des calculs dans un autre Thread...
petite question toute bête...
Citation:
C'était juste pour savoir si vous avez des idées sur comment afficher des boutons sur la Carte, j'entends par bouton une image cliquable style un bouton plus et un bouton moins sur les coins bas-gauche et bas-droite
J'ai réussi à ajouter des éléments au dessus de la Carte (MySurface) en utilisant la méthode addContentView qui ajoute une vue au FrameLayout parent de l'activité, voici le code :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| public class Main extends Activity {
MySurface view;
TextView textview;
Button button;
/*[...]*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = new MySurface(this); // Initialisation de la Carte
// Initialisation du TextView + quelques paramètres
textview = new TextView(this);
textview.setText(R.string.app_name);
textview.setPadding(10, 10, 10, 10);
textview.setTextColor(Color.WHITE);
textview.setBackgroundColor(Color.argb(99, 00, 00, 00));
// Initialisation du Button + listener pour zoomer
button = new Button(this);
button.setText("+");
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
view.niveauZoom++;
view.initialiserDimensionsCarte(); // Initialise deux int avec les dimensions de la carte suivant niveauZoom
/* Ajouter quelques méthodes dans la classe de la Carte :
- conserver la position sur la Carte en zoomant
- etc...(pour faire plus Class quoi :°)*/
view.Tuiles.clear();
view.initialisationTuiles();
}
});
FrameLayout.LayoutParams params; // Création des paramètres pour addContentView
// Instenciation : width, height
params = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
addContentView(view, params); // On ajoute au FrameLayout qui contient l'activité (du moins il me semble que c'est ça)
// Instenciation : width, height, gravity
params = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
params.setMargins(0, 0, 0, 10); // Une petite marge en bas
addContentView(textview, params);
params = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM);
addContentView(button, params);
}
/*[...]*/
} |
Citation:
Voire aussi des "boutons" sur la Carte style points gps cliquables pour lancer d'autres activités...
Pour les boutons sur la carte par contre, je cherche encore... des idées ?
Ps : j'ai cherché pour les Assets (et j'ai trouvé :°), voici le petit bout de code :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| AssetManager Asset;
/*[...]*/
if (!tuileExtiste) {
Bitmap Image;
Asset = getAssets();
InputStream inputStream = null;
try {
inputStream = Asset.open("zooms/zoom" + niveauZoom + "/" + x+ "." + y + ".jpg");
Image = BitmapFactory.decodeStream(inputStream);
} catch (IOException e) {
Image = ((BitmapDrawable)Main.this.getResources().getDrawable(R.drawable.tuilevide)).getBitmap();
}
Tile Tuile = new Tile(Image, x, y);
Tuiles.add(Tuile);
surface.drawBitmap(Image, (float)(x*256) - origineEcranX, (float)(y*256) - origineEcranY, null);
} |
Pour ce qui est de la réactivité, sur l'émulateur, il semble que ce soit un poil (mais un tout petit poil) plus rapide. Malheureusement ma machine Android frezze lors du boot donc j'ai pas pu tester ça en live (en passant : c'est bizarre car le pilote ADB se lance bien, et la machine est bien reconnue par Eclipse...)
Problème Assets : INSTALL_FAILED_INSUFFICIENT_STORAGE
Bonjour à tous, me revoici avec un problème de taille ^^
Apparemment, je stocke trop d'images dans le dossier assets... voici la hiérarchie du dossier :
- assets
- zooms
- zoom0
- zoom1
- zoom2
- zoom3
- zoom4
- zoom5
Soit 26.38Mo de tuiles au total
Mais au moment de lancer l'application sur l'émulateur (1.5), je me retrouves avec ça dans la Console :
Installation error: INSTALL_FAILED_INSUFFICIENT_STORAGE
et ça dans le LogCat :
ERROR/PackageManager(563): Couldn't copy package file to temp file.
Le pire étant que je prévoyais de mettre des logos et des icones dans le dossier assets (qui devait être téléchargés depuis le web et stockés pour être précis...)
Ps : Je suis ouvert à toute critique et/ou suggestion ^^