Sign in to follow this  
pulo

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

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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this