Amélioration des performances de la détection des clics sur une grille isométrique de colonne décalée

Je travaille sur un moteur de jeu isométrique et j'ai déjà créé un algorithme pour la détection par clic parfaite des pixels. Visitez le projet et notez que la détection des clics est capable de détecter le bord de la tuile a été cliqué. Il vérifie également l'indice y pour cliquer sur la tuile la plus en avance.

Une explication de mon algorithme actuel:

La grille isométrique est faite d'images de mosaïque qui sont 100 * 65px. TileW=100, TileL=50, tileH=15

Dimensionnement du carreau

La carte est représentée par une map[z][y][x] matrice tridimensionnelle map[z][y][x] .

Les points centraux du carreau (x,y) sont calculés de la manière suivante:

 //x, y, z are the position of the tile if(y%2===0) { x-=-0.5; } //To accommodate the offset found in even rows this.centerX = (x*tileW) + (tileW/2); this.centerY = (y*tileL) - y*((tileL)/2) + ((tileL)/2) + (tileH/2) - (z*tileH); 

Grille isométrique

Fonctions de prototype qui déterminent si la souris se trouve dans une zone donnée sur la mosaïque:

 Tile.prototype.allContainsMouse = function() { var dx = Math.abs(mouse.mapX-this.centerX), dy = Math.abs(mouse.mapY-this.centerY); if(dx>(tileW/2)) {return false;} //Refer to image return (dx/(tileW*0.5) + (dy/(tileL*0.5)) < (1+tileHLRatio)); } 

Tile.prototype.allContainsMouse() renvoie true si la souris est en vert. La zone rouge est coupée en vérifiant si dx> la moitié de la largeur de la tuile

Figure 1


 Tile.prototype.topContainsMouse = function() { var topFaceCenterY = this.centerY - (tileH/2); var dx = Math.abs(mouse.mapX-this.centerX), dy = Math.abs(mouse.mapY-topFaceCenterY); return ((dx/(tileW*0.5) + dy/(tileL*0.5) <= 1)); }; 

Renvoie true si la souris est sur le dessus


 Tile.prototype.leftContainsMouse = function() { var dx = mouse.mapX-this.centerX; if(dx<0) { return true; } else { return false; } }; 

(Si la souris est à gauche du point central)


 Tile.prototype.rightContainsMouse = function() { var dx = mouse.mapX-this.centerX; if(dx>0) { return true; } else { return false; } }; 

(Si la souris est à droite du point central)

Mettre ensemble toutes les méthodes pour fonctionner comme un seul:

  • Boucle sur toute la carte 3D [z] [y] [x] tableau
  • Si allContainsMouse() renvoie true, la carte [z] [y] [x] est la mosaïque sur laquelle notre souris est allContainsMouse() .
  • Ajoutez cette mosaïque au tableau tilesUnderneathMouse array.
  • tilesUnderneathMouse boucle à l'aide de la tilesUnderneathMouse et choisissez la tuile avec le plus haut. C'est la tuile la plus en avance.

     if(allContainsMouse && !topContainsMouse) 

Combinaison inférieure

  •  if(allContainsMouse && !topContainsMouse && leftContainsMouse) 

Match à gauche

(Un concept similaire s'applique à droite)

Enfin, mes questions:

# 1 Comment pourriez-vous accomplir cela, de sorte qu'il soit plus efficace (ne boucle pas toutes les tuiles) (code pesudo accepté)

# 2 Si vous ne parvenez pas à répondre au n ° 1, quelles suggestions avez-vous pour améliorer l'efficacité de ma détection de clic (le chargement par bloc a déjà été pris en compte)

Ce que j'ai pensé:

J'ai essayé à l'origine de résoudre ce problème en n'utilisant pas les points centraux de la tuile, mais en convertissant la position de la souris (x, y) directement sur la tuile x, y. Dans mon esprit, c'est le plus difficile à coder, mais la solution la plus efficace. Sur une grille carrée, il est très facile de convertir une position (x, y) en un carré sur la grille. Toutefois, dans une grille de colonne décalée, vous faites face aux décalages. J'ai essayé de calculer les décalages en utilisant la fonction a qui prend une valeur x ou y et renvoie le décalage résultant y ou x. Le graphique Zig-zag d' arccos (cosx) a résolu cela.

En vérifiant si la souris était dans la tuile, utiliser cette méthode était difficile et je ne pouvais pas comprendre. Je vérifiais si la souris (x, y) était sous une ligne y=mx+b qui dépendait de l'approximation tileX, tileY (une grille carrée approximative).

Si vous venez ici, merci!

Cette réponse est basée sur:

  • Valeurs d'image de grille 2D vers un tableau 2D

Alors voici:

  1. Conversion entre grille et écran

    Comme je l'ai mentionné dans le commentaire, vous devez créer des fonctions qui convergent entre les positions de l'écran et de la grille de la cellule. Quelque chose comme (en C ++ ):

     //--------------------------------------------------------------------------- // tile sizes const int cxs=100; const int cys= 50; const int czs= 15; const int cxs2=cxs>>1; const int cys2=cys>>1; // view pan (no zoom) int pan_x=0,pan_y=0; //--------------------------------------------------------------------------- void isometric::cell2scr(int &sx,int &sy,int cx,int cy,int cz) // grid -> screen { sx=pan_x+(cxs*cx)+((cy&1)*cxs2); sy=pan_y+(cys*cy/2)-(czs*cz); } //--------------------------------------------------------------------------- void isometric::scr2cell(int &cx,int &cy,int &cz,int sx,int sy) // screen -> grid { // rough cell ground estimation (no z value yet) cy=(2*(sy-pan_y))/cys; cx= (sx-pan_x-((cy&1)*cxs2))/cxs; cz=0; // isometric tile shape crossing correction int xx,yy; cell2scr(xx,yy,cx,cy,cz); xx=sx-xx; mx0=cx; yy=sy-yy; my0=cy; if (xx<=cxs2) { if (yy> xx *cys/cxs) { cy++; if (int(cy&1)!=0) cx--; } } else { if (yy>(cxs-xx)*cys/cxs) { cy++; if (int(cy&1)==0) cx++; } } } //--------------------------------------------------------------------------- 

    J'ai utilisé votre mise en page (j'ai essayé de convertir le mien en espérant que je n'ai pas commis d'erreur idiote quelque part):

    disposition

    • La croix rouge représente les coordonnées renvoyées par cell2scr(x,y,0,0,0)
    • La croix verte représente les coordonnées de la souris
    • Aqua highlight représente la position de la cellule renvoyée

    Attention, si vous utilisez des arithmétiques entières, vous devez prendre en compte si vous divisez / multipliez par la moitié des tailles, vous pouvez perdre de la précision. Utilisez la taille réelle et divisez le résultat par 2 pour de tels cas (passez beaucoup de temps à calculer cela dans le passé).

    Le cell2scr est assez simple. La position de l'écran est en pan offset + position de la cellule multipliée par sa taille (étape). L'axe des x nécessite une correction pour les lignes pairs / impaires (c'est-à-dire que ((cy&1)*cxs2) est pour) et l'axe y est déplacé par l'axe z ( ((cy&1)*cxs2) ). L'écran de mine a un point (0,0) dans le coin supérieur gauche, +x axe +x pointe vers la droite et +y est orienté vers le bas.

    Le scr2cell est effectué par une position d'écran résolu de manière algébrique à partir des équations de cell2scr en supposant que z=0 sélectionne uniquement le terrain de grille. En plus, il n'y a que la correction pair / impair ajoutée si la position de la souris est en dehors de la zone cellulaire trouvée.

  2. Scanner les voisins

    Le scr2cell(x,y,z,mouse_x,mouse_y) retourne juste la cellule où votre souris est sur le terrain. Donc, si vous souhaitez ajouter votre fonctionnalité de sélection actuelle, vous devez numériser la cellule supérieure sur cette position et peu de cellules voisines et sélectionner celle avec moins de distance.

    Pas besoin de numériser toute la grille / carte, juste quelques cellules autour de la position retournée. Cela devrait accélérer considérablement les choses.

    Je le fais comme ceci:

    Modèle de balayage

    Le nombre de lignes dépend de la taille de l'axe z la cellule ( czs ), du nombre maximal de couches z ( gzs ) et de la taille de la cellule ( cys ). Le code de mine C ++ de l'exploration est comme suit:

     // grid size const int gxs=15; const int gys=30; const int gzs=8; // my map (all the cells) int map[gzs][gys][gxs]; void isometric::scr2cell(int &cx,int &cy,int &cz,int sx,int sy) { // rough cell ground estimation (no z value yet) cy=(2*(sy-pan_y))/cys; cx= (sx-pan_x-((cy&1)*cxs2))/cxs; cz=0; // isometric tile shape crossing correction int xx,yy; cell2scr(xx,yy,cx,cy,cz); xx=sx-xx; yy=sy-yy; if (xx<=cxs2) { if (yy> xx *cys/cxs) { cy++; if (int(cy&1)!=0) cx--; } } else { if (yy>(cxs-xx)*cys/cxs) { cy++; if (int(cy&1)==0) cx++; } } // scan closest neighbors int x0=-1,y0=-1,z0=-1,a,b,i; #define _scann \ if ((cx>=0)&&(cx<gxs)) \ if ((cy>=0)&&(cy<gys)) \ { \ for (cz=0;(map[cz+1][cy][cx]!=_cell_type_empty)&&(cz<czs-1);cz++); \ cell2scr(xx,yy,cx,cy,cz); \ if (map[cz][cy][cx]==_cell_type_full) yy-=czs; \ xx=(sx-xx); yy=((sy-yy)*cxs)/cys; \ a=(xx+yy); b=(xx-yy); \ if ((a>=0)&&(a<=cxs)&&(b>=0)&&(b<=cxs)) \ if (cz>=z0) { x0=cx; y0=cy; z0=cz; } \ } _scann; // scan actual cell for (i=gzs*czs;i>=0;i-=cys) // scan as many lines bellow actual cell as needed { cy++; if (int(cy&1)!=0) cx--; _scann; cx++; _scann; cy++; if (int(cy&1)!=0) cx--; _scann; } cx=x0; cy=y0; cz=z0; // return remembered cell coordinate #undef _scann } 

    Ceci sélectionne toujours la cellule supérieure (le plus haut possible) lorsque vous jouez avec la souris, elle se sent correctement (au moins pour moi):

    Résultat du balayage

Ici, la source VCL / C ++ complète pour le moteur isométrique de mienne J'ai réussi pour cela aujourd'hui:

  //--------------------------------------------------------------------------- //--- Isometric ver: 1.01 --------------------------------------------------- //--------------------------------------------------------------------------- #ifndef _isometric_h #define _isometric_h //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // colors 0x00BBGGRR DWORD col_back =0x00000000; DWORD col_grid =0x00202020; DWORD col_xside=0x00606060; DWORD col_yside=0x00808080; DWORD col_zside=0x00A0A0A0; DWORD col_sel =0x00FFFF00; //--------------------------------------------------------------------------- //--- configuration defines ------------------------------------------------- //--------------------------------------------------------------------------- // #define isometric_layout_1 // x axis: righ+down, y axis: left+down // #define isometric_layout_2 // x axis: righ , y axis: left+down //--------------------------------------------------------------------------- #define isometric_layout_2 //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- /* // grid size const int gxs=4; const int gys=16; const int gzs=8; // cell size const int cxs=100; const int cys= 50; const int czs= 15; */ // grid size const int gxs=15; const int gys=30; const int gzs=8; // cell size const int cxs=40; const int cys=20; const int czs=10; const int cxs2=cxs>>1; const int cys2=cys>>1; // cell types enum _cell_type_enum { _cell_type_empty=0, _cell_type_ground, _cell_type_full, _cell_types }; //--------------------------------------------------------------------------- class isometric { public: // screen buffer Graphics::TBitmap *bmp; DWORD **pyx; int xs,ys; // isometric map int map[gzs][gys][gxs]; // mouse int mx,my,mx0,my0; // [pixel] TShiftState sh,sh0; int sel_x,sel_y,sel_z; // [grid] // view int pan_x,pan_y; // constructors for compiler safety isometric(); isometric(isometric& a) { *this=a; } ~isometric(); isometric* operator = (const isometric *a) { *this=*a; return this; } isometric* operator = (const isometric &a); // Window API void resize(int _xs,int _ys); // [pixels] void mouse(int x,int y,TShiftState sh); // [mouse] void draw(); // auxiliary API void cell2scr(int &sx,int &sy,int cx,int cy,int cz); void scr2cell(int &cx,int &cy,int &cz,int sx,int sy); void cell_draw(int x,int y,int tp,bool _sel=false); // [screen] void map_random(); }; //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- isometric::isometric() { // init screen buffers bmp=new Graphics::TBitmap; bmp->HandleType=bmDIB; bmp->PixelFormat=pf32bit; pyx=NULL; xs=0; ys=0; resize(1,1); // init map int x,y,z,t; t=_cell_type_empty; // t=_cell_type_ground; // t=_cell_type_full; for (z=0;z<gzs;z++,t=_cell_type_empty) for (y=0;y<gys;y++) for (x=0;x<gxs;x++) map[z][y][x]=t; // init mouse mx =0; my =0; sh =TShiftState(); mx0=0; my0=0; sh0=TShiftState(); sel_x=-1; sel_y=-1; sel_z=-1; // init view pan_x=0; pan_y=0; } //--------------------------------------------------------------------------- isometric::~isometric() { if (pyx) delete[] pyx; pyx=NULL; if (bmp) delete bmp; bmp=NULL; } //--------------------------------------------------------------------------- isometric* isometric::operator = (const isometric &a) { resize(a.xs,a.ys); bmp->Canvas->Draw(0,0,a.bmp); int x,y,z; for (z=0;z<gzs;z++) for (y=0;y<gys;y++) for (x=0;x<gxs;x++) map[z][y][x]=a.map[z][y][x]; mx=a.mx; mx0=a.mx0; sel_x=a.sel_x; my=a.my; my0=a.my0; sel_y=a.sel_y; sh=a.sh; sh0=a.sh0; sel_z=a.sel_z; pan_x=a.pan_x; pan_y=a.pan_y; return this; } //--------------------------------------------------------------------------- void isometric::resize(int _xs,int _ys) { if (_xs<1) _xs=1; if (_ys<1) _ys=1; if ((xs==_xs)&&(ys==_ys)) return; bmp->SetSize(_xs,_ys); xs=bmp->Width; ys=bmp->Height; if (pyx) delete pyx; pyx=new DWORD*[ys]; for (int y=0;y<ys;y++) pyx[y]=(DWORD*) bmp->ScanLine[y]; // center view cell2scr(pan_x,pan_y,gxs>>1,gys>>1,0); pan_x=(xs>>1)-pan_x; pan_y=(ys>>1)-pan_y; } //--------------------------------------------------------------------------- void isometric::mouse(int x,int y,TShiftState shift) { mx0=mx; mx=x; my0=my; my=y; sh0=sh; sh=shift; scr2cell(sel_x,sel_y,sel_z,mx,my); if ((sel_x<0)||(sel_y<0)||(sel_z<0)||(sel_x>=gxs)||(sel_y>=gys)||(sel_z>=gzs)) { sel_x=-1; sel_y=-1; sel_z=-1; } } //--------------------------------------------------------------------------- void isometric::draw() { int x,y,z,xx,yy; // clear space bmp->Canvas->Brush->Color=col_back; bmp->Canvas->FillRect(TRect(0,0,xs,ys)); // grid DWORD c0=col_zside; col_zside=col_back; for (y=0;y<gys;y++) for (x=0;x<gxs;x++) { cell2scr(xx,yy,x,y,0); cell_draw(xx,yy,_cell_type_ground,false); } col_zside=c0; // cells for (z=0;z<gzs;z++) for (y=0;y<gys;y++) for (x=0;x<gxs;x++) { cell2scr(xx,yy,x,y,z); cell_draw(xx,yy,map[z][y][x],(x==sel_x)&&(y==sel_y)&&(z==sel_z)); } // mouse0 cross bmp->Canvas->Pen->Color=clBlue; bmp->Canvas->MoveTo(mx0-10,my0); bmp->Canvas->LineTo(mx0+10,my0); bmp->Canvas->MoveTo(mx0,my0-10); bmp->Canvas->LineTo(mx0,my0+10); // mouse cross bmp->Canvas->Pen->Color=clGreen; bmp->Canvas->MoveTo(mx-10,my); bmp->Canvas->LineTo(mx+10,my); bmp->Canvas->MoveTo(mx,my-10); bmp->Canvas->LineTo(mx,my+10); // grid origin cross bmp->Canvas->Pen->Color=clRed; bmp->Canvas->MoveTo(pan_x-10,pan_y); bmp->Canvas->LineTo(pan_x+10,pan_y); bmp->Canvas->MoveTo(pan_x,pan_y-10); bmp->Canvas->LineTo(pan_x,pan_y+10); bmp->Canvas->Font->Charset=OEM_CHARSET; bmp->Canvas->Font->Name="System"; bmp->Canvas->Font->Pitch=fpFixed; bmp->Canvas->Font->Color=clAqua; bmp->Canvas->Brush->Style=bsClear; bmp->Canvas->TextOutA(5, 5,AnsiString().sprintf("Mouse: %ix %i",mx,my)); bmp->Canvas->TextOutA(5,20,AnsiString().sprintf("Select: %ix %ix %i",sel_x,sel_y,sel_z)); bmp->Canvas->Brush->Style=bsSolid; } //--------------------------------------------------------------------------- void isometric::cell2scr(int &sx,int &sy,int cx,int cy,int cz) { #ifdef isometric_layout_1 sx=pan_x+((cxs*(cx-cy))/2); sy=pan_y+((cys*(cx+cy))/2)-(czs*cz); #endif #ifdef isometric_layout_2 sx=pan_x+(cxs*cx)+((cy&1)*cxs2); sy=pan_y+(cys*cy/2)-(czs*cz); #endif } //--------------------------------------------------------------------------- void isometric::scr2cell(int &cx,int &cy,int &cz,int sx,int sy) { int x0=-1,y0=-1,z0=-1,a,b,i,xx,yy; #ifdef isometric_layout_1 // rough cell ground estimation (no z value yet) // translate to (0,0,0) top left corner of the grid xx=sx-pan_x-cxs2; yy=sy-pan_y+cys2; // change aspect to square cells cxs x cxs yy=(yy*cxs)/cys; // use the dot product with axis vectors to compute grid cell coordinates cx=(+xx+yy)/cxs; cy=(-xx+yy)/cxs; cz=0; // scan closest neighbors #define _scann \ if ((cx>=0)&&(cx<gxs)) \ if ((cy>=0)&&(cy<gys)) \ { \ for (cz=0;(map[cz+1][cy][cx]!=_cell_type_empty)&&(cz<czs-1);cz++); \ cell2scr(xx,yy,cx,cy,cz); \ if (map[cz][cy][cx]==_cell_type_full) yy-=czs; \ xx=(sx-xx); yy=((sy-yy)*cxs)/cys; \ a=(xx+yy); b=(xx-yy); \ if ((a>=0)&&(a<=cxs)&&(b>=0)&&(b<=cxs)) \ if (cz>=z0) { x0=cx; y0=cy; z0=cz; } \ } _scann; // scan actual cell for (i=gzs*czs;i>=0;i-=cys) // scan as many lines bellow actual cell as needed { cy++; _scann; cx++; cy--; _scann; cy++; _scann; } cx=x0; cy=y0; cz=z0; // return remembered cell coordinate #undef _scann #endif #ifdef isometric_layout_2 // rough cell ground estimation (no z value yet) cy=(2*(sy-pan_y))/cys; cx= (sx-pan_x-((cy&1)*cxs2))/cxs; cz=0; // isometric tile shape crossing correction cell2scr(xx,yy,cx,cy,cz); xx=sx-xx; yy=sy-yy; if (xx<=cxs2) { if (yy> xx *cys/cxs) { cy++; if (int(cy&1)!=0) cx--; } } else { if (yy>(cxs-xx)*cys/cxs) { cy++; if (int(cy&1)==0) cx++; } } // scan closest neighbors #define _scann \ if ((cx>=0)&&(cx<gxs)) \ if ((cy>=0)&&(cy<gys)) \ { \ for (cz=0;(map[cz+1][cy][cx]!=_cell_type_empty)&&(cz<czs-1);cz++); \ cell2scr(xx,yy,cx,cy,cz); \ if (map[cz][cy][cx]==_cell_type_full) yy-=czs; \ xx=(sx-xx); yy=((sy-yy)*cxs)/cys; \ a=(xx+yy); b=(xx-yy); \ if ((a>=0)&&(a<=cxs)&&(b>=0)&&(b<=cxs)) \ if (cz>=z0) { x0=cx; y0=cy; z0=cz; } \ } _scann; // scan actual cell for (i=gzs*czs;i>=0;i-=cys) // scan as many lines bellow actual cell as needed { cy++; if (int(cy&1)!=0) cx--; _scann; cx++; _scann; cy++; if (int(cy&1)!=0) cx--; _scann; } cx=x0; cy=y0; cz=z0; // return remembered cell coordinate #undef _scann #endif } //--------------------------------------------------------------------------- void isometric::cell_draw(int x,int y,int tp,bool _sel) { TPoint pnt[5]; bmp->Canvas->Pen->Color=col_grid; if (tp==_cell_type_empty) { if (!_sel) return; bmp->Canvas->Pen->Color=col_sel; pnt[0].x=x; pnt[0].y=y ; pnt[1].x=x+cxs2; pnt[1].y=y+cys2; pnt[2].x=x+cxs; pnt[2].y=y ; pnt[3].x=x+cxs2; pnt[3].y=y-cys2; pnt[4].x=x; pnt[4].y=y ; bmp->Canvas->Polyline(pnt,4); } else if (tp==_cell_type_ground) { if (_sel) bmp->Canvas->Brush->Color=col_sel; else bmp->Canvas->Brush->Color=col_zside; pnt[0].x=x; pnt[0].y=y ; pnt[1].x=x+cxs2; pnt[1].y=y+cys2; pnt[2].x=x+cxs; pnt[2].y=y ; pnt[3].x=x+cxs2; pnt[3].y=y-cys2; bmp->Canvas->Polygon(pnt,3); } else if (tp==_cell_type_full) { if (_sel) bmp->Canvas->Brush->Color=col_sel; else bmp->Canvas->Brush->Color=col_xside; pnt[0].x=x+cxs2; pnt[0].y=y+cys2; pnt[1].x=x+cxs; pnt[1].y=y; pnt[2].x=x+cxs; pnt[2].y=y -czs; pnt[3].x=x+cxs2; pnt[3].y=y+cys2-czs; bmp->Canvas->Polygon(pnt,3); if (_sel) bmp->Canvas->Brush->Color=col_sel; else bmp->Canvas->Brush->Color=col_yside; pnt[0].x=x; pnt[0].y=y; pnt[1].x=x+cxs2; pnt[1].y=y+cys2; pnt[2].x=x+cxs2; pnt[2].y=y+cys2-czs; pnt[3].x=x; pnt[3].y=y -czs; bmp->Canvas->Polygon(pnt,3); if (_sel) bmp->Canvas->Brush->Color=col_sel; else bmp->Canvas->Brush->Color=col_zside; pnt[0].x=x; pnt[0].y=y -czs; pnt[1].x=x+cxs2; pnt[1].y=y+cys2-czs; pnt[2].x=x+cxs; pnt[2].y=y -czs; pnt[3].x=x+cxs2; pnt[3].y=y-cys2-czs; bmp->Canvas->Polygon(pnt,3); } } //--------------------------------------------------------------------------- void isometric::map_random() { int i,x,y,z,x0,y0,r,h; // clear for (z=0;z<gzs;z++) for (y=0;y<gys;y++) for (x=0;x<gxs;x++) map[z][y][x]=_cell_type_empty; // add pseudo-random bumps Randomize(); for (i=0;i<10;i++) { x0=Random(gxs); y0=Random(gys); r=Random((gxs+gys)>>3)+1; h=Random(gzs); for (z=0;(z<gzs)&&(r);z++,r--) for (y=y0-r;y<y0+r;y++) if ((y>=0)&&(y<gys)) for (x=x0-r;x<x0+r;x++) if ((x>=0)&&(x<gxs)) map[z][y][x]=_cell_type_full; } } //--------------------------------------------------------------------------- #endif //--------------------------------------------------------------------------- 

La mise en page définit uniquement les directions des axes du système de coordonnées (pour votre utilisation #define isometric_layout_2 ). Cela utilise Borlands VCL Graphics::TBitmap donc, si vous n'utilisez pas Borland, modifiez-le sur n'importe quel bitmap GDI ou écrasez la partie gfx dans votre API gfx (il est pertinent uniquement pour draw() et resize() ). Également, TShiftState fait partie de VCL. Il s'agit juste de l'état des boutons de la souris et des touches spéciales comme shift,alt,ctrl sorte que vous pouvez utiliser bool ou autrement (actuellement pas utilisé car je n'ai pas encore de fonctionnalités de clics).

Ici, mon code de fenêtre Borland (application de formulaire unique avec une minuterie sur elle) afin que vous voyiez comment utiliser ceci:

 //$$---- Form CPP ---- //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "win_main.h" #include "isometric.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TMain *Main; isometric iso; //--------------------------------------------------------------------------- void TMain::draw() { iso.draw(); Canvas->Draw(0,0,iso.bmp); } //--------------------------------------------------------------------------- __fastcall TMain::TMain(TComponent* Owner) : TForm(Owner) { Cursor=crNone; iso.map_random(); } //--------------------------------------------------------------------------- void __fastcall TMain::FormResize(TObject *Sender) { iso.resize(ClientWidth,ClientHeight); draw(); } //--------------------------------------------------------------------------- void __fastcall TMain::FormPaint(TObject *Sender) { draw(); } //--------------------------------------------------------------------------- void __fastcall TMain::tim_redrawTimer(TObject *Sender) { draw(); } //--------------------------------------------------------------------------- void __fastcall TMain::FormMouseMove(TObject *Sender, TShiftState Shift, int X,int Y) { iso.mouse(X,Y,Shift); draw(); } void __fastcall TMain::FormMouseDown(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { iso.mouse(X,Y,Shift); draw(); } void __fastcall TMain::FormMouseUp(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { iso.mouse(X,Y,Shift); draw(); } //--------------------------------------------------------------------------- void __fastcall TMain::FormDblClick(TObject *Sender) { iso.map_random(); } //--------------------------------------------------------------------------- 

Approche graphique [Edit1]

Jetez un oeil à Simple OpenGL GUI Framework User Interaction Advice? .

L'idée principale est de créer un tampon d'écran d'ombre où l'identité de la cellule rendue est stockée. Cela fournit une sélection parfaite de sprite / cellule pixel dans O(1) juste avec quelques lignes de code.

  1. Créer un tampon d'écran d'ombre idx[ys][xs]

    Il devrait avoir la même résolution que votre vue de carte et devrait être capable de stocker la valeur (x,y,z) de la cellule de rendu à l'intérieur d'un seul pixel (dans les unités de cellules de grille de carte). J'utilise un format de pixel de 32 bits, alors je choisis 12 bits pour x,y et 8 bits pour z

     DWORD color = (x) | (y<<12) | (z<<24) 
  2. Avant que le rendu de la carte ne soit clair ce tampon

    J'utilise 0xFFFFFFFF comme couleur vide de sorte qu'il ne heurte pas la cellule (0,0,0) .

  3. Sur le rendu de la cellule sprite

    Chaque fois que vous rendez le pixel au tampon d'écran pyx[y][x]=color vous pouvez également créer un pixel sur le tampon de l'écran d'ombre idx[y][x]=cc est la position de la cellule codée dans les unités de la grille de carte (voir n ° 1 ).

  4. Sur le clic de souris (ou quoi que ce soit)

    Vous avez la position de l'écran de la souris mx,my donc, si elle est en gamme, lisez simplement le tampon de l'ombre et obtenez la position de la cellule sélectionnée.

     c=idx[my][mx] if (c!=0xFFFFFFFF) { x= c &0x00000FFF; y=(c>>12)&0x00000FFF; z=(c>>24)&0x000000FF; } else { // here use the grid floor cell position formula from above approach if needed // or have empty cell rendered for z=0 with some special sprite to avoid this case. } 

    Avec l'encodage ci-dessus pour cette carte (écran):

    écran

    Est également rendu à l'écran d'ombre comme ceci:

    ombre

    La sélection est un pixel parfait, peu importe si vous cliquez sur le dessus, le côté …

    Les carreaux utilisés sont:

     Title: Isometric 64x64 Outside Tileset Author: Yar URL: http://opengameart.org/content/isometric-64x64-outside-tileset License(s): * CC-BY 3.0 http://creativecommons.org/licenses/by/3.0/legalcode 

    Et ici Win32 Demo:

    • Démo