Jump to content
  • Advertisement

Tile Explosion for Diagonal Roads

Recommended Posts

Roads that turn at only 90 degrees seem unnatural, but rendering 45 degree turns is turning into a major headache, because it means that roads cut diagonally across tiles and leave pieces of themselves in neighboring tiles. In addition to having textures to render all the various turns an intersections, we also need every combination of nearby roads sticking out in the corners of the tile. In total that seems to be 4096 road tiles just to cover all the possibilities, even when we restrict roads to only going tile-center to tile-center.

We can save memory by doing some flips and rotations in UV coordinates, but I estimate we'd still need dozens of tiles. We can also save some effort by making the tiles transparent beyond the edges of the road, and then layer together multiple tiles when appropriate, either in pre-processing or in the shader. Whatever we do, it is becoming a major headache for something which seems to be so simple in concept. Am I somehow approaching this problem from the wrong direction?

Share this post

Link to post
Share on other sites

I'm misunderstanding you somewhere - you are talking about "tiles" but also mentioning tiles crossing over other tiles. Are your tiles always in a uniform, non-overlapping, axis-aligned grid, or do you depart from that?

What I mean is, if you have a diagonal road like this:


I would count that as four tiles - is that how your game works?

I'm also not understanding where you are getting the number 4096 from. Recently when I was thinking about the same concept (with grid-aligned tile-based games), I calculated it as a max of 256 for automapping tiles, and for roads specifically, it was less - and if using rotations and horizontal/vertical mirroring, less still.

Share this post

Link to post
Share on other sites
46 minutes ago, Servant of the Lord said:

You are talking about "tiles" but also mentioning tiles crossing over other tiles.

Actually I meant that roads cross over neighboring tiles. The tiles are a perfectly regular, axis aligned grid as one would expect. Here's an illustration of the kind of thing I was thinking about:


Notice how when the road goes from the center tile to the bottom-left tile, it doesn't just affect those two tiles but also adds a bit of road to the center-left tile and the center-bottom tile. That's how I got to 4096, because every combination of curves and intersections has to also include every possible combination of bits of road in the corners. For example, the center tile and the center-right tile are almost identical except for a bit of road in the bottom-right corner of the center tile.

Now I see that using smaller tiles could potentially solve the problem. By chopping the road into smaller pieces, there are more ways to assemble roads from fewer pieces. The downside of this is more tiles will mean more triangles on the screen, less variety in the shape of roads, and it also turns road rendering into a jigsaw puzzle where we have to figure out how to assemble the pieces to get the road we want. One tricky point will be ensuring that diagonal roads are the same width as axis-aligned roads.

Edited by Outliner

Share this post

Link to post
Share on other sites

Ouch, that does complicate matters.

Can you just cheat? Draw your roads as you normally do:


And then come back and layer the corners on top of existing tiles in a second pass:


Then, while you still have a buttload of tiles, you at least simplify it somewhat by not having to worry about the tiny corners.





Share this post

Link to post
Share on other sites
8 minutes ago, Outliner said:

Now I see that using smaller tiles could potentially solve the problem. By chopping the road into smaller pieces, there are more ways to assemble roads from fewer pieces. The downside of this is more tiles will mean more triangles on the screen, less variety in the shape of roads, and it also turns road rendering into a jigsaw puzzle where we have to figure out how to assemble the pieces to get the road we want. One tricky point will be ensuring that diagonal roads are the same width as axis-aligned roads.

As far as the "more triangles on the screen", you could make the road pieces in smaller tiles, but then use a tool to programmatically assemble the different variations (including rotations and flippings) into the large/regular tiles.

Even poor videocards can output bajillions of triangles, though, so I wouldn't worry about having to draw a bunch of small triangles unless it actually proves to be a bottleneck. I don't know how many will be onscreen in your game, though.

A third option would be to split the tiles into 2x2 instead of 3x3:


Share this post

Link to post
Share on other sites
4 hours ago, h8CplusplusGuru said:

Why cant you just splat the roads?

As far as I know, splat just means having multiple textures on a mesh so we can have one texture in some places and other textures in other places by using a splat map to to define where each texture is visible. That has several issues. For one, the splat map is a raster, so it tends to pixelate unless it has hugely high resolution. For another, roads often have a direction to their texture, like the lines of a highway, or the grooves of a dirt road. I'm not aware of any way to achieve that effect with splatting.

Edited by Outliner

Share this post

Link to post
Share on other sites

Every combination of road directions and wat-not, that would be a lot of tiles. However, maybe it is not necessary to pre-gen them and instead generate them on the fly as a road placement is determined. Like, is your game really going to be using all those tiles such that it's necessary to worry about having so many? I said splatting: my idea is, because the actual amount of roads are probably not that high, you can have a series of tiles that contain a road, say 3x3, and then you have a splat map and it splats the road right onto that area. The splat map can be generated on the fly depending on the shape of the road, and its resolution can be high as well because you aren't stretching it across your entire terrain. As far as directions of the road go, you can use a detail map or decals or something like that to add features that give the road a particular direction. (or just bake the data into the road splat ).

