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 lay
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:
-
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 ejex
necesita una corrección para las filas pares / impares (para eso es((cy&1)*cxs2)
)y
ejey
se desplaza por el ejez
(((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 decell2scr
, suponiendo quez=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. -
la cruz roja
representa las coordenadas devueltas por
-
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 capasz
(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.
-
crear
idx[ys][xs]
búfer de pantalla de sombraidx[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 elijo12
bits parax,y
y8
bits paraz
DWORD color = (x) | (y<<12) | (z<<24)
-
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)
. -
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 sombraidx[y][x]=c
dondec
está codificada en la posición de la celda en unidades de cuadrícula del mapa (ver # 1 ). -
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: