Godot Version
4.7
Problem
I am trying to create a map game similar to those made by Paradox as a fun side project. Currently, I am trying to create the map for the game. I have made a png with two countries divided into provinces. Each province has its own unique hex color as a means of identification; however, I want all of the provinces for a country to be of the same color which I am having trouble implementing.
My Work So Far
Here is the map, which I have as a texture, without any shaders:
I initially made a shader material that would highlight whatever province was clicked white. It looks like this:
This all worked fine. here are the two relevant bits of code for applying this shader
//This is the shader file itself, not sure if I selected the right coding language for this part of the post
shader_type canvas_item;
uniform vec4 province_highlight_color : source_color = vec4(1.0,1.0,1.0,1.0);
void fragment() {
vec4 input_color = texture(TEXTURE, UV);
if (abs(input_color.r- province_highlight_color.r) < 0.0001
&& abs(input_color.g- province_highlight_color.g) < 0.0001
&& abs(input_color.b - province_highlight_color.b) < 0.0001){
COLOR = vec4(1.0,1.0,1.0,input_color.a);
}
}
#and this is the function that handles the clicking of the province and subsequent application of the shader. Essentially what I am doing is taking the color of the pixel that is clicked passing its value to province_highlight_color then comparing it to all pixels in the texture. If the pixels are essentially the same color as the one clicked, the shader applies a white color over every matching pixel
func _unhandled_input(event):
camera_controller.handle_zoom(event)
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
var world_pos = camera.get_global_mouse_position()
var local_pos = map_sprite.to_local(world_pos)
var tex_size = map_sprite.texture.get_size()
var pivot_offset = tex_size / 2 if map_sprite.centered else Vector2()
var pixel_pos = local_pos + pivot_offset
pixel_pos.x = clamp(pixel_pos.x, 0, tex_size.x - 1)
pixel_pos.y = clamp(pixel_pos.y, 0, tex_size.y - 1)
pixel_pos = Vector2i(pixel_pos)
var img : Image = map_sprite.texture.get_image()
var pixel_color : Color = img.get_pixelv(pixel_pos)
var hex_color = "#" + pixel_color.to_html(false).to_upper()
if hex_color in color_to_province: # This if statement is where the selecting of provinces happens
province_name = color_to_province[hex_color]
#this line of code below is what does the color comparison and highlighting
map_sprite.material.set_shader_parameter("province_highlight_color", Color(pixel_color.r,pixel_color.g,pixel_color.b,1.0))
print(Color(pixel_color.r,pixel_color.g,pixel_color.b,1.0))
#map_coloring()
else:
print("unknown color: ", hex_color)
The next step was to make each country appear as a solid color. My first step was to create a class called Nation that has variables for the provinces, name, and color of a nation. There are three functions associated with this class. One to create all the nations, two at the moment, one to change the owner of the nation, haven’t used it yet so not relevant to this post, and one to return the owner of a given province. There is also an array of all the provinces. Here is all the code for all this (it’s in its own script called nation.gd which I preload into my main.gd):
extends Node
class Nation:
var name : String
var color : Color
var provinces : Array = []
#Nation management data
var nations : Dictionary = {}
var province_to_owner : Dictionary = {}
func create_nations():
var bulgaria = Nation.new()
bulgaria.name = "BULGARIA"
bulgaria.color = Color(0,1,0)
bulgaria.provinces = ["Videin","Lom","Orechow","Pleven","Ruse","Silistria","Dulovo","Dobrich","Cavarna","Tolbuchin","Berkovica","Vrasta","Sopot","Kazanlak","Tarnovo","Shumen","Sliven","Varna","Kyustendil","Pernik","Sofia","Pazardzhik","Stara Zagora","Plovdiv","Dimitrovgrad"]
nations[bulgaria.name] = bulgaria
var serbia = Nation.new()
serbia.name = "Serbia"
serbia.color = Color(0,0,1)
serbia.provinces = ["Salbac","Valjevo","Beograd","Pozega","Kraljevo","Arandelovae","Panavo","Pozarevac","Velico Gradiste","Petrovac","Turnu Severin","Cuprija","Boljevac","Zajecar","Jagodina","Knjazevac","Kursumlija","Medveda","Surdulica","Nis","Vlasotince","Pirot","Krusevac"]
nations[serbia.name] = serbia
func map_provinces_to_nations():
for nation in nations.values():
for province_name in nation.provinces:
province_to_owner[province_name] = nation
func change_provine_owner(province_name:String, new_owner: Nation):
var old_owner = province_to_owner.get(province_name,null)
if old_owner:
old_owner.provinces.erase(province_name)
new_owner.provinces.append(province_name)
province_to_owner[province_name] = new_owner
func get_province_owner(province_name: String) -> Nation:
return province_to_owner.get(province_name,null)
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
var province_list : Array = [
It is at this point that I am stuck. I am currently trying to use the same shader file to do both the highlighting and the normal shading of the map. I am able change the color of all the provinces and highlight the province I click on. The issue is, I can not color the two countries, Bulgaria and Serbia, properly.
I have tried several different things, though I do not have all the code since I have changed it so much, and none of them work. The two outcomes I seem to get is that either only one province gets colored properly, or every province gets the exact same color as seen here( one province is highlighted which it is supposed to be):
The current shader file looks like this:
//the province array has the color of all of the individual provinces I am using this array in a for loop to compare each province color to the overall texture in the same way I did the highlight
shader_type canvas_item;
const vec4 province_color_array[48] = {
vec4(0.0157, 0.2667, 0.0627, 1.0),
vec4(0.0157, 0.2667, 0.1255, 1.0),
vec4(0.0157, 0.2667, 0.1882, 1.0),
vec4(0.0157, 0.2667, 0.251, 1.0),
vec4(0.0157, 0.2667, 0.3137, 1.0),
vec4(0.0157, 0.2667, 0.3765, 1.0),
vec4(0.0157, 0.2667, 0.4392, 1.0),
vec4(0.0157, 0.2667, 0.502, 1.0),
vec4(0.0157, 0.2667, 0.5647, 1.0),
vec4(0.0157, 0.2667, 0.5843, 1.0),
vec4(0.0157, 0.2706, 0.0, 1.0),
vec4(0.0157, 0.2706, 0.0627, 1.0),
vec4(0.0157, 0.2706, 0.1255, 1.0),
vec4(0.0157, 0.2706, 0.1882, 1.0),
vec4(0.0157, 0.2706, 0.251, 1.0),
vec4(0.0157, 0.2706, 0.3137, 1.0),
vec4(0.0157, 0.2706, 0.3765, 1.0),
vec4(0.0157, 0.2706, 0.4392, 1.0),
vec4(0.0157, 0.2706, 0.502, 1.0),
vec4(0.0157, 0.2706, 0.5647, 1.0),
vec4(0.0157, 0.2745, 0.0, 1.0),
vec4(0.0157, 0.2745, 0.0627, 1.0),
vec4(0.0157, 0.2745, 0.1255, 1.0),
vec4(0.0157, 0.2745, 0.1882, 1.0),
vec4(0.0157, 0.2745, 0.251, 1.0),
vec4(0.502, 0.0, 0.0, 1.0),
vec4(0.0078, 0.5686, 0.4392, 1.0),
vec4(0.0078, 0.0353, 0.4392, 1.0),
vec4(0.0078, 0.2078, 0.4392, 1.0),
vec4(0.251, 0.0078, 0.4392, 1.0),
vec4(0.4392, 0.0078, 0.4039, 1.0),
vec4(0.4392, 0.0078, 0.3216, 1.0),
vec4(0.4392, 0.0078, 0.2118, 1.0),
vec4(0.149, 0.4392, 0.0078, 1.0),
vec4(0.0392, 0.4392, 0.0078, 1.0),
vec4(0.0078, 0.4392, 0.0196, 1.0),
vec4(0.0078, 0.4392, 0.2, 1.0),
vec4(0.0078, 0.4392, 0.2902, 1.0),
vec4(0.4392, 0.0078, 0.1608, 1.0),
vec4(0.2118, 0.4392, 0.0078, 1.0),
vec4(0.3647, 0.4392, 0.0078, 1.0),
vec4(0.0078, 0.4392, 0.1412, 1.0),
vec4(0.0078, 0.4392, 0.4353, 1.0),
vec4(0.4392, 0.0078, 0.0588, 1.0),
vec4(0.4392, 0.1686, 0.0078, 1.0),
vec4(0.4235, 0.4392, 0.0078, 1.0),
vec4(0.4392, 0.3922, 0.0078, 1.0),
vec4(0.4392, 0.2902, 0.0078, 1.0)
};
uniform vec4 province_highlight_color : source_color = vec4(1.0,1.0,1.0,1.0);
uniform vec4 nation_color : source_color = vec4(0.482, 0.157, 0.251, 1.0);
uniform vec4 nation_color_parser : source_color = vec4(0.0, 0.0, 0.0, 1.0);
void fragment() {
vec4 input_color = texture(TEXTURE, UV);
if (abs(input_color.r- province_highlight_color.r) < 0.0001 && abs(input_color.g- province_highlight_color.g) < 0.0001 && abs(input_color.b - province_highlight_color.b) < 0.0001){
COLOR = vec4(1.0,1.0,1.0,input_color.a);
}else {
for(int i=0; i < 48;i++) {
if(abs(input_color.r- province_color_array[i].r) < 0.0001
&& abs(input_color.g- province_color_array[i].g) < 0.0001
&& abs(input_color.b - province_color_array[i].b) < 0.0001
){
COLOR = nation_color;
}
}
}
}
I believe the issue comes in that very last line applying nation_color. In my main file I am using a function called map_coloring( ) that looks like this:
func map_coloring() :
for ins in range(nation_manager.province_list.size()):
for job in color_to_province:
if nation_manager.province_list[ins] == color_to_province[job]:
for key in nation_manager.nations:
if nation_manager.get_province_owner(job) == nation_manager.nations.get(ins,null) :
map_sprite.material.set_shader_parameter("nation_color", nation_manager.nations.get(key,null).color)
This function has three loops. The first loop goes through every province in the list of all provinces.
The second loop goes through each province and its matching color as defined early in this thread. It then compares each entry in the dictionary to the current entry in the province array and only moves on to the final loop when the two match (which they should only do once per iteration of the outermost loop)
The third loop goes through two iterations at the moment and is where the potential solution may lie. This third loop goes through all of the nations in the game, currently just bulgaria and serbia, and sees if the current province we are looking at is owned by one or the other and then assigning the proper nation color to the shader parameter. (bulgaria should be green and serbia should be blue.
After all this is done and the program is run, the result is everything is blue, as seen above, which leads me to believe the loops are going all the way through as serbia is the second country in nations and is the color that shows up.
I am assuming that the issue is that once nation_color changes, all of the provinces that were green are then turned blue.
Is there any way I can prevent this from happening and keep using the shader material I have setup, or will I have to switch up tactics? If the latter is the way, what would you recommend?
Thanks


