c++ - 2D 钻石(等距) map 编辑器 - 无限扩展的纹理?

标签 c++ 2d textures sfml isometric

我目前正在开发 2D 等距 map 编辑器。 我显示包含点和纹理的实体(立方体,播放器)。 每个立方体由12个点组成。(12个点,但在sfml(sf::VertexArray)显示时作为4个点的3条边处理。

(我知道我时常包含一些“.cpp”,我的 IDE(visual studio)有问题,我正在尝试解决,请不要关心它。)

main.cpp

#pragma once
#include "globalfunctions.h" //global functions + main headers + class headers

int main() {
    int mapSize = 0;
    int cubeSize = 0;

    cout << "Map size: "; cin >> mapSize; cout << endl;
    cout << "Cube size: "; cin >> cubeSize; cout << endl;

    int windowWidth = (mapSize * cubeSize) - (cubeSize * 2);
    int windowHeight = ((mapSize * cubeSize) - (cubeSize * 2)) / 2;

    renderWindow window(windowWidth, windowHeight, mapSize, cubeSize);
        int nbMaxTextures = 9;
        for (int t = 0; t < nbMaxTextures; t++) {
            window.loadTexture("test", t);
        }

    window.run();

    return EXIT_SUCCESS;
}

全局函数.h

#pragma once
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <iostream>
#include <math.h>
//#include <sstream>
#include <vector>

using namespace std;

sf::Vector2u isometricToCartesian(int i, int j, int cubeSize) {
    sf::Vector2u carth;
        carth.x = (j - i) * (cubeSize / 2);
        carth.y = (j + i) * (cubeSize / 4);

    return carth;
}

sf::Vector2u cartesianToIsometric(int x, int y, int cubeSize) {//TODO
    sf::Vector2u iso;
        iso.x = 0;
        iso.y = 0;

    return iso;
}

#include "entity.h"
#include "renderWindow.h"

renderWindow.h

#pragma once

class renderWindow {
    public:
        renderWindow(float WIDTH, float HEIGHT, int MAPSIZE, int CUBESIZE);
        void run();
        void loadTexture(sf::String folder, int numTexture);

        //SETTERS
        //...

        //GETTERS
        //...

    private:
        int mCurrentLayerID;
        int mMapSize;
        int mCubeSize;
        int mSelectedTexture;

        vector<entity> mMap;

        sf::RenderWindow mWindow;
        vector<sf::Texture> mTextures;
            sf::Texture mMemoryTexture;

        void processEvent();
        void update(sf::Time deltaTime);
        void render();

//CUBE ACTION-------------------------------------------
        void addCube(int layerID, float x, float y);
        entity& getCube(int ID);
        entity& getCubeAt(float x, float y);
        vector<sf::VertexArray> loadCube(int cubeID);//UPDATE DATA LIKE COORDINATES -> create/chnge the vertex
        void drawCube(int cubeID);//draw the vertex

        //VARIABLES
        vector<sf::VertexArray> verticesSide1;
        vector<sf::VertexArray> verticesSide2;
        vector<sf::VertexArray> verticesSide3;
//CUBE ACTION-------------------------------------------
};

#include "renderWindow.cpp"

renderWindow.cpp

#pragma once

renderWindow::renderWindow(float WIDTH, float HEIGHT, int MAPSIZE, int CUBESIZE) : mWindow(sf::VideoMode(WIDTH, HEIGHT), "") {
    mMapSize = MAPSIZE;
    mCubeSize = CUBESIZE;

    mSelectedTexture = 6;

    mCurrentLayerID = -1;

    int x = 0;
    int y = 0;

    //default layer
    for (int j = 0; j < mMapSize; j++) {
        for (int i = 0; i < mMapSize; i++) {
            x = isometricToCartesian(i, j, mCubeSize).x;
            y = isometricToCartesian(i, j, mCubeSize).y;
            addCube(0, x, y);
        }
    }

    for (int c = 0; c < mMap.size(); c++) {
        verticesSide1.push_back(loadCube(c)[0]);
        verticesSide2.push_back(loadCube(c)[1]);
        verticesSide3.push_back(loadCube(c)[2]);

        //then only do that when something the cube's coordinate changed
    }
}

void renderWindow::run() {
    sf::Clock clock;
    sf::Time timeSinceLastUpdate = sf::Time::Zero;
    sf::Time TimePerFrame = sf::seconds(1.f / 60.f);

    while (mWindow.isOpen()) {
        processEvent();

        timeSinceLastUpdate += clock.restart();

        while (timeSinceLastUpdate > TimePerFrame) {
            timeSinceLastUpdate -= TimePerFrame;

            processEvent();
            update(TimePerFrame);
        }

        render();
    }
}

void renderWindow::loadTexture(sf::String folder, int numTexture) {
    if (mMemoryTexture.loadFromFile("textures/" + folder + "/" + to_string(numTexture) + ".jpg"))
        mTextures.push_back(mMemoryTexture);
    else
        cout << "Texture n°" << numTexture << " as failed to load." << endl;
}


//SETTERS
//...

//GETTERS
//...

//PRIVATE METHODE
void renderWindow::processEvent() {
    sf::Event event;

    while (mWindow.pollEvent(event)) {
        switch (event.type) {
        case sf::Event::Closed:
            mWindow.close();
            break;

        case sf::Event::KeyPressed:
            if (event.key.code == sf::Keyboard::Escape)
                mWindow.close();
            break;

        case sf::Event::MouseButtonPressed:
            if (event.MouseButtonPressed == sf::Mouse::Left)
                getCubeAt(event.mouseButton.x, event.mouseButton.y).setTexture(0, mSelectedTexture);//TEST
                getCubeAt(event.mouseButton.x, event.mouseButton.y).setTexture(1, mSelectedTexture + 1);//TEST
                getCubeAt(event.mouseButton.x, event.mouseButton.y).setTexture(2, mSelectedTexture + 2);//TEST
            break;

            /*case sf::Event::MouseMoved:
                cout << "(" << event.mouseMove.x << ", " << event.mouseMove.y << ")" << endl;
                break;*/
        }
    }
}

void renderWindow::update(sf::Time deltaTime) {
    //REMEMBER: distance = speed * time
    //MOVEMENT, ANIMATIONS ETC. ...
}

void renderWindow::render() {
    mWindow.clear();

    for (int c = 0; c < mMap.size(); c++) {
        drawCube(c);
    }

    mWindow.display();
}

//CUBE ACTION-------------------------------------------
void renderWindow::addCube(int layerID, float x, float y) {
    //Thoses make the code more readable:
        int half_cubeSize = mCubeSize / 2;
        int oneQuarter_cubeSize = mCubeSize / 4;
        int twoQuarter_cubeSize = oneQuarter_cubeSize * 2;
        int treeQuarter_cubeSize = oneQuarter_cubeSize * 3;

    mCurrentLayerID = layerID;

    entity dummy(mMap.size(), 0, layerID);
        dummy.addPoint(12);
        dummy.addTexture(6);
        dummy.addTexture(7);
        dummy.addTexture(8);
    //SIDE 1------------------------------------------------
        dummy.setPoint(0, x, y + oneQuarter_cubeSize);
        dummy.setPoint(1, x + half_cubeSize, y + twoQuarter_cubeSize);
        dummy.setPoint(2, x + half_cubeSize, y + mCubeSize);
        dummy.setPoint(3, x, y + treeQuarter_cubeSize);
    //SIDE 2------------------------------------------------
        dummy.setPoint(4, x + half_cubeSize, y + twoQuarter_cubeSize);
        dummy.setPoint(5, x + mCubeSize, y + oneQuarter_cubeSize);
        dummy.setPoint(6, x + mCubeSize, y + treeQuarter_cubeSize);
        dummy.setPoint(7, x + half_cubeSize, y + mCubeSize);
    //SIDE 3------------------------------------------------
        dummy.setPoint(8, x, y + oneQuarter_cubeSize);
        dummy.setPoint(9, x + half_cubeSize, y);
        dummy.setPoint(10, x + mCubeSize, y + oneQuarter_cubeSize);
        dummy.setPoint(11, x + half_cubeSize, y + twoQuarter_cubeSize);

    mMap.push_back(dummy);
}

entity& renderWindow::getCube(int ID) {
    for (int c = 0; c < mMap.size(); c++) {
        if (mMap[c].getID() == ID)
            return mMap[c];
    }
}

entity& renderWindow::getCubeAt(float x, float y) {//TO DO
    return entity(-1, 0, 0);
}

vector<sf::VertexArray> renderWindow::loadCube(int cubeID) {
    vector<sf::VertexArray> vertices;
    vertices.push_back(sf::VertexArray());
    vertices.push_back(sf::VertexArray());
    vertices.push_back(sf::VertexArray());

    vertices[0].setPrimitiveType(sf::Quads);
    vertices[0].resize(4);

    vertices[1].setPrimitiveType(sf::Quads);
    vertices[1].resize(4);

    vertices[2].setPrimitiveType(sf::Quads);
    vertices[2].resize(4);

    sf::Vector2f tv0 = sf::Vector2f(0, 0);
    sf::Vector2f tv1 = sf::Vector2f(mCubeSize, 0);
    sf::Vector2f tv2 = sf::Vector2f(mCubeSize, mCubeSize);
    sf::Vector2f tv3 = sf::Vector2f(0, mCubeSize);

    sf::Vector2f v0 = sf::Vector2f(getCube(cubeID).getPoint(0, 0), getCube(cubeID).getPoint(0, 1));
    sf::Vector2f v1 = sf::Vector2f(getCube(cubeID).getPoint(1, 0), getCube(cubeID).getPoint(1, 1));
    sf::Vector2f v2 = sf::Vector2f(getCube(cubeID).getPoint(2, 0), getCube(cubeID).getPoint(2, 1));
    sf::Vector2f v3 = sf::Vector2f(getCube(cubeID).getPoint(3, 0), getCube(cubeID).getPoint(3, 1));

    sf::Vector2f v4 = sf::Vector2f(getCube(cubeID).getPoint(4, 0), getCube(cubeID).getPoint(4, 1));
    sf::Vector2f v5 = sf::Vector2f(getCube(cubeID).getPoint(5, 0), getCube(cubeID).getPoint(5, 1));
    sf::Vector2f v6 = sf::Vector2f(getCube(cubeID).getPoint(6, 0), getCube(cubeID).getPoint(6, 1));
    sf::Vector2f v7 = sf::Vector2f(getCube(cubeID).getPoint(7, 0), getCube(cubeID).getPoint(7, 1));

    sf::Vector2f v8 = sf::Vector2f(getCube(cubeID).getPoint(8, 0), getCube(cubeID).getPoint(8, 1));
    sf::Vector2f v9 = sf::Vector2f(getCube(cubeID).getPoint(9, 0), getCube(cubeID).getPoint(9, 1));
    sf::Vector2f v10 = sf::Vector2f(getCube(cubeID).getPoint(10, 0), getCube(cubeID).getPoint(10, 1));
    sf::Vector2f v11 = sf::Vector2f(getCube(cubeID).getPoint(11, 0), getCube(cubeID).getPoint(11, 1));

    vertices[0][0] = sf::Vertex(v0, tv0);
    vertices[0][1] = sf::Vertex(v1, tv1);
    vertices[0][2] = sf::Vertex(v2, tv2);
    vertices[0][3] = sf::Vertex(v3, tv3);

    vertices[1][0] = sf::Vertex(v4, tv0);
    vertices[1][1] = sf::Vertex(v5, tv1);
    vertices[1][2] = sf::Vertex(v6, tv2);
    vertices[1][3] = sf::Vertex(v7, tv3);

    vertices[2][0] = sf::Vertex(v8, tv0);
    vertices[2][1] = sf::Vertex(v9, tv1);
    vertices[2][2] = sf::Vertex(v10, tv2);
    vertices[2][3] = sf::Vertex(v11, tv3);

    return vertices;
}

void renderWindow::drawCube(int cubeID) {
    mWindow.draw(verticesSide1[cubeID], &mTextures[getCube(cubeID).getTexture(0)]);
    mWindow.draw(verticesSide2[cubeID], &mTextures[getCube(cubeID).getTexture(1)]);
    mWindow.draw(verticesSide3[cubeID], &mTextures[getCube(cubeID).getTexture(2)]);
}

//CUBE ACTION-------------------------------------------

实体.h

    #pragma once

    class entity {
    public:
        entity();
        entity(int id, int type, int numlayer);
        void addPoint(int nbPoints);
        void addTexture(int numTexture);

        //SETTERS
        void setPoint(int numPoint, float x, float y);
        void setTexture(int textureID, int numTexture);

        //GETTERS
        int getID();
        float getPoint(int numPoint, int numIndex);//if numIndex = 0 -> x || if numIndex = 1 -> y
        int getType();
        int getNumLayer();
        int getTexture(int numTexture);

    private:
        int mID;
        int mType;
        int mNumLayer;
        vector<sf::Vector2u> mPoints;
        vector<int> mTextures;
    };

    #include "entity.cpp"

实体.cpp

#pragma once

entity::entity() {
    mID = 0;
    mType = -1;
    mNumLayer = 0;
}

entity::entity(int id, int type, int numlayer) {
    mID = id;
    mType = type;
    mNumLayer = numlayer;
}

void entity::addPoint(int nbPoints) {
    mPoints.clear();

    int newSize = 0;
    for (int p = 0; p < nbPoints; p++) {
        newSize++;
    }

    mPoints = vector<sf::Vector2u>(newSize);
}

void entity::addTexture(int numTexture) {
    mTextures.push_back(numTexture);
}

//SETTERS
void entity::setPoint(int numPoint, float x, float y) {
    mPoints[numPoint].x = x;
    mPoints[numPoint].y = y;
}

void entity::setTexture(int textureID, int numTexture) {
    mTextures[textureID] = numTexture;
}

//GETTERS
int entity::getID() {
    return mID;
}

float entity::getPoint(int numPoint, int numIndex) {
    if (numIndex == 0)
        return mPoints[numPoint].x;
    else
        return mPoints[numPoint].y;
}

int entity::getType() {
    return mType;
}

int entity::getNumLayer() {
    return mNumLayer;
}

int entity::getTexture(int numTexture) {
    return mTextures[numTexture];
}

我做了很多测试,太多了,所以我现在不会发布它们,但如果您有任何问题,请随时提问。

这是标题中描述的问题:

enter image description here

这里是只显示一张脸的屏幕(代码中的顺序相同):

Side 1 only displayed

Side 2 only displayed

Side 3 only displayed

我唯一不明白的是,如果您手动输入坐标,单独显示的立方体工作得很好。即使是扩展的。但是坐标公式没问题...(我注意到 15x15 map 的立方体 n°50 和 64x64 立方体显示一个宽度为“无限”的矩形) 如果纹理被扩展(可能无限大),这表明坐标在某处不断增加?那么,为什么立方体仍然放置得很好?

这是 Assets (64 * 64 png): enter image description here enter image description here enter image description here 目录:纹理/测试/

最佳答案

不是真正的答案(因为代码无论如何都会被重写)所以对新代码的提示很少(其中一些已经在评论中提到)。

  1. 图 block 集

    在最终的等距引擎中使用 Sprite 。它们速度更快并且支持像素艺术。出于我的目的,我使用这两个免费使用的 tilesets ( 64x64 ) 的编译:

    两者兼容。我编译和编辑它们以适应我的引擎的需要。所以这就是我使用的(仍在进行中):

    my tileset

    白色0x00FFFFFF透明的意思。 Sprite 是不够的。我添加了有关瓷砖高度和旋转的信息。

    如果您从左上角看到前 4 个图 block ,它们都是旋转 90 度后的相同内容。所以我所有的瓷砖都有 4 个瓷砖的索引(90 度旋转)int rot[4] .这样我就可以旋转整个 map 或只是查看。我编译了集合,因此旋转彼此相邻。有 3 个选项:

    • tile[ix].rot[]={ ix,ix,ix,ix };其中 ix是没有旋转的平铺(地面)
    • tile[ix].rot[]={ ix,ix+1,ix,ix+1 };其中 ix2 的瓷砖旋转(右中间那两 block 带有砍树 block 的方 block )
    • tile[ix].rot[]={ ix,ix+1,ix+2,ix+3 };其中 ix4 的瓷砖旋转(如第一 block 瓷砖)

    索引仅对第一个图 block 有效或粗略,其他图 block 具有整个 rot[]数组从邻居旋转 1 个值。一些旋转是不可见的(见宽阔的树),但图 block 仍然存在以允许旋转。

    瓦片高度对于在编辑时放置瓦片以及自动生成 map 很重要。

    tileset info

    我还打算添加 A*为每个图 block 绘制 map ,以便我可以使用路径查找或计算水流量等。

  2. map 编辑器

    我更喜欢3D map 。对于更大的分辨率,您需要正确选择要查看的查看区域以最大限度地提高性能。另外一个好主意是创建空心地下,这样渲染速度会快得多(这也可以在渲染过程中虚拟完成,无需更新 map )。

    我建议对这些功能进行编码:

    • 让地面空心
    • 打好基础
    • 随机地形(菱形和方形)
    • 过滤掉小孔和光滑的边缘(将坡度瓦片添加到立方体瓦片中)
  3. 磁贴编辑器

    除了显而易见的绘图编辑器之外,您还应该添加其他功能,例如:

    1. 楼层 <->天花板
    2. 离开 <->
    3. <->返回
    4. 将大 Sprite 分成规则的图 block
    5. 复制/合并/粘贴
    6. 调整离开后的灯光<->右镜像操作

    它们在编译/编辑 tileset 资源时非常方便。如您所见,我的瓦片集中有许多瓦片在源瓦片集中不存在。它们是由这些功能+一些小的绘画编辑创建的...图 block 集底部的彩色 mask 用于遮盖并正确组合部分图 block 以创建缺失的部分...(您可以采用一侧形式瓷砖和其他来自其他......)

    tile operations

[注释]

有关更多信息/想法,请查看一些相关的Q/A:

关于c++ - 2D 钻石(等距) map 编辑器 - 无限扩展的纹理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36248821/

相关文章:

c++ - Opengl C++ : texture code textures all models with the same texture

C++ 检查可用的内存?

c++ - 如何将整数输入为空的vector <int>?

c++ - 在运行时查找数组的大小

javascript - 更改 Canvas 渐变对象的属性

3d - POVRay "Contour line"纹理

c++ - BOOST::CRC使用process.block()方法创建校验和

python - numpy 二维数组运算,保留某些位置

java - 绘图 : eg. Graphics.drawRect 或 Rectangle.draw 之间的差异

javascript - 更新纹理的正确方法(THREE.JS)