c++ - 2D 等距 - SFML - 正确的公式,错误的坐标范围

标签 c++ sfml isometric linear-equation

我不使用瓷砖,而是使用 sf::Vertex 绘制的立方体。每个立方体有 6 个面,每个面有 4 个点。 enter image description here

所以我只需要 cubes[numCube].sides()[numSide].... 来选择一个边。

我创建立方体 layer.cpp :

for(int J = 0; J < mapSize; J++)
    {
        for(int I = 0; I < mapSize; I++)
        {
            x = (J - I) * (cubeSize/2);
            y = (J + I) * (cubeSize/4);

            c = new cube(cubeSize, x, y, z, I, J);
            cs.push_back(*c);
        }
    }

在 cube.cpp 中我创建边,然后在边s.cpp 中,我像这样计算每个点的坐标:

switch(typeSide)
{
    case 0://DOWN_SIDE
        light = 1;

        tmp_x = x + (size/2);
        tmp_y = y + (size/2);
        p0 = new point(tmp_x, tmp_y, tmp_z);

        tmp_x = x + size;
        tmp_y = y + (3 * (size/4));
        p1 = new point(tmp_x, tmp_y, tmp_z);

        tmp_x = x + (size/2);
        tmp_y = y + size;
        p2 = new point(tmp_x, tmp_y, tmp_z);

        tmp_x = x;
        tmp_y = y + (3 * (size/4));
        p3 = new point(tmp_x, tmp_y, tmp_z);
        break;

    case 1://BACK_LEFT_SIDE

//ETC. ....

Point.cpp:

/*
 * point.cpp
 *
 *  Created on: 21 nov. 2015
 *      Author: user
 */

#include "point.h"

point::point(float tx, float ty, float tz)
{
    coords* dummyVar = new coords(tx, ty, tz);
    coordinates = dummyVar;
}

std::vector<float> point::position()//Use : myPoint.getPosition[0] //get the x
{
    std::vector<float> dummyVar;

    dummyVar.push_back(coordinates->getX());
    dummyVar.push_back(coordinates->getY() - coordinates->getZ());

    return dummyVar;
}

void point::move(float tx, float ty, float tz)
{
    coordinates->setX(tx);
    coordinates->setY(ty);
    coordinates->setZ(tz);
}

我的问题来 self 用来检测点击的功能:

if (event.type == sf::Event::MouseMoved)
{
            currentSelectedCube = maps[currentMapID].getCubeIDAt(event.mouseMove.x, event.mouseMove.y, offsetLeft, offsetTop, enableOffset);
}

功能(不要理会评论):

我尝试在没有“for 循环”的情况下在我的多维数据集 vector 中获取多维数据集的条目。 为什么 ?点击时使用更少的 CPU。

int map::getCubeIDAt(float x, float y, int offsetLeft, int offsetTop, bool enableOffset)//WIP ! //USED FOR CLICK DETECTION ON CUBES
    {
    //----------------------------------------------------------------//
        int unsigned entry = -1;

        int I = 0;
        int J = 0;
    //----------------------------------------------------------------//

        if(currentLayerId() > -1)//If there is any layers
        {
            //IF CHECK IN MAP BOUDING BOX + ROTATION TO GOT DIAMOND SHAPE AREA(LAYER + OFFSETS)----------------------------------
            //{

                if(!enableOffset)//With offsets disabled
                {
                    I = (y * 2 - x) / cubeSize;
                    J = (y * 2 + x) / cubeSize;
                }
                else //With offsets enabled
                {
                    I = (((y-offsetTop)+(currentLayerId()*(cubeSize/2))) * 2 - (x-offsetLeft)) / cubeSize;
                    J = (((y-offsetTop)+(currentLayerId()*(cubeSize/2)))  * 2 + (x-offsetLeft)) / cubeSize;
                }

                entry = I + J * size;

                if (entry < 0 || entry >= layers()[currentLayerId()].cubes().size())
                {
                    entry = -1;
                }
                else//DEBUG - DISPLAYING VALUES FOR TEST
                {
                    std::cout << "Entry n°" << entry << " - ";
                    std::cout << "[" << I << "; " << J << "]" << std::endl;
                }
            //}
            //END IF CHECK IN MAP BOUDING BOX + ROTATION TO GOT DIAMOND SHAPE AREA(LAYER + OFFSETS)----------------------------------
        }

        return entry;
    }

I-J 和 entryNumber 都可以。我的意思是,例如,对于立方体 0,我有 I = 0; J = 0;等等……这行得通。

我不明白为什么坐标范围像这张图片中的红色部分(不是100%准确,我不是绘画天才哈哈):

enter image description here

但我应该明白(第二张图片 - 红色部分是我点击的地方):

但经过几次检查,我得到的 I-J 和条目是对应的。这太奇怪了。

enter image description here

EDIT2: 已实现偏移和层数。 问题左:坐标范围错误。

以防万一,这是处理事件的“函数”:

void GRAPHICS_HANDLER::listenEvents()
{
    while (window->pollEvent(event))
    {
        if (event.type == sf::Event::Closed)
        {
            window->close();
        }

        if(event.type == sf::Event::KeyPressed)
        {
            //DISPLAY/UNDISPLAY GRID -- DEBUG FUNCTION
            if(event.key.code == sf::Keyboard::Escape)
            {
                if(grid)
                    grid = false;
                else
                    grid = true;
            }

//-----------------------------------------------------------------------------------DEBUG---------------------------------------------------------------//
            if(event.key.code == sf::Keyboard::B)//ACTIVE BRUSHMODE -- NEED TO BLOCK IT WHEN ACCESS VIOLATION OF CUBES ARRAY(CRASH)
            {
                if(!brushMode)
                {
                    brushMode = true;
                    std::cout << "Brush mode enabled" << std::endl;
                }
                else
                {
                    brushMode = false;
                    std::cout << "Brush mode disabled" << std::endl;
                }
            }

            if(event.key.code == sf::Keyboard::L)//ADD_LAYER
            {
                addLayer(getCurrentMapID());
            }

            if(event.key.code == sf::Keyboard::M)//DELETE_LAYER
            {
                deleteLayer(currentMapID, maps[currentMapID].currentLayerId());
            }

            if(event.key.code == sf::Keyboard::S)//ADD_LAYER
            {
                std::cout << "Select a texture: ";
                std::cin >> currentSelectedTexture; std::cout << std::endl;
            }

            if(event.key.code == sf::Keyboard::Left)//Move in Layer
            {
                if(maps[currentMapID].currentLayerId() > 0)
                {
                    maps[currentMapID].setCurrentLayerID(maps[currentMapID].currentLayerId()-1);
                }
            }

            if(event.key.code == sf::Keyboard::Right)//Move in Layer
            {
                if(maps[currentMapID].currentLayerId() < maps[currentMapID].layers().size()-1)
                {
                    maps[currentMapID].setCurrentLayerID(maps[currentMapID].currentLayerId()+1);
                }
            }
//-----------------------------------------------------------------------------------DEBUG---------------------------------------------------------------//
        }

        if (event.type == sf::Event::MouseMoved)
        {
//--------------------------------------------------------------------------CURSOR-----------------------------------------------------------------------//
            currentSelectedCube = maps[currentMapID].getCubeIDAt(event.mouseMove.x, event.mouseMove.y, offsetLeft, offsetTop, enableOffset);
//--------------------------------------------------------------------------CURSOR-----------------------------------------------------------------------//
        }

        if (event.type == sf::Event::MouseButtonPressed)
        {
//--------------------------------------------------------------------------CURSOR-----------------------------------------------------------------------//
            currentSelectedCube = maps[currentMapID].getCubeIDAt(event.mouseButton.x, event.mouseButton.y, offsetLeft, offsetTop, enableOffset);
//--------------------------------------------------------------------------CURSOR-----------------------------------------------------------------------//
            if (event.mouseButton.button == sf::Mouse::Left)
            {
//--------------------------------------------------------------------------CUBE CLICK DETECTION--------------------------------------------------//
                if(maps.size() > 0 && maps[currentMapID].layers().size() > 0 && currentSelectedCube > -1)
                {
                    cubeClicked = true;
                }
            }

            if (event.mouseButton.button == sf::Mouse::Right)
            {
                if(maps.size() > 0 && maps[currentMapID].layers().size() > 0 && currentSelectedCube > -1)
                {
                    maps[currentMapID].layers()[maps[currentMapID].currentLayerId()].cubes()[currentSelectedCube].setTexture(1);
                }
            }
//--------------------------------------------------------------------------CUBE CLICK DETECTION--------------------------------------------------//
        }
    }
}

EDIT3:我更新了我的代码以允许我只绘制立方体的下侧,所以我可以这样做(草): enter image description here

当我放置平面正方形(绿色)时,坐标范围(屏幕截图中显示的红色等距正方形)略有变化。 我不知道为什么,我更喜欢精确一点,以防万一。

最佳答案

您需要从瓦片平面存储每个元素的“高度”,以便区分您实际选择的是哪个立方体(离观察者越近):

enter image description here

相同的屏幕坐标,但不同的图 block 。

我不清楚你是如何为你的世界建模的,所以我会给你一个部分算法来检查哪个立方体的哪个面是单击的。请根据您的实际代码和您编写的类对其进行调整以使其正常工作。

// I'll let you to add the offsets for the screen coordinates
I = (y * 2 - x) / cubeSize;
J = (y * 2 + x) / cubeSize;
// find out if it is a left or right triangle
if ( x < (J - I) * (cubeSize/2) ) {
    // left triangle
    for ( k = max_n_layer; k > -1; --k ) {
        // you create the cubes nesting the I loop in the J loop, so to get the index of a cube,
        // assuming that you have created all the cubes (even the invisible ones, like it seems from your code)
        index = (J+1+k)*mapsize + I+1+k;

        // I don't really get how you define the existence or not of a face, but I guess something like this:
        if ( index < map.layer[k].cubes.size() 
            &&  map.layer[k].cubes[index].sides[top_side] != 0 ) { 
        // the face selected is the top side of cube[index] of layer k
            // you have to return index and k to select the right face, or simply a pointer to that face
            // if this makes any sense with how you have designed your model
            return &map.layer[k].cubes[index].sides[top_side];
        }
        // now check for the side
        index = (J+k)*mapsize + I+1+k;
        if ( index < map.layer[k].cubes.size() 
            && map.layer[k].cubes[index].sides[right_side] != 0 ) { 

            return &map.layer[k].cubes[index].sides[right_side];
        }
        index = (J+k)*mapsize + I+k;
        if ( index < map.layer[k].cubes.size() 
            && map.layer[k].cubes[index].sides[left_side] != 0 ) { 

            return &map.layer[k].cubes[index].sides[left_side];
        }
    }
} else {
    // right triangle
    for ( k = max_n_layer; k > -1; --k ) {

        index = (J+1+k)*mapsize + I+1+k;

        if ( index < map.layer[k].cubes.size() 
            &&  map.layer[k].cubes[index].sides[top_side] != 0 ) { 
            return &map.layer[k].cubes[index].sides[top_side];
        }

        index = (J+1+k)*mapsize + I+k;
        if ( index < map.layer[k].cubes.size() 
            && map.layer[k].cubes[index].sides[left_side] != 0 ) { 

            return &map.layer[k].cubes[index].sides[left_side];
        }
        index = (J+k)*mapsize + I+k;
        if ( index < map.layer[k].cubes.size() 
            && map.layer[k].cubes[index].sides[right_side] != 0 ) { 

            return &map.layer[k].cubes[index].sides[right_side];
        }
    }
}    
// well, no match found. As I said is up to you to decide how to do in this case
return nullptr;

编辑

我建议你尝试另一种方式。

认为屏幕不是由四边形图 block 划分的,而是由您已经描绘的三角形划分的。模型的每个 2D 瓦片将由其中两个三角形组成,因此您要绘制立方体的所有边。因为每个立方体都不绘制,甚至不创建背面,它们永远不会被绘制。

您可以尝试实现一种专门的 z 缓冲区算法,方法是为您必须在屏幕上绘制的每个三角形存储离观察者较近的一侧的索引。 使用您已有的代码(一次)计算所有三角形的顶点坐标。

            (I,J)              //For every node (I,J) you have a left and a right triangle
           .  *  .
(I+1,J) *  .  |  .  * (I,J+1)
              *
          (I+1,J+1)

您正在逐层创建立方体,我猜,每一层在基准平面上都有不同的高度。使用之前计算的坐标创建立方体的每一面。对于每个面(仅指向观察者的 3 个面)考虑其 2 个三角形中的每一个。按顺序进行就可以轻松判断是否可见,只需更新对应三角形中存储的ID即可。

完成此步骤后,您必须绘制每个三角形一次,因为您已经删除了隐藏的三角形。 要确定从屏幕坐标到单元格索引的逆变换,您只需计算击中哪个三角形,然后查找对应的 ID。所以将 x,y 转换回 I,J (你已经有了这些方程),如果 x < (J-I)/cubesize 则选择左三角形否则是正确的。

关于c++ - 2D 等距 - SFML - 正确的公式,错误的坐标范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33906516/

相关文章:

java - 通过其 id 获取等距单元格的 X、Y 坐标

c++ - SQLite3和释放锁

python - boost::python 可以将对象的所有权传递给 python 回调函数吗?

c++ - 什么是 "balanced-token-seq"

c++ - EventType 不是类或命名空间

C++ SFML 成员初始值设定项

c++ - 鼠标位置改变绘制立方体的大小

c++ - 绘制大量重叠的二维阴影

c# - 对于 iso 网格上的瓦片 map 画笔,算法比中点圆更好

actionscript-3 - 大物体的等距深度排序问题