javascript algorithm click collision-detection isometric

javascript - Mejora del rendimiento de la detección de clics en una cuadrícula isométrica de columna escalonada



algorithm click (1)

Estoy trabajando en un motor de juego isométrico y ya he creado un algoritmo para la detección de clics perfectos en píxeles. Visite el proyecto y observe que la detección de clics puede detectar en qué borde del mosaico se hizo clic. También se comprueba el índice y para hacer clic en el mosaico más adelantado.

Una explicación de mi algoritmo actual:

La cuadrícula isométrica está hecha de imágenes de mosaico que son 100 * 65px. TileW=100, TileL=50, tileH=15

El mapa está representado por un map[z][y][x] matriz tridimensional map[z][y][x] .

Los puntos centrales del mosaico (x,y) se calculan así:

//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);

Funciones de prototipo que determinan si el mouse está dentro de un área determinada en el mosaico:

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() devuelve verdadero si el mouse está dentro de verde. El área roja se recorta comprobando si dx> la mitad del ancho del mosaico

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)); };

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

(Si el mouse queda a la izquierda del punto central)

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

(Si el mouse está a la derecha del punto central)

Reuniendo todos los métodos para trabajar como uno solo:

  • Recorrer todo el mapa 3d [z] [y] [x] matriz
  • si allContainsMouse() devuelve verdadero, el mapa [z] [y] [x] es el mosaico en el que está nuestro mouse.
  • Agregue este mosaico a la matriz tilesUnderneathMouse array.
  • tilesUnderneathMouse los azulejosUnderneathMouse, y elija el mosaico con la y más alta. Es el mosaico más adelantado.

    if(allContainsMouse && !topContainsMouse)

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

(Se aplica un concepto similar para el derecho)

Finalmente, mis preguntas:

# 1 ¿Cómo lograrías esto, de modo que sea más eficiente (no recorrer todos los mosaicos) (se acepta el código de pesudo)

# 2 Si no puede responder # 1, ¿qué sugerencias tiene para mejorar la eficiencia de mi detección de clics (ya se ha considerado la carga de fragmentos)

Lo que he pensado:

Originalmente traté de resolver este problema al no usar puntos centrales de mosaico, en lugar de convertir la posición del mouse (x, y) directamente en el mosaico x, y. En mi opinión, esta es la solución más difícil de codificar, pero más eficiente. En una cuadrícula cuadrada es muy fácil convertir una posición (x, y) a un cuadrado en la cuadrícula. Sin embargo, en una cuadrícula de columnas escalonadas, se trata de compensaciones. Traté de calcular los desplazamientos utilizando la función a que toma un valor x o y, y devuelve el desplazamiento resultante y, o x. El gráfico en zig-zag de arccos(cosx) resolvió eso.

Verificando si el mouse estaba dentro del mosaico, usar este método fue difícil y no pude resolverlo. Estaba comprobando si el mouse (x, y) estaba debajo de una línea y=mx+b que dependía de la aproximación tileX, tileY (una cuadrícula cuadrada aproximadamente).

Si llegaste hasta aquí, ¡Gracias!


Esta respuesta se basa en:

  • Valores de imagen de cuadrícula 2D a matriz 2D

