4
\$\begingroup\$

Crusader Kings, Europa Universalis and several other strategy games use provice based maps where "tiles" are not squares nor hexagons. Border style indicates different ranks/owner.

enter image description here


I've done some research and this is what I found from modding guides: they seem to use a source image to dynamically generate the borders which are used to separate the provices but also to indicate currently selected areas (border changes color or flashes etc) and Im pretty sure that same file is used for "clickable areas" (click on provice to get province details screen).

Source image contains all the provinces painted with solid colors. This image is same size as height map (water bodies and land masses) to align geographical shapes with provinces.

enter image description here

Then there's some sort of dictionary file (text, JSON etc) that has all the province colors that maps these colors to meta data about the province. It also makes provinces "loopable" for game engine in order to add them to in-game database and to use colors to find the correct shapes from image.

Example in JSON:

// "key" is RGB color that points to correct province in previous image
"253:28:37": {
  "name": "Kent",
  "otherStuff": 13
},
"142:133:150": {
  "name": "Oxford",
  "otherStuff": 54
}

How to create referenceable borders and clickable areas from source image?

\$\endgroup\$
2
\$\begingroup\$

What I would suggest is reading the provincemap pixel-by-pixel, and matching the color values (use Color32 for simplicity) you get from the pixels with the color values defined in the JSON file. That way, each province has a list of pixels (tiles) that it controls.

At this point, when clicking on the map, if you use raycasting you can figure out the tile coordinates, and thus find out which province has been clicked.

As for the borders, I do belive LineRenderers (or some optimized form of it) is the way to go for borders. If you actually look closely in the reference image you posted, (for example, where The Solent, Winchester, and Dorset meet) you can see that the borders don't match up perfectly, leaving these jagged edges, which is typical for linerenderers.

To achieve this, some semi-mad algorithms have to be run. If you take every "tile" in the memory and ask if their neighbouring tiles belong to the same province, you can figure out which tiles are "border tiles". You can also keep track of which province they are bordering with, which is useful if you want different styles of borders later on.

Once you have the border tiles, you basically have the points you need to input into the linerenderer. You want to sort the points clockwise/counter clockwise first though, but that's another issue.

At this point you have one big "chain" of points, that create a line around the whole province. You could split the chain up into smaller parts (one part per neighbouring province) if you want thinner/fatter border types for different province neighbours. If you treat the ocean as a province, your border mesh should match up with the coastline aswell.

If you're using 3D terrain, you also probably want to offet the Y value of the line points to reflect the height of the terrain. That could be done by raycasting or just reading straight from the heightmap.

This should take you 90% there. The rest really depends on what kind of terrain you're using. At first you're gonna get blocky (and possibly unaligned) borders, just becuase of how you straight up converted a 2D tile pos to a 3D world pos. But by using some fancy spline curves you could probably get a decent looking result. Furthermore, you might wanna consider "sharing" border meshes between provinces, for the sake of optimization and general prettyness.

\$\endgroup\$
0
\$\begingroup\$

Here is how I would do it:

You will need two textures, one with your "real" world, and an other one, where each country is represented by one color (the image you provided is perfect). The latter texture must be marked as "Read/Write" in the Importer options.

Then, create a script and find the country according to the color you are hovering / clicking:

private Camera cam;

void Start()
{
    cam = Camera.main;
}

void Update()
{
    RaycastHit hit;
    if (!Physics.Raycast(cam.ScreenPointToRay(Input.mousePosition), out hit))
        return;

    Renderer rend = hit.transform.GetComponent<Renderer>();
    MeshCollider meshCollider = hit.collider as MeshCollider;

    if (rend == null || rend.sharedMaterial == null || rend.sharedMaterial.mainTexture == null || meshCollider == null)
        return;

    Texture2D tex = rend.material.mainTexture as Texture2D;
    Color countryColor = tex.GetPixel(hit.textureCoord * tex.width, pixelUV.y * tex.height);

    // Then, find the country based on the color.
    // You can use ColorUtility.TryParseHtmlString if you want to store an hexadecimal representation of your color in your data file (JSON / Database)      
    // https://docs.unity3d.com/ScriptReference/ColorUtility.TryParseHtmlString.html

   // Highlight the real texture with a custom shader
   // This shader would take two textures : the displayed one and the colored one, and a color
   // The color would help you highlight the correct texel. Something like :
   /*
         fixed4 frag (v2f i):SV_Target{
             fixed4 mainCol=tex2D(_MainTex,i.uv);
             fixed4 countryCol=tex2D(_ColoredTex,i.uv);
             // if( countryCol == _HighlightCol )
             //    Make mainCol brighter
             return mainCol; 
         }
   */
}
\$\endgroup\$
  • \$\begingroup\$ I'm far from an expert, but I would store the "borders" information as the alpha value of the colored texture for example. But I don't know how you could distinguish "inner" borders from "outer" borders. \$\endgroup\$ – Hellium Oct 31 '17 at 19:12
  • \$\begingroup\$ can't you use different images and overlap them? LineRederer is too expensive. \$\endgroup\$ – Saad Anees Nov 1 '17 at 11:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.