草庐IT

javascript - 提高交错列等距网格上点击检测的性能

coder 2024-05-16 原文

我正在研究等距游戏引擎,并且已经创建了一个用于像素完美点击检测的算法。访问 project并注意点击检测能够检测到瓷砖的哪个边缘被点击。它还检查 y-index 以单击最前面的图块。

我当前算法的解释:

等距网格由 100*65px 的平铺图像组成。TileW=100, TileL=50, tileH=15


map 由一个三维数组 map[z][y][x] 表示.

瓷砖中心点 (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);



确定鼠标是否在图块上的给定区域内的原型(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。通过检查 dx > 瓷砖宽度的一半来裁剪红色区域


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

(如果鼠标在中心点的左边)
Tile.prototype.rightContainsMouse = function() {
    var dx = mouse.mapX-this.centerX;
    if(dx>0) { return true; } else { return false; }
};

(如果鼠标在中心点的右边)

将所有方法结合在一起工作:
  • 循环遍历整个 3d map[z][y][x] 数组
  • 如果 allContainsMouse()返回 true,map[z][y][x] 是我们鼠标所在的图块。
  • 将此磁贴添加到数组 tilesUnderneathMouse大批。
  • 循环 tilesUnderneathMouse阵列,并选择最高y的瓦片.它是最前面的瓷砖。
    if(allContainsMouse && !topContainsMouse)
    


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



  • (类似的概念适用于权利)

    最后,我的问题:

    #1 你将如何实现这一点,使其更有效率(不循环遍历所有图块)(接受伪代码)

    #2 如果您无法回答#1,您有什么建议可以提高我的点击检测效率(块加载已经考虑过)

    我想到的:

    我最初试图通过不使用瓷砖中心点来解决这个问题,而是将鼠标(x,y)位置直接转换为瓷砖 x,y。在我看来,这是最难编码但最有效的解决方案。在正方形网格上,很容易将 (x,y) 位置转换为网格上的正方形。但是,在交错的列网格中,您需要处理偏移量。我尝试使用 a 函数计算偏移量,该函数采用 x 或 y 值,并返回结果偏移量 y 或 x。 arccos(cosx)的之字形图解决了那个。

    检查鼠标是否在磁贴内,使用这种方法很困难,我无法弄清楚。我正在检查鼠标(x,y)是否在 y=mx+b 下方依赖于 tileX、tileY 近似值的线(近似方形网格)。

    如果你到了这里,谢谢!

    最佳答案

    这个答案基于:

  • 2D grid image values to 2D array

  • 所以这里是这样的:
  • 网格与屏幕之间的转换
    正如我在评论中提到的,您应该制作在屏幕和单元格网格位置之间转换的函数。类似(在 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++; } }
         }
     //---------------------------------------------------------------------------
    
    我使用了您的布局(我花了一段时间将我的布局转换为它,希望我不会在某处犯一些愚蠢的错误):

  • 红十字表示 cell2scr(x,y,0,0,0) 返回的坐标
  • 绿十字代表鼠标坐标
  • 水色亮点 表示返回的单元格位置

  • 请注意,如果您使用的是整数算术,则需要记住,如果除以/乘以一半大小可能会失去精度。使用全尺寸并将结果除以 2对于这种情况(过去花很多时间弄清楚那个)。cell2scr很简单。屏幕位置是平移偏移 + 单元格位置乘以其大小(步长)。 x轴需要对偶数/奇数行进行更正(这就是 ((cy&1)*cxs2) 的用途)和 y轴由 z 移动轴 ( ((cy&1)*cxs2) )。我的屏有点(0,0)在左上 Angular ,+x轴指向右侧和 +y是向下。scr2cellcell2scr 的方程代数求解屏幕位置完成同时假设 z=0所以只选择网格地。最重要的是,如果鼠标位置在找到的单元格区域之外,则添加偶数/奇数校正。
  • 扫描邻居scr2cell(x,y,z,mouse_x,mouse_y)只返回鼠标在地面上的单元格。因此,如果您想添加当前的选择功能,您需要扫描该位置的顶部单元格和几个相邻的单元格,然后选择距离最小的单元格。
    无需扫描整个网格/ map ,只需返回位置周围的几个单元格。这应该会大大加快速度。
    我这样做:

    行数取决于单元格 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
         }
    
    这总是选择顶部单元格(所有可能的最高单元格)在使用鼠标时感觉正确(至少对我而言):


  • 这里完整 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 VCL Graphics::TBitmap所以如果你不使用 Borland 将其更改为任何 GDI 位图或将 gfx 部分覆盖到您的 gfx API (它仅与 draw()resize() 相关)。还有 TShiftState 的一部分VCL 它只是鼠标按钮和特殊键(如 shift、alt、ctrl)的状态,因此您可以使用 bool或其他任何东西(目前未使用,因为我还没有任何点击功能)。
    这是我的 Borland 窗口代码(带有一个计时器的单一表单应用程序),因此您可以了解如何使用它:
    //$$---- 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? .
    主要思想是创建存储渲染单元格 ID 的阴影屏幕缓冲区。这在 O(1) 中提供了像素完美的 Sprite /单元格选择只需几行代码。
  • 创建阴影屏幕缓冲区 idx[ys][xs]
    它应该与您的 map View 具有相同的分辨率并且应该能够存储 (x,y,z)单个像素内渲染单元的值(以 map 网格单元为单位)。我使用 32 位像素格式,所以我选择 12 x,y 的位和 8 z 的位
     DWORD color = (x) | (y<<12) | (z<<24)
    
  • 在渲染 map 之前清除此缓冲区
    我用 0xFFFFFFFF为空颜色,因此不会与单元格冲突 (0,0,0) .
  • 在 map 单元格 Sprite 渲染上
    每当您将像素渲染到屏幕缓冲区时 pyx[y][x]=color您还将像素渲染到阴影屏幕缓冲区 idx[y][x]=c哪里c是以 map 网格单元编码的单元格位置(参见 #1 )。
  • 单击鼠标(或其他)
    你得到了鼠标的屏幕位置 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.
      }
    
    通过以上编码此 map (屏幕):

    也像这样渲染到阴影屏幕:

    选择是像素完美无所谓,如果你点击顶部,侧面...
    使用的瓷砖是:
     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

  • 关于javascript - 提高交错列等距网格上点击检测的性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35885029/

    有关javascript - 提高交错列等距网格上点击检测的性能的更多相关文章

    1. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

      我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

    2. 程序员如何提高代码能力? - 2

      前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

    3. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

      我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

    4. Ruby 的数字方法性能 - 2

      我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0

    5. ruby - Ruby 性能中的计时器 - 2

      我正在寻找一个用ruby​​演示计时器的在线示例,并发现了下面的代码。它按预期工作,但这个简单的程序使用30Mo内存(如Windows任务管理器中所示)和太多CPU有意义吗?非常感谢deftime_blockstart_time=Time.nowThread.new{yield}Time.now-start_timeenddefrepeat_every(seconds)whiletruedotime_spent=time_block{yield}#Tohandle-vesleepinteravalsleep(seconds-time_spent)iftime_spent

    6. ruby-on-rails - 如果条件与 &&,是否有任何性能提升 - 2

      如果用户是所有者,我有一个条件来检查说删除和文章。delete_articleifuser.owner?另一种方式是user.owner?&&delete_article选择它有什么好处还是它只是一种写作风格 最佳答案 性能不太可能成为该声明的问题。第一个要好得多-它更容易阅读。您future的自己和其他将开始编写代码的人会为此感谢您。 关于ruby-on-rails-如果条件与&&,是否有任何性能提升,我们在StackOverflow上找到一个类似的问题:

    7. ruby - 检测由 RSpec、Ruby 运行的代码 - 2

      我想知道我的代码是否在rspec下运行。这可能吗?原因是我正在加载一些错误记录器,这些记录器在测试期间会被故意错误(expect{x}.toraise_error)弄得乱七八糟。我查看了我的ENV变量,没有(明显的)测试环境变量的迹象。 最佳答案 在spec_helper.rb的开头添加:ENV['RACK_ENV']='test'现在您可以在代码中检查RACK_ENV是否经过测试。 关于ruby-检测由RSpec、Ruby运行的代码,我们在StackOverflow上找到一个类似的问题

    8. ruby - 使用 Ruby Daemons gem 检测停止 - 2

      我正在使用rubydaemongem。想知道如何向停止操作添加一些额外的步骤?希望我能检测到停止被调用,并向其添加一些额外的代码。任何人都知道我如何才能做到这一点? 最佳答案 查看守护程序gem代码,它似乎没有用于此目的的明显扩展点。但是,我想知道(在守护进程中)您是否可以捕获守护进程在发生“停止”时发送的KILL/TERM信号...?trap("TERM")do#executeyourextracodehereend或者你可以安装一个at_exit钩子(Hook):-at_exitdo#executeyourextracodehe

    9. ruby - 在 Mechanize 中使用 JavaScript 单击链接 - 2

      我有这个:AccountSummary我想单击该链接,但在使用link_to时出现错误。我试过:bot.click(page.link_with(:href=>/menu_home/))bot.click(page.link_with(:class=>'top_level_active'))bot.click(page.link_with(:href=>/AccountSummary/))我得到的错误是:NoMethodError:nil:NilClass的未定义方法“[]” 最佳答案 那是一个javascript链接。Mechan

    10. ruby - 如何找到我的 Ruby 应用程序中的性能瓶颈? - 2

      我编写了一个Ruby应用程序,它可以解析来自不同格式html、xml和csv文件的源中的大量数据。我如何找出代码的哪些区域花费的时间最长?有没有关于如何提高Ruby应用程序性能的好资源?或者您是否有任何始终遵循的性能编码标准?例如,你总是用加入你的字符串吗?output=String.newoutput或者你会使用output="#{part_one}#{part_two}\n" 最佳答案 好吧,有一些众所周知的做法,例如字符串连接比“#{value}”慢得多,但是为了找出您的脚本在哪里消耗了大部分时间或比所需时间更多,您需要进行分

    随机推荐