2016-03-09 2 views
9

Я работаю над изометрическим движком игры и уже создал алгоритм обнаружения идеального щелчка пикселя. Посетите project и обратите внимание, что обнаружение щелчка может обнаружить, какой край плитки был нажат. Он также проверяет y-index, чтобы щелкнуть самую верхнюю часть.Улучшение производительности обнаружения щелчка на изометрической сетке с шахматным столбцом

Объяснение моего текущего алгоритма:

Изометрическая сетка выполнена из мозаичных изображений, которые 100 * 65px. TileW=100, TileL=50, tileH=15

Sizing of tile

Карта представлена ​​трехмерного массива map[z][y][x].

точки Tile центр (x,y) рассчитываются следующим образом:

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

Isometric grid

Prototype функции, которые определяют, если мышь находится в пределах заданной области на плитке:

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() возвращает true, если мышь находится в пределах зеленого. Красная площадь вырежется, проверяя, если де> полуширины плитки в

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

Returns true if mouse is on top face


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

(Если мышь находится слева от центральной точки)


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

(Если мышь находится справа от центральной точки)

Приведения всех метод вместе, чтобы работать как один:

  • петля через весь 3D карты [г] [г ] [x] array
  • если allContainsMouse() возвращает true, map [z] [y] [x] - это наша мышь.
  • Добавить эту плитки в массив tilesUnderneathMouse массив.
  • Петля через tilesUnderneathMouse массив, и выберите плитки с самым высоким y. Это самый передний плит.

    if(allContainsMouse && !topContainsMouse) 
    

Bottom match

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

left match

(Аналогичная концепция применяется для правостороннего)

Наконец, мои вопросы:

# 1 Как бы вы сделать это таким образом, что это более эффективно (не пробегаем по всем плитки) (pesudo код принят)

# 2 Если вы не можете ответить # 1, какие предложений вы должны повысить эффективность моего обнаружения клик (фрагмент загрузка уже рассмотрена)

Что я подумал:

первоначально я пытался решить эту проблему, не используя плитки центра точнее, вместо того, чтобы преобразовать позицию мыши (x, y) непосредственно в плитку x, y. На мой взгляд, это сложнее всего кодировать, но наиболее эффективное решение. На квадратной сетке очень легко преобразовать позицию (x, y) в квадрат на сетке. Однако в сетке с разнесенной колонкой вы имеете дело с смещениями. Я попытался вычислить смещения, используя функцию, которая принимает значение x или y, и возвращает результирующее смещение y или x. График Zig-zag arccos(cosx) решил это.

Проверка, была ли мышка внутри плитки, использование этого метода было затруднительным, и я не мог понять это. Я проверял, была ли мышь (x, y) ниже линии y=mx+b, которая зависела от приближения tileX, tileY (квадратная сетка приблизительно).

Если вы добрались сюда, спасибо!

+0

см [2D значения сетки изображения в 2D массива] (Http: // StackOverflow .com/a/30024958/2521214) вы можете вычислять координаты сетки пола непосредственно из координат мыши, а затем просто прокручивать ось «Y, Z» только близких соседей – Spektre

+0

Может ли плитка быть та достаточно ли полностью скрыть плитки выше/выше слева/выше справа? –

+0

@DavidEisenstat Нет, это игровой движок и должен поддерживать каждую плиту, которую хочет пожелать программист. –

ответ

4

Этот ответ основан на:

Так вот он идет:

  1. преобразование между сеткой и экраном

    Как я уже говорил в Comme nt вы должны выполнять функции, которые преобразуют между ячейками сетки и ячейки.что-то вроде (в 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++; } } 
        } 
    //--------------------------------------------------------------------------- 
    

    Я использовал макет (взял миль в то время как преобразовать мое к нему, надеюсь, я не сделал какой-то глупую ошибку где-то):

    layout

    • красный крест представляет координаты, возвращенные cell2scr(x,y,0,0,0)
    • green крест представляет координаты мыши
    • аква изюминкой представляет вернулся Поместите ячеечная

    Остерегайтесь, если вы используете целые арифметику, нужно принимать во внимание, если вы разделив/умножения на половину размера вы можете потерять точность. Используйте полный размер и разделите результат на 2 для таких случаев (потратьте много времени на то, чтобы понять, что это было в прошлом).

    cell2scr довольно прост. Положение экрана - смещение панорамирования + позиция ячейки, умноженная на его размер (шаг). Ось x нуждается в коррекции для четных/нечетных строк (то есть для ((cy&1)*cxs2)), а ось y сдвинута на ось z (((cy&1)*cxs2)). Экран шахты имеет точку (0,0) в верхнем левом углу, +x ось указывает направо и +y указывает вниз.

    scr2cell выполнен по алгебраически разрешенному положению экрана из уравнений cell2scr, принимая при этом z=0, поэтому выбирает только землю сетки. Кроме того, добавляется четная/нечетная коррекция, если позиция мыши находится вне найденной области ячеек.

  2. соседей сканирования

    в scr2cell(x,y,z,mouse_x,mouse_y) возвращается только клетки, где мышь находится на земле. поэтому, если вы хотите добавить свою текущую функциональность выбора, вам нужно отсканировать верхнюю ячейку в этой позиции и несколько соседних ячеек и выбрать ту, которая находится на минимальном расстоянии.

    Не нужно сканировать всю сетку/карту всего лишь несколько клеток по возвращенной позиции. Это должно значительно ускорить дело.

    я сделать это следующим образом:

    scan pattern

    Число линий зависит от размера ячейки z оси (czs), максимальное число z слоев (gzs) и размера ячейки (cys).C++ код шахты с разверткой выглядит следующим образом:

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

    Это выбирает всегда верхнюю ячейку (самый высокий из всех возможных) при игре с помощью мыши он чувствует себя правильно (по крайней мере, для меня):

    scan result

