Jump to content
  • Advertisement
Sign in to follow this  
pulo

Best way to represent multi-layered tilemap for rendering and logic

This topic is 827 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi there,

 

i am currently creating a small 2d top-down game (just for learning purposes). The map itself is build with Tiled (http://www.mapeditor.org/) - great tool. Now i already have implemented most of the rendering code, but i am still wondering if there are other/better ways of doing it, especially in the context of frustum culling and update logic for the tiles.

 

This is how my setup in Tiled looks:

  1.  I have different layer types in Tiled:
    • Two Layers with type background, which are basically just static backgrounds for the map, they won't be changed in run-time. 
    • One Layer which has type action, which contains all tiles which have different effects in game (e.g. collision, destruction - like bushes).
    • One Layer which has type foreground, which is basically like the static background, but will be rendered after each entity in the game.
    • I am considering one additional layer which would have type animated, which does contain all tiles which are animated somehow (only environmental stuff, no entities)
  2. Each layer can use each tileset (texture) loaded in tiled (thus is not restricted to just one)

Is this even a good Setup? How would you organize those layers?

 

Now in my code those layers are represented by one class: TilemapLayer which is basically only for the rendering of those. Each non-empty tile of the layers from "Tiled" creates an Instance:

struct Instance {
    Instance() {};
    Instance(float x, float y, float z, float tileWidth, float tileHeight, float uOffset, float vOffset) :
        textureInfos(tileWidth, tileHeight, uOffset, vOffset), position(x, y, z) {
    }
    DRE::Vector3f position;
    DRE::Vector4f textureInfos;
};

Those instances are what the name suggests, instances for an instance-buffer. I group those by the tilesets they are using to minimize texture-swapping and Draw-Calls like this:

// Here we "order" by texture atlas so that we minimize switching!
m_instances[countByte].push_back(Instance(offsetX, offsetY, 0.0f, (float)tileWidth, (float)tileHeight, uOffset, vOffset));

Rendering looks like this:

DRE::uint_t offsetCounter = 0;

DRE::uint_t stride = sizeof(Instance);
DRE::uint_t offset = 0;
m_graphicsObject->GetContext()->SetVertexBuffers(m_instanceBuffer.GetAddressOf(), 1, 1, &stride, &offset);

for (DRE::uint_t i = 0; i < m_instances.size(); ++i) {
    // Update the Shader variables with tile set texture and information
    unsigned int textureWidth = (*m_tilesets)[i]->GetTextureWidth();
    unsigned int textueHeight = (*m_tilesets)[i]->GetTextureHeight();
    m_graphicsObject->GetRenderParameterManager()->SetParameter("textureWidth", textureWidth);
    m_graphicsObject->GetRenderParameterManager()->SetParameter("textureHeight", textueHeight);
    m_graphicsObject->GetRenderParameterManager()->SetParameter("shaderTexture", *(*m_tilesets)[i]->GetTexture());

    stateObject->UpdateConstantBuffer("perLayer");
    stateObject->UpdateShaderResourceView("shaderTexture");

    if (m_isDynamic) {
        // Update the Instance Buffer
        D3D11_MAPPED_SUBRESOURCE bufferData;
        bufferData.pData = NULL;
        bufferData.DepthPitch = bufferData.RowPitch = 0;
        m_graphicsObject->GetGraphicsDevice()->GetDeviceContext()->Map(m_instanceBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &bufferData);
        memcpy((Instance*)bufferData.pData, m_instances[i].data(), sizeof(Instance) * m_instances[i].size());
        m_graphicsObject->GetGraphicsDevice()->GetDeviceContext()->Unmap(m_instanceBuffer.Get(), 0);

        // Draw the layer with instances
        m_graphicsObject->GetContext()->DrawInstanced(4, (DRE::uint_t)m_instances[i].size()); //
    }
    else {
        m_graphicsObject->GetContext()->DrawInstanced(4, (DRE::uint_t)m_instances[i].size(), offsetCounter);
        offsetCounter += (DRE::uint_t)m_instances[i].size();
    }

 

This setup works reasonable fine (losing like 60fps in debug for each new tileset (texture) introduced), but i am worried about frustum culling. There is currently no easy way to do this, at least i am not seeing it. Do you have any suggestions?

 

My plan for the update logic (collision checks etc.) is to create a different representation for the game-state of the map, based on the action layer out of tiled. I am fine with updating only the visible section of the map. How do is represent this in memory? 

 

I am thankful for any input one might give me for this. If you have any questions, feel free to ask.

Edited by pulo

Share this post


Link to post
Share on other sites
Advertisement
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!