【问题标题】:2D isometric - SFML - Right formulas, wrong coordinate range2D 等距 - SFML - 正确的公式,错误的坐标范围
【发布时间】:2016-02-27 15:40:39
【问题描述】:

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

所以我只需要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);
}

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

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

函数(不要打扰 cmets):

我尝试在没有“for 循环”的情况下在我的多维数据集向量中获取多维数据集的条目。 为什么 ?点击时使用更少的 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%准确,我不是绘画天才哈哈):

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

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

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:我更新了我的代码以允许我只绘制立方体的下侧,所以我可以这样做(草):

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

【问题讨论】:

    标签: c++ sfml isometric linear-equation


    【解决方案1】:

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

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

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

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

    编辑

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

    认为屏幕不是由四边形图块划分的,而是由您已经描绘的三角形划分的。您的模型的每个 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 &lt; (J-I)/cubesize 则选择左边的三角形,否则选择右边的三角形。

    【讨论】:

    • 我已经设置了“高度”,在我所有的代码中,“高度”是层数。我只需要更正 I/J 计算即可。
    • 感谢您的回答,不幸的是,我不知道是不是因为我缺乏英语技能或编码(数学?),但我不确定是否理解。我明白了我不得不放弃不会显示的脸的部分,但什么是节点?
    • @Madz 从您的代码中创建每个立方体,计算其顶点的 x,y 坐标,从两个索引 I 和 J。因此每两个索引 (I,J) 定义一个立方体。在您的图纸(和地雷)中,每个三角形或四边形瓷砖的每个顶点对应于几个指数(I,J)。您正在做的数学运算是将这些值转换为屏幕坐标并向后转换。
    • @Madz 您可能会在查看这篇简短的tutorialhere 时找到一些帮助
    • @Madz 我尝试根据我对您的代码的理解编写一些代码(公平地说不是太多)。我希望它能以某种方式帮助你。
    猜你喜欢
    • 2012-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-10
    • 2015-01-01
    • 1970-01-01
    • 2012-06-12
    • 2020-03-03
    相关资源
    最近更新 更多