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:
- 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)
- 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.