Edited by h8CplusplusGuru

Share this post

Link to post
Share on other sites

  • Advertisement
  • Advertisement
  • Popular Tags

  • Similar Content

    • By Luke McManus
      Hey all,

      So I have this unfathomable obsession with the games LIMBO and INSIDE, two games produced by Playdead Studios. But as a sound designer, I'm specifically into the audio of the game, its use of ambient tracks and specific sounds to create this level of emotion I've never felt in a platformer before.

      I took a stab at redesigning the sound and ambiance of one of LIMBO's trailers to see if I could replicate such emotionality with my own creativity. Check it out below. All feedback is appreciated. 
    • By RoKabium Games
      Been a bit quiet recently, but we've been busy bug fixing and tweaking things... Now we have lots more 'Particle effects' in the game, specifically here the Flamethrower and Enemy attacks!
    • By Gezu
      I'm working on Warriorb for about 2,5 years now and recently we made a playable version of the first part of the game. There are still some elements missing (mostly vfx and sfx) but most of it is close to final.
      I would love to hear what do you think about our game so far. Any critique, feedback, idea or tip is appreciated.
      If you are interested send me a pm and I will give you a steam key for the game.
      I'm eager to receive your feedback:
    • By Erik Nivala
      So, as the title says i am trying to figure out a good way sync all that information with other players in Unity. My problem is that i can't come up with a good solution since i am used to creating classes for everything e.g. attachments are its own class and then the weapon would save a reference to that attachment. But since you can't send custom classes over [Command] & [ClientRPC] i am a little stuck. A solution for this would be giving each attachment for a slot a unique ID and then passing the ID to other player but i feel like that is very error prone if other ppl add a new attachment or the IDs get mixed up.
      Is there a "standard" way that this is usually done that i am missing?
      I am fairly new to programming so any help is appreciated!
    • By MarcusAseth
      Hi guys, I'm having a problem rendering with DWrite, and I don't understand why, can you help me figure it out?
      As you can see in the image below, if you look carefully you'll notice that the top of R8 is cut (missing 1 row of pixels), the bottom of R11 is cut again, the 4 in R14 is rendered weird compared to the 4 in R4 and so on, if you look closely you'll spot more yourself.
      I can't figure out why 😕
      Under the image I'll also leave the code, in case I'm doing something wrong like with type conversion or stuff. Any help is much appreciated

      #include "GBAEmulator_PCH.h" #include "Disassembler.h" #include "GBAEmulator.h" Disassembler::Disassembler(LONG width, LONG height, HINSTANCE hInstance, GBAEmulator* emuInstance) : D2DWindowBase(width, height, hInstance, emuInstance), m_background(0.156f, 0.087f, 0.16f, 1.f), m_textFormat{ nullptr } { //Init Window std::string className = "Disassembler"; std::string windowName = "Disassembler"; WNDCLASSEX clientClass{}; clientClass.cbSize = sizeof(WNDCLASSEX); clientClass.style = CS_HREDRAW | CS_VREDRAW; clientClass.lpfnWndProc = GBAEmulator::DisassemblerWinProc; clientClass.hInstance = m_hInstance; //clientClass.hIcon =; TODO: Add Icon clientClass.hCursor = LoadCursor(m_hInstance, IDC_ARROW); clientClass.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1); clientClass.lpszClassName = className.c_str(); //clientClass.hIconSm =; TODO: Add Icon DWORD windowStyle = WS_VISIBLE | WS_CAPTION | WS_MINIMIZEBOX | WS_TABSTOP | WS_SYSMENU; m_isValid = InitWindow(windowName, clientClass, windowStyle, false); //Init DWrite if (m_isValid) m_isValid = InitDWrite(); std::vector<std::wstring> tempEntries{ L"PC: ", L"R0: ", L"R1: ", L"R2: ", L"R3: ", L"R4: ", L"R5: ", L"R6: ", L"R7: ", L"R8: ", L"R9: ", L"R10: ", L"R11: ", L"R12: ", L"R13: ", L"R14: ", L"R15: ", L"R16: " }; std::wstring value = L"-UNDEFINED-"; FLOAT left{}, top{}, right{ 300.f }, bottom{ 50.f }; for (auto& s : tempEntries) { m_entries.emplace_back(TextEntry{ s, value, D2D1_RECT_F{ left, top, right, bottom} }); top += 30.f; bottom += 30.f; } } bool Disassembler::InitDWrite() { //Set Text Format HRESULT hr; hr = m_DWriteFactory->CreateTextFormat( L"consolas", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 22.f, L"en-US", &m_textFormat ); if (FAILED(hr)) { MessageBox(NULL, "Failed to create TextFormat", "Error", MB_OK); return false; } //Set Colors m_renderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::SkyBlue), &m_fillBrush1 ); m_renderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Crimson), &m_fillBrush2 ); return true; } Disassembler::~Disassembler() { DestroyWindow(m_hwnd); if (m_textFormat) m_textFormat->Release(); if (m_fillBrush1) m_fillBrush1->Release(); if (m_fillBrush2) m_fillBrush2->Release(); } void Disassembler::Updade(float deltaTime) { } void Disassembler::Draw() { m_renderTarget->BeginDraw(); m_renderTarget->Clear(m_background); for (auto& entry : m_entries) { DrawEntryWithShadow(entry); } m_renderTarget->EndDraw(); } void Disassembler::DrawEntryWithShadow(const TextEntry& entry) { //shadow offset D2D1_RECT_F shadowPos = entry.position; shadowPos.top += 1.05f; shadowPos.left -= 0.95f; //draw text DrawEntry(entry.text, shadowPos, m_fillBrush2); DrawEntry(entry.text, entry.position, m_fillBrush1); D2D1_RECT_F valuePos = entry.position; FLOAT valueOffset = 50.f; valuePos.left += valueOffset; valuePos.right += valueOffset; shadowPos.left += valueOffset; shadowPos.right += valueOffset; //draw value DrawEntry(entry.value, shadowPos, m_fillBrush2); DrawEntry(entry.value, valuePos, m_fillBrush1); } void Disassembler::DrawEntry(const std::wstring& text, const D2D1_RECT_F& pos, ID2D1SolidColorBrush* brush) { m_renderTarget->DrawTextA( text.c_str(), static_cast<UINT>(text.size()), m_textFormat, pos, brush, D2D1_DRAW_TEXT_OPTIONS_NONE ); }  
    • By Catalin Danciu
      In the hopes that my thread is not off-topic or offensive in any way, I dare to ask the following "noob" question:
      what would be the correct way to create sprite animations from 2D *.bmp files?
      I have for reference the 2001 game  Desperados:Wanted Dead or Alive.
      with the help of some tools i found , I was able to extract files containing what seems to be animation frames and frame shadow masks for animating a horse.
      Attached are the archived assets.
      My goal is to recreate the demo level of the game using Unity, for educational purposes.
      I have started by loading the background map layer (also a large bmp file), and next step is to load a 2d character object and animate on the perspective.
    • By Alberto Muratore
      Hello, my name is Alberto Muratore and I'm a young game developer specialized in programming.

      Last summer I finished working on Abstract Arena, my first completed project published on Steam. During the development, lasted more than 1 year, I created every aspect of the game except for the audio sector. I previoulsy joined small competitions and had collaborations within the amateur scene, since I started having fun with game development when I was a kid. In the recent months I also started writing videogames reviews for a english-italian website about indie games.

      Abstract Arena Steam page: http://store.steampowered.com/app/678230/Abstract_Arena/
      Abstract Arena website: http://www.abstractarena.com/


      I'm currently offering my skills as freelancer in the role of programmer: what I'm searching for is a project that only lacks of the coding part. I'm very good at using the GameMaker: Studio engine, and I already own a license to export on Android platform. I can write code for any genre of 2D games, and I already have experience with the peculiarities of the Android platform (multiple touch controls, accelerometer). I will be able to start working full time only during September, so be aware of that.

      The plan is the following:
      1 - you have an idea about your next project (2D game of any kind, for Windows and/or Android platform),
      2 - you (or your team) create all of the graphics and audio resources,
      3 - I write the whole code for the game (gameplay, menus and anything within the game) putting everything togheter [during September],
      4 - if you like my work you can choose how much to pay for it, and you (or your team) keep the rights to sell the game without sharing any percentage with me.

      Thank you for reading my announcement
      If you have any question, please write me at the following address: albertomurat@gmail.com
    • By N Drew
      I am working on a 2D SideScroller game in my own made game engine using SFML and C++.I am searching for 2D artists,especially pixel artist for making and animating characters,backgrounds and other props that can be made in any Drawing Program.The artist will become part of the team of Hammer Studios and he got a part of the Revenue Sharing.If you are interested send me a mail at:ghiurcutaandrei@gmail.com .If you are not an artist but you want to be a part of our Team,Soon we will be recruiting an C++ AI programmers that worked in SFML/OpenGL.
      We work together using Discord.

    • By JoAndRoPo
      While going through a Game Design Document Template, I came across this heading - Core Game Loop & Core Mechanics Loop. 
      What's the difference? Can you provide some examples of an existing game?  Suppose if I am including these topics in a Game Design Document, how should I explain it so that my team can understand? 
  • Advertisement
  • Popular Now

  • Forum Statistics

    • Total Topics
    • Total Posts

Important Information

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

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!