Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/417.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 提高交错柱等轴测网格上点击检测的性能_Javascript_Algorithm_Click_Collision Detection_Isometric - Fatal编程技术网

Javascript 提高交错柱等轴测网格上点击检测的性能

Javascript 提高交错柱等轴测网格上点击检测的性能,javascript,algorithm,click,collision-detection,isometric,Javascript,Algorithm,Click,Collision Detection,Isometric,我正在开发一个等距游戏引擎,已经创建了一个像素完美点击检测算法。请访问并注意,单击检测可以检测单击了磁贴的哪个边。它还检查y索引以单击最前面的平铺 if(allContainsMouse && !topContainsMouse) 对我当前算法的解释: 等轴测栅格由100*65px的平铺图像组成。 TileW=100,TileL=50,tileH=15 地图由三维数组map[z][y][x]表示 瓷砖中心点(x,y)的计算如下: //x, y, z are the posit

我正在开发一个等距游戏引擎,已经创建了一个像素完美点击检测算法。请访问并注意,单击检测可以检测单击了磁贴的哪个边。它还检查y索引以单击最前面的平铺

if(allContainsMouse && !topContainsMouse)
对我当前算法的解释: 等轴测栅格由100*65px的平铺图像组成。
TileW=100,TileL=50,tileH=15

地图由三维数组
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);

用于确定鼠标是否在互动程序的给定区域内的原型函数:

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 = 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));
}
  • 在整个3d地图[z][y][x]阵列中循环
  • 如果
    allContainsMouse()
    返回true,则map[z][y][x]是鼠标所在的磁贴
  • 将此磁贴添加到Neathmouse下的阵列
    磁贴
    阵列
  • 在Neathmouse数组下循环浏览
    磁贴,然后选择具有最高
    y
    的磁贴。这是最前面的瓷砖

    if(allContainsMouse && !topContainsMouse)
    

(类似概念适用于权利)

最后,我的问题是: #1您将如何实现这一点,以使其更高效(而不是在所有瓷砖中循环)(接受pesudo代码)

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

我想到的是: 我最初试图通过不使用平铺中心点来解决这个问题,而是将鼠标(x,y)位置直接转换为平铺x,y。在我看来,这是最难编码的,也是最有效的解决方案。在正方形网格上,很容易将(x,y)位置转换为网格上的正方形。但是,在交错柱网轴线中,需要处理偏移。我尝试使用一个函数来计算偏移量,该函数取x或y值,并返回结果偏移量y或x。这张“之”字形曲线图解决了这个问题

检查鼠标是否在瓷砖内,使用这种方法很困难,我无法理解。我正在检查鼠标(x,y)是否位于一条
y=mx+b
线下,该线取决于tileX,tileY近似值(近似方形网格)


如果你到了这里,谢谢

这个答案基于:

下面是:

  • 网格和屏幕之间的转换

    正如我在评论中提到的,您应该创建在屏幕和单元格网格位置之间转换的函数。类似于(在C++中):

    在玩鼠标时,它总是选择顶部单元格(尽可能高),感觉正确(至少对我来说):

  • 这里是完整的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
        //---------------------------------------------------------------------------
    
    [Edit1]图形方法

    看一看

    其主要思想是创建阴影屏幕缓冲区,其中存储渲染单元的id。这在
    O(1)
    中提供了像素完美的精灵/单元选择,只需几行代码

  • 创建阴影屏幕缓冲区
    idx[ys][xs]

    它应该具有与地图视图相同的分辨率,并且应该能够在单个像素(以地图网格单元为单位)内存储渲染单元的
    (x,y,z)
    值。我使用32位像素格式,因此我为
    x,y
    选择
    12位,为
    z选择
    8位

     DWORD color = (x) | (y<<12) | (z<<24)
    
    使用上面的编码对该地图(屏幕)进行编码:

    也会渲染到阴影屏幕,如下所示:

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

    选择是完美的像素并不重要,如果你点击顶部,侧面

    使用的瓷砖有:

     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演示:


  • 请看,您可以直接从鼠标坐标计算地板网格坐标,然后只需在近邻的
    Y,Z
    轴上循环即可。只有瓷砖高度足以完全隐藏左上方/右上方/上方的瓷砖?@Davidisenstat No,这是一个游戏引擎,必须支持程序员希望拥有的每一块瓦片。@MaxMastalerz很有趣,所以我用我的等轴测引擎玩了一会儿,并成功地实现了我的方法。我已经编辑了我的答案并添加了扫描部分…其中很多可以用伪代码更清楚地解释,特别是相邻的扫描部分。@MaxMastalerz我认为没有必要,因为图像是自解释的,文本包含描述。你还声称你的扫描部分工作,但扫描所有单元格,因此你只需要从中选择要扫描的单元格。你的代码是否在鼠标位于瓷砖边缘时检测到鼠标?@不,只检测到顶部(因为这符合我的需要)如果您还需要边,那么使用您自己的检测。我看不到任何扩展此功能以进行全平铺扫描的简单方法。不管怎样,谢谢你的努力。
        //---------------------------------------------------------------------------
        //--- 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
        //---------------------------------------------------------------------------
    
    //$$---- 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();
        }
    //---------------------------------------------------------------------------
    
     DWORD color = (x) | (y<<12) | (z<<24)
    
     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.
      }
    
     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