Así que aquí va:

  1. conversión entre cuadrícula y pantalla

    Como mencioné en el comentario, debe realizar funciones que conviertan entre la pantalla y las posiciones de la cuadrícula de la celda. algo como (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++; } } } //---------------------------------------------------------------------------

    Usé su diseño (tardé mi tiempo en convertir el mío en él, espero no haber cometido ningún error tonto en alguna parte):

    • la cruz roja representa las coordenadas devueltas por cell2scr(x,y,0,0,0)
    • la cruz verde representa las coordenadas del mouse
    • el punto culminante aqua representa la posición de la celda devuelta

    Tenga en cuenta que si está usando aritmética de enteros, debe tener en cuenta que si divide / multiplica por la mitad, puede perder precisión. Use tamaño completo y divida el resultado entre 2 para tales casos (pase mucho tiempo resolviendo eso en el pasado).

    El cell2scr es bastante sencillo. La posición de la pantalla es desplazamiento de desplazamiento + posición de celda multiplicada por su tamaño (paso). El eje x necesita una corrección para las filas pares / impares (para eso es ((cy&1)*cxs2) ) y eje y se desplaza por el eje z ( ((cy&1)*cxs2) ). La pantalla de la mina tiene un punto (0,0) en la esquina superior izquierda, el eje +x apunta hacia la derecha y +y apunta hacia abajo.

    El scr2cell se realiza mediante la posición de pantalla resuelta algebraicamente a partir de las ecuaciones de cell2scr , suponiendo que z=0 por lo tanto, selecciona solo el terreno de la cuadrícula. Además de eso, solo se agrega la corrección par / impar si la posición del mouse está fuera del área de celda encontrada.

  2. escanear vecinos

    scr2cell(x,y,z,mouse_x,mouse_y) devuelve solo la celda donde está el mouse en el suelo. por lo tanto, si desea agregar su funcionalidad de selección actual, debe escanear la celda superior en esa posición y algunas celdas vecinas y seleccionar la que tenga la menor distancia.

    No es necesario escanear toda la cuadrícula / mapa, solo algunas celdas alrededor de la posición devuelta. Eso debería acelerar la cosa considerablemente.

    Lo hago así:

    El número de líneas depende del tamaño del eje z la celda ( czs ), el número máximo de capas z ( gzs ) y el tamaño de la celda ( cys ). El código mío de C ++ con escaneo se ve así:

    // 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 }

    Esto selecciona siempre la celda superior (la más alta de todas las posibles) cuando se juega con el mouse se siente correctamente (al menos para mí):

Aquí completa la fuente de VCL / C ++ para el motor isométrico de minas que descubrí hoy:

//--------------------------------------------------------------------------- //--- 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: %i x %i",mx,my)); bmp->Canvas->TextOutA(5,20,AnsiString().sprintf("Select: %i x %i x %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 //---------------------------------------------------------------------------

El diseño define solo las direcciones de los ejes del sistema de coordenadas (para el suyo, use #define isometric_layout_2 ). Esto usa Borlands VCL Graphics::TBitmap por lo que si no usa Borland cámbielo a cualquier mapa de bits GDI o sobrescriba la parte gfx en la API gfx de su (es relevante solo para draw() y resize() ). También TShiftState es parte de VCL , es solo el estado de los botones del mouse y las teclas especiales como shift,alt,ctrl para que pueda usar bool o cualquier otra cosa (actualmente no se usa, ya que todavía no tengo ninguna funcionalidad de clic).

Aquí mi código de ventana Borland (aplicación de formulario único con un temporizador) para que vea cómo usar esto:

//$$---- 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(); } //---------------------------------------------------------------------------

[Editar1] enfoque gráfico

Eche un vistazo a Simple OpenGL GUI Framework User Interaction Advice? .

La idea principal es crear un búfer de pantalla de sombra donde se almacena la identificación de la celda renderizada. Esto proporciona una selección de sprite / celda perfecta de píxeles en O(1) solo con pocas líneas de código.

  1. crear idx[ys][xs] búfer de pantalla de sombra idx[ys][xs]

    Debe tener la misma resolución que su vista de mapa y debe ser capaz de almacenar el valor (x,y,z) de la celda de renderizado dentro de un solo píxel (en unidades de celdas de la cuadrícula del mapa). Uso el formato de píxeles de 32 bits, así que elijo 12 bits para x,y y 8 bits para z

    DWORD color = (x) | (y<<12) | (z<<24)

  2. antes de renderizar el mapa, borre este búfer

    Yo uso 0xFFFFFFFF como color vacío para que no choque con la celda (0,0,0) .

  3. en la representación de sprites de celda de mapa

    cada vez que renderiza píxel a búfer de pantalla pyx[y][x]=color , también renderiza píxel a búfer de pantalla de sombra idx[y][x]=c donde c está codificada en la posición de la celda en unidades de cuadrícula del mapa (ver # 1 ).

  4. Al hacer clic con el mouse (o lo que sea)

    Obtuviste la posición de la pantalla del mouse mx,my por lo tanto, si está dentro del rango, simplemente lee el búfer de sombra y obtén la posición de celda seleccionada.

    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. }

    Con la codificación anterior de este mapa (pantalla):

    se representa también en la pantalla de sombra de esta manera:

    La selección es píxel perfecto, no importa si hace clic en la parte superior, lateral ...

    Los azulejos utilizados son:

    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

    Y aquí Win32 Demo: