Code that changes color not working

I’m making a grand strategy game, and I have two classes, a nation and a province. The province has a public nation owner, a public List pixellist, and a public color32 color, and the nation has a public color32 color. My code is supposed to take this


a(This is a screenshot of part of the file) and color each nation’s owners in. For some reason, I end up with this
5596144--578557--upload_2020-3-16_17-11-19.png
(another screenshot) ignore the sizes being different). Here is my code, does anyone know why?
Also, some of the provinces are not coloring, though when I check them in the inspector I see a full pixellist, and an onwer. This isn’t even consistent across owners.

using UnityEngine;
using System;
public class ProvincePixelAssigner : MonoBehaviour
{
    public Texture2D provinceMap;
    public Texture2D changemap;
    public GameObject Economyobject;
    Economy _Economy;
    // Start is called before the first frame update
    void Start()
    {
        _Economy = Economyobject.GetComponent<Economy>();
        for(int x = 1; x < provinceMap.width; x++){
            for (int y = 1; y < provinceMap.height; y++){
                Color32 color = provinceMap.GetPixel(x,y);
                foreach(Province province in _Economy.ProvinceArray){
                    if(province.color.r == color.r && province.color.g == color.g && province.color.b == color.b){
                        province.pixellist.Add(new Vector2 (x,y));
                    }
                }
            }
        }
        foreach(Province province in _Economy.ProvinceArray){
            if(province.owner != null && province.pixellist.Count > 0){
            foreach(Vector2 coord in province.pixellist){
                changemap.SetPixel(Convert.ToInt32(coord.x), Convert.ToInt32(coord.y), province.owner.color);
            }
            }
        }
        changemap.Apply();
    }
}

As opposed to what? What is wrong with the image posted? What should it actually look like?

Without knowing what the desired output is I can only guess at the problem, but I do see a lot of == being used with values of type float (the RGB values are floats), and that has potential to cause issues. Though that’s usually only an issue if math is being performed on the floats, but sometimes math creeps in to unexpected places.

Also, I know this isn’t your main problem, but that is an insanely slow and inefficient way to do what you’re doing. I can’t even fathom how long this Start() method must be taking to to run. After you’ve resolved the functionality, please come back to the thread and let us help you rewrite this to be more efficient - I’m fairly certain I could literally get you at least 100x speed improvement, no exaggeration.

what are we supposed to see here?
that you made a syntactical or logical error that correctly paints your map into incorrect colors?
that’s hardly the case.

I’ve also seen that, but then maybe it’s Color32, and he’s not showing the actual data, so we can only guess.

Thank you for the quick reply, and for future help with the speed, that’d be great!. I’m going to use one country for an example of the desired output, but it would change this 5596249--578566--upload_2020-3-16_17-44-35.png to 5596249--578569--upload_2020-3-16_17-44-54.png. Thus, every province would be recolored to show the nations.

So then, you think that my code would work? If that’s the case, then the issue would most likely be with the data right? I’ll check to see if that’s the case, thank you

well it certainly doesn’t have any glaring errors, but maybe the problems with it are more nuanced. it all depends on what else you do with the actual data. some of the countries that I’ve seen in your image, are colored the same as in the original one. some other are not. it seems to be working, it just doesn’t take proper data into account.

and I concur with StarManta btw, that particular technique is appalling. we don’t live in the 80’s anymore, pictures aren’t made by plotting pixels these days.

Ok, I believe the issue was with how the province’s color’s were determined. Thanks alot for your replies! StarManta, how would you suggest I speed up the color change?

So basically the problem seems to be that it’s only changing some of the provinces to the nation’s color, is that correct?

Actually, if the floating point issue is the reason for the problem, some of my recommendations for efficiency might help solve the problem, as well, so I’ll go ahead and overview them.

First thing that jumps out at me is GetPixel(). There’s a certain amount of overhead in getting information out of a texture, so every time you call GetPixel() you’ll incur that overhead - and here you’re probably calling it, what, a million times? If you use GetPixels() instead before your loop, you’ll only incur that overhead once. And it’ll also make your loops a little easier. Now each pixel, instead of being represented by two floats, will be represented by a single integer - this is both faster and more reliable.

Color[] provinceMapPixels = provinceMap.GetPixels();
for (int p=0; p < provinceMapPixels.Length; p++) {

Next up is pixellist. Now, to make this fit with the GetPixels change, you’ll want to make this list a List now. One thing about List<> is that it benefits from knowing ahead of time about how large it might end up being, which you can specify by sending a number in the constructor (e.g. pixellist = new List(1000); ). If you don’t do this, then it will be forced to guess at what its capacity will be, and anytime you exceed that capacity, it must make a new guess at the size (I think it doubles the size each time?), grab a new chunk of memory and copy over the old values to it, and this takes time. Normally it’s not a lot of time, but when you get into the hundreds or thousands of items it can add up having to do this copying thing 10 or 20 times, for every province. You don’t have to get the capacity exactly right, but the closer you get the less often it will have to resize itself to fit the ever-growing list of pixels.

So make this a List, initialize it with an approximate capacity when you create the list, and line 18 will now look more like:

province.pixellist.Add(p);

Now I’d like to look at lines 16-17 and replace them with a dictionary lookup. Before you start looping through the pixels, let’s loop through your provinces and generate a Dictionary<Color, Province>. Now, rather than looping through the provinces for every single pixel, you can just use provinceDict[someColor] and it’ll go straight to it - dictionaries are quite fast. The setup would look something like:

var provinceDict = new Dictionary<Color, Province>();
foreach (Province province in _Economy.provinceArray) {
provinceDict.Add(province.color, province);
}

Then to access this, you can use provinceDict like I said above, but I’ll also add a safety check:

Color thisPixel = provinceMapPixels[p];
Province thisProvince = null;
if (provinceDict.ContainsKey(thisPixel) ) {
thisProvince = provinceDict[thisPixel];
}
else {
Debug.LogWarning($"Pixel number {p}, color {thisPixel} was not found in our province list!");
//break;
}

This will print out a warning every time it comes across a pixel that doesn’t correspond to a province. Now, this first time you run it, you probably want to uncomment that “break” line, because I’m guessing if there’s one then there will be thousands of these log statements, and just printing the log statements will take a considerable amount of time. This is one potential spot where your original algorithm may have been breaking. If you’re expecting pixels that don’t fit in any provinces (Maybe the ocean pixels? idk) then you should skip the whole “else” section there, but be advised that in that case you won’t be alerted to whether or not this problem is a problem. (Maybe you could create a fake “province” to which you add all pixels that don’t fit any other province, and then paint that province’s pixels onto the final result, as a test?)

So that takes care of optimizing the reading of the map. Now for writing of the map, this should be pretty easy, as it’s just the inverse of what we’ve done already - just liek GetPixel vs GetPixels, SetPixel repeatedly is much slower than SetPixels once.

You might wish to call GetPixels again to get changemap’s pixels, but if changemap’s initial state looks the same as provinceMap, then you may as well just use the Color[ ] array you already have. In either case, loop through your provinces like you’re doing now, and you can do this:

foreach (int thisPixelIndex in province.pixellist) {
pixels[thisPixelIndex] = province.owner.color;
}
....
changeMap.SetPixels(pixels);
changeMap.Apply();

This replaces the whole conversion of a Vector2 back into integers, which is one of the potential spots where your original algorithm may have been breaking.

Now, if you’ve done all this, you’ll have a much faster algorithm that should accomplish much the same thing as your first one, with two risky lines being made less risky. It’s possible the logic error is somewhere else, in which case, head on back here and we’ll dig a little deeper into your data structures to see if there’s an issue with those. But at the very least, you’ll be able to see the results without what I imagine was a multiple-second (at least) lag on Start() with the old algorithm! Hell, this one might be fast enough to be almost realtime.

1 Like

Alright, I tried what you said, and my code worked. You’ve already helped alot, but I was wondering, do you have any idea how code that would draw a black line between the provinces at startup would look?

That’s probably something I’d try to do with a shader and not with C# code. Unfortunately, I suck at shaders so I can’t give you a starting point there but I’m sure if you search for something like “outline shaders” or anything along those lines you’ll find something.

Sadly, I suck at shaders too, worse case scenario I’ll do it manually but I bet there’s something out there

That probably is best done with a shader, but if you already have the pixels in an array you can probably easily do something like this:

bool isOutline = (
PixelsAreDifferent(pixels, p, p + provinceMap.width)  ||
PixelsAreDifferent(pixels, p, p - provinceMap.width)  ||
PixelsAreDifferent(pixels, p, p + 1)  ||
PixelsAreDifferent(pixels, p, p - 1)  );
if (isOutline) pixels[p] = Color.black;

....

bool PixelsAreDifferent(Color[] pixels, int index1, int index2) {
if (index1 < 0 || index2 < 0 || index1 > pixels.Length || index2 > pixels.Length) return false;
return pixels[index1] != pixels[index2];
}