Здесь полная VCL/C++ источника для шахтного изометрического двигателя я лопнул для этого сегодня:

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

Макет определяет только направления координат системы координат (для вашего использования #define isometric_layout_2). При этом используется Borlands VCLGraphics::TBitmap так что если вы не используете Борланд изменить его в любой GDI растрового изображения или перезаписать GFX часть на ваше: GFX API (это актуально только для draw() и resize()). Также TShiftState является частью VCL Это просто состояние кнопок мыши и специальных клавиш, таких как shift,alt,ctrl, поэтому вы можете использовать bool или что-то еще вместо этого (в настоящее время не используется, поскольку у меня еще нет функции щелчка).

Вот мой Борланд окно кода (единая форма приложения с одного таймера на нем), так что вы видите, как использовать это:

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

[Edit1] графики приближаются

Посмотрите на Simple OpenGL GUI Framework User Interaction Advice?.

Основная идея заключается в создании буфера теневого экрана, в котором хранится идентификатор отображаемой ячейки. Это обеспечивает идеальный выбор спрайта/ячейки пикселя в O(1) только с несколькими строками кода.

  1. создать буфер экрана тени idx[ys][xs]

    Он должен иметь такое же разрешение, как ваш вид карты и должен быть способен хранить значение визуализации ячейки внутри одного пикселя (в единицах карты ячейки сетки) (x,y,z) , Я использую 32-битный формат пиксель, так я выбираю 12 бит для x,y и 8 бит для z

    DWORD color = (x) | (y<<12) | (z<<24) 
    
  2. перед визуализацией карты ясном этого буфера

    Я использую 0xFFFFFFFF, как пустой цвета поэтому не сталкиваясь с ячейкой (0,0,0).

  3. на карту клеток спрайта оказывающего

    всякий раз, когда вы предоставляете пиксель в буфер экрана pyx[y][x]=color вы также делаете пиксель тень буфера экрана idx[y][x]=c где c закодирован положение ячейки в сетке карты единиц (см # 1) ,

  4. На мыши (или любой другой)

    Вы получили положение экрана мыши mx,my так, если он находится в диапазоне только читать буфер теней и получить выбранное положение ячейки.

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

    С выше кодирования этой карты (экран):

    screen

    оказывает также тень экрана, как это:

    shadow

    Выбор пиксель идеально подходит не имеет значения если вы нажмете сверху, сбоку ...

    Плитки используются:

    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 
    

    И здесь Win32 Demo:

+0

Многое из этого можно было бы более подробно объяснить с помощью псевдокода, особенно в области сканирования соседей. –

+0

@MaxMastalerz Я не думаю, что это необходимо, так как изображение самоочевидно, а текст содержит описание. Также вы утверждали, что у вас есть часть сканирования, но сканирование всех ячеек, поэтому вам нужно просто выбрать ячейки для сканирования. – Spektre

+0

Определяет ли ваш код мышь, когда она находится на краю плитки? –