Change the color of SpriteRenderer in Unity3d - c#

I have this method that picks a random color from a preset array of colors.
public Color GetRandomColor()
{
return colors[rand.Next(0, colors.Length)];
}
Then I use it in this method in another class
public void RandomizeColors()
{
for(int i = 0; i< spriteRenderers.Length; i++)
{
spriteRenderers[i].color = rColors.GetRandomColor();
}
}
The problem is that the colors of the sprite renderers all get set to some strange values. The RGB gets set to something in the thousands.
if I use spriteRenderers[i].color = Color.black for example it works fine.
I have checked the return values of GetRandomColor() and they are all correct. Where am I doing this wrong ?
Edit:
The array of colors. This is part of a constructor:
colors = new Color[7];
colors[0] = new Color(87f,72f,161f) ;
colors[1] = new Color(39f,145f,221f);
colors[2] = new Color(233f,191f,57f);
colors[3] = new Color(238f,133f,57f);
colors[4] = new Color(238f,71f,46f);
colors[5] = new Color(193f,57f,235f);
colors[6] = new Color(104f,176f,58f);

Unity Documentation says you need to have the r, g and b arguments in the range of 0f to 1f. So you can basically divide your r, g and b by 256.

Directly extracted from the Unity Scripting API:
Color
struct in UnityEngine
Description
Representation of RGBA colors.
This structure is used throughout Unity to pass colors around. Each color component is a floating point value with a range from 0 to 1.
Here is the Link to the Documentation:
Unity Scripting API

Related

C# How to keep 1 range of colors in a bitmap?

So in c# I have an image in a bitmap with different colors in it. Now i'm trying to only keep one single range of colors in the image and all the others can be removed (turn them into white pixels). Now the color I want to extract is yellow but just comparing the pixels their color with Color.Yellow won't suffice since the pixels can have different shades of yellow so I'm guessing I sort of need to filter out all the other colors but I can't seem to figure out on how to do it.
I've read something about convolution but I don't see a way to implement this directly into the program.
Is there a way that makes this possible that I only keep the color yellow and it's diffent shades in the image?
Thanks in advance.
Here is a fast and simple solution.
It uses a function, which will plug-in with a post you can find here.
This is the function:
public Color ToWhiteExceptYellow(Color c, int range)
{
float hueC = c.GetHue();
float e = 1.5f * range; // you can adapt this nuumber
float hueY = Color.Yellow.GetHue();
float delta = hueC - hueY;
bool ok = (Math.Abs(delta) < e);
//if (!ok) { ok = (Math.Abs(360 + delta) < e); } // include these lines ..
//if (!ok) { ok = (Math.Abs(360 - delta) < e); } // for reddish colors!
return ok ? c : Color.White;
}
It works well with yellow but as color hues is a wraparound number it will need more code to work with the wrap point color (red). I have included two lines to help out.
To make it work change these lines in the linked post:
// pick one of our filter methods
ModifyHue hueChanger = new ModifyHue(ToWhiteExceptYellow);
..and..
// we pull the bitmap from the image
Bitmap bmp = new Bitmap( (Bitmap)pictureBox1.Image); // create a copy
..and..
c = hueChanger(c, trackBar1.Value); // insert a number you like, mine go from 1-10
..and..:
// we need to re-assign the changed bitmap
pictureBox2.Image = (Bitmap)bmp; // show in a 2nd picturebox
Don't forget to include the delegate:
public delegate Color ModifyHue(Color c, int ch);
and the using clause:
using System.Drawing.Imaging;
Note that one ought to dispose of the old content to avoid leaking the images, maybe like so:
Bitmap dummy = (Bitmap )pictureBox2.Image;
pictureBox2.Image = null;
if (dummy != null) dummy.Dispose;
// now assign the new image!
Let's see it at work:
Feel free to expand on this. You could change the function's signature to include a target color and add ranges for brightness and/or saturation..
very vague definition,
if i understood what you want to do, i'd do it like this:
Iterate over every pixel of the bitmap and compare it to the yellow range (if outside - assign white value)
Convert every pixels RGB value to CMYK (search online for a conversion formula) [Y = (1-Blue-Black) / (1-Black)]
Assign white value if YellowMin
Convolution wont help you, it acts in spacial domain not in color

Best way to generate random Color and exclude old one

Not exactly sure how to phrase the title but I want to generate a random color every second on an object. I also want this color to not be similar to the old color the object already has. If the current random color is the-same as the color the Object already has, re-generate the random color again. That object is simply a Text component.
This is the function I use to generate the color:
public Color genRandomColor()
{
float r, g, b;
r = UnityEngine.Random.Range(0f, 1f);
g = UnityEngine.Random.Range(0f, 1f);
b = UnityEngine.Random.Range(0f, 1f);
return new Color(r, g, b);
}
For comparing if the color are similar, I ported and used the function from this answer to C#.
public double ColourDistance(Color32 c1, Color32 c2)
{
double rmean = (c1.r + c2.r) / 2;
int r = c1.r - c2.r;
int g = c1.g - c2.g;
int b = c1.b - c2.b;
double weightR = 2 + rmean / 256;
double weightG = 4.0;
double weightB = 2 + (255 - rmean) / 256;
return Math.Sqrt(weightR * r * r + weightG * g * g + weightB * b * b);
}
I put the random color generator in while loop inside a coroutine function to run over and over again until the generated color is not similar. I use yield return null; to wait for a frame each time I generate a random color so that it does not freeze the program.
const float threshold = 400f;
bool keepRunning = true;
while (keepRunning)
{
Text text = obj.GetComponent<Text>();
//Generate new color but make sure it's not similar to the old one
Color randColor = genRandomColor();
while ((ColourDistance(text.color, randColor) <= threshold))
{
Debug.Log("Color not original. Generating a new Color next frame");
randColor = genRandomColor();
yield return null;
}
text.color = randColor;
yield return new WaitForSeconds(1f);
}
Everything seems to be working with one minor problem.
The problem is that it takes 1 to 3 seconds to re-generate a color that is not a similar to the old one sometimes. I removed yield return null; to prevent it from waiting each frame and it seems to work now but I risk freezing the whole game since I am now using the random function to control the while loop. Already, I've noticed tiny freezes and that's not good.
What's a better way to generate a random color that is not similar to the object's new color without freezing the game or waiting for seconds?
I would take a different approach:
Generate a random integer in [0; 2] representing red, green, blue
Add 0.5 to the color just determined in the first step but subtract -1 if greater than 1
Generate 2 independent random float numbers in the range [0; 1] that are taken for the remaining two color components
Example: Assume we have C1 = (R1; G1; B1) = (0.71; 0.22; 0.83)
Assume step 1 produces index 0 i.e. red
So we take R1 + 0.5 = 0.71 + 0.5f = 0.21f
We create G2 and B2 as new green and blue components and get (0.21f; G2; B2)
Even if G2 and B2 are identical to their predecessors the new color will be clearly distinct as R2 is shifted
Update code
public static class RandomColorGenerator
{
public static Color GetNextPseudoRandomColor(Color current)
{
int keep = new System.Random().Next(0, 2);
float red = UnityEngine.Random.Range(0f, 1f);
float green = UnityEngine.Random.Range(0f, 1f);
float blue = UnityEngine.Random.Range(0f, 1f);
Color c = new Color(red, green, blue);
float fixedComp = c[keep] + 0.5f;
c[keep] = fixedComp - Mathf.Floor(fixedComp);
return c;
}
}
Test:
public class RandomColorTest
{
[Test]
public void TestColorGeneration()
{
Color c = Color.magenta;
for (int i = 0; i < 100; i++)
{
Vector3 pos = new Vector3(i / 20f, 0f, 0f);
c = RandomColorGenerator.GetNextPseudoRandomColor(c);
Debug.Log(i + " = " + c);
Debug.DrawRay(pos, Vector3.up, c);
}
}
}
Result in scene view around (0; 0; 0) after running editor test
Here's another idea:
Rather than generating the RBG components, instead generate a HSV combination (and convert). As far as the human eye concerns, a difference in hue of about 15 degrees (~0.05 on the 0-1 scale) is sufficient to be considered a different color. This is the only value that's important.
For example, here's an image with red along the top ("0 degrees"). Along the bottom are 4 colors: 7, 15, 30, and 60 degrees along the "hue" slider.
The 7 degree difference can be seen but looks too close. The 15 degree shift is definitely a different color (even if I'd call both it and the next one "orange", they're at least different oranges).
You can pick whatever threshold you want and generating new colors until the new color is more than your threshold in degrees away from the old. For value and saturation, you can pretty much generate any value from 0 to 1, as even if the values are exactly the same as the previous color, the minimum enforced hue difference will result in a color that is sufficiently different.
Then, converting from HSV to RGB is pretty easy.
I've done this sort of thing in two different languages, one of which was for a project where I specifically wanted to generate two or three "different colors" while also insuring that the brightness was sufficiently high (i.e. I did not want "black" to be possible), so HSV was the only sensible solution: I could generate random hues, set value and saturation up towards max (e.g. random range [0.8,1.0]). The other project I was merging two images into a single texture: I wanted to keep the same hue and saturation of the first image but adjust the value based on the second.
You may also want to bias the Saturation and Value numbers, depending on your goals, if either Saturation or Value is too low (under 0.5) hues will start blurring together and be too similar (the colors will be all muddy). Alternatively, you could generate a single float between 0 and 1 and set the Saturation to that number and Value to one minus that number.

Array random color not generating

I'm facing a problem where when I declare array of random colors. It shows random colors in a particle system on game start, but every time the game starts it shows white. I don't know why it happens, I didn't set white in my array.
public class A : MonoBehaviour
{
Color[] colors = {
new Color (170, 7, 107),
new Color (58, 96, 115),
new Color(81, 99, 149),
new Color(71, 118, 231)
};
void start()
{
GetComponent<ParticleSystem>().startColor = colors[Random.Range(0, colors.Length)];
}
In Unity, a color's ARGB components range between 0.0 to 1.0. So anything >1 will be considered 1 and so all the colors are white naturally.
To convert the colors, divide each component by 255. You can either do this yourself or leave it to the code itself. Also, don't forget to cast as float. Credit to #Masih Akbari for reminding me about this.
So, it should be :
Color[] colors = {
new Color (170f/255, 7f/255, 107f/255),
new Color (58f/255, 96f/255, 115f/255),
new Color(81f/255, 99f/255, 149f/255),
new Color(71f/255, 118f/255, 231f/255)
}
The reason for this is that colours are normalised in Unity. You have to divide each float you've set by 255 to get the actual value, e.g.
Color[] colors = {
new Color (170/255, 7/255, 107/255),
new Color (58/255, 96/255, 115/255),
new Color(81/255, 99/255, 149/255),
new Color(71/255, 118/255, 231/255)
};
Your Color values must be between 0 to 1. everything after 1 is considered white.
Don't forget to cast your number as a float

C# XNA How do I set a Texture2D to a single color?

How do I set every non transparent pixel in an arbitrary Texture2D to let's say Color.White temporarily?
Just a conditional loop? This won't be exact syntax for it, but something among the lines of:
Texture2D texture = /*copy the texture you want to change*/;
Pixel pixel;/*note it's really inexact, so don't mind it, the idea is to show how it would be done*/
for(int i=0; i<texture.width; i++)
{
for(int j=0; j<texture.height; j++)
{
pixel = texture.GetPixel(i, j);
if(pixel.Color.A==1)
pixel.Color = Color.White;
}
}
I cannot stress this enough: do NOT just copy paste this in, this is similar to pseudocode, just there to show how it would be done.
Haven't tested this, but out of my head, you could do something like this:
Color[] az = Enumerable.Range(0, 100).Select(i => Color.White).ToArray();
Texture2D texture = new Texture2D(GameRef.GraphicsDevice, 10, 10, false, SurfaceFormat.Color);
texture.SetData(az);
This first creates an array with 100 elements, and fills it with Color.White
Then using the SetData, we fill it with the colorarray.
Just make sure the array is the same size as the texture size (height*width)

Draw a Graph in C# using zedGraph

I need to create a graph that have the following properties:
The X Axis is for schools names.
The Y Axis is for classes names.
In Point (x,y) I need to put a dot that it's color will represent the number of students (darker means more students).
I'm using ZedGraph (using that sample: http://zedgraph.org/wiki/index.php?title=Gradient-By-Value_Demo), but I don't know how to put the dot (and to determine it's dark-level) in the correct position (compare it to school's name and class's name).
Also, I don't know how to make the X and Y axis to show the school's name and the class's name.
How can I do that? (It's NOT have to be in zedGraph).
many thanks!
The problem is that ZedGraph is treating a Text-type scale in a little bit strange way. So it's almost impossible to display correctly data when you have both scales of Text type.
But you can fool ZG a little bit.
The whole trick is to display the data using coordinates of hidden scale, while displaying second, fake scale.
string[] schools = { "A", "B", "C" };
string[] classes = { "cl. 1", "cl. 2", "cl. 3" };
var pane = zg1.GraphPane;
Random x = new Random();
// Hide the basic scale, show the second with text labels
pane.X2Axis.Type = AxisType.Text;
pane.X2Axis.IsVisible = true;
pane.Y2Axis.Type = AxisType.Text;
pane.Y2Axis.IsVisible = true;
pane.XAxis.Scale.IsVisible = false;
pane.YAxis.Scale.IsVisible = false;
pane.X2Axis.Scale.TextLabels = schools;
pane.Y2Axis.Scale.TextLabels = classes;
// Main problem - synchronize the scales correctly
pane.XAxis.Scale.Min = -0.5;
pane.XAxis.Scale.Max = schools.Count() - 0.5;
pane.YAxis.Scale.Min = -0.5;
pane.YAxis.Scale.Max = classes.Count() - 0.5;
pane.YAxis.MajorGrid.IsZeroLine = false;
// generate some fake data
PointPairList list = new PointPairList();
for(int i=0;i<schools.Count();i++)
for (int j = 0; j < classes.Count(); j++)
{
list.Add(new PointPair(i, j, x.Next(30)));
}
var pointsCurve = pane.AddCurve("", list, Color.Transparent);
pointsCurve.Line.IsVisible = false;
// Create your own scale of colors.
pointsCurve.Symbol.Fill = new Fill(new Color[] { Color.Blue, Color.Green, Color.Red });
pointsCurve.Symbol.Fill.Type = FillType.GradientByZ;
pointsCurve.Symbol.Fill.RangeMin = 0;
pointsCurve.Symbol.Fill.RangeMax = 30;
pointsCurve.Symbol.Type = SymbolType.Circle;
pane.AxisChange();
zg1.Refresh();
I don't do quite this in my project, but I do change the color based on some criteria. It should be pretty easy for you to modify. Look at the svn depot in stochfit.sourceforge.net at the graphing classes. You may also want to take a look at the version of zedgraph I have in my depot, some image capture and a scaling bug were fixed.

Categories