生成随机颜色并排除旧颜色的最佳方法

3

我不确定如何准确表达标题的意思,但我想在对象上每秒生成一种随机颜色。我还希望这种颜色与对象已有的旧颜色不相似。如果当前随机颜色与对象已有的颜色相同,则重新生成随机颜色。该对象仅是一个Text组件。

这是我用来生成颜色的函数:

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);
}

为了比较颜色是否相似,我将从答案中的函数移植并用于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);
}

我将随机颜色生成器放在while循环中的协程函数内,以使其一遍又一遍地运行,直到生成的颜色不相似。我使用“yield return null;”每次生成随机颜色时等待一帧,以确保不会冻结程序。
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);
}

所有东西看起来都运转正常,只有一个小问题。

问题是有时需要1到3秒钟才能重新生成与旧颜色不相似的颜色。我删除了yield return null;以防止它在每帧等待,现在似乎可以工作,但我冒着整个游戏冻结的风险,因为现在我正在使用随机函数控制while循环。已经注意到了微小的停顿,这不好。

有没有更好的方法来生成一个与对象新颜色不相似的随机颜色,而不会冻结游戏或等待几秒钟?


为什么不提前生成一个颜色数组,然后随机从中选择值呢?这样可以节省一些运行时间... - Zohar Peled
Zohar说的和我一样。我不能在另一个线程中使用Unity的API(Random) Thread。虽然我可以使用C#标准的Random API,但我试图避免使用存储在集合中的预定颜色。就像我的第一个回复一样,我希望颜色尽可能独特,而不是有时重新使用一些颜色。你的“Thread”建议是一个好主意,我会研究一下。 - Programmer
我之前的评论没有解释清楚。请记住,我不是在生成随机数。如果我是在生成随机数,我会采用这种方法。我是为了颜色而这样做。如果我必须预先填充它们,那么我更有可能在数组或哈希集中看到相似的颜色被反复使用。我想减少再次看到完全相同颜色的机会。 - Programmer
是的,如果在那个场次中有那么多玩家,否则就少一些。 - Programmer
每个玩家的计算机上都会生成4种颜色,而不是在同一台计算机上生成。这4种颜色必须至少是独特的。如果最后生成的颜色是蓝色,则下一个生成的颜色也可以是蓝色,但是更亮或更暗的蓝色。它不能是上次生成的确切蓝色。这减少了这8个玩家拥有太多相同类似颜色的机会。我觉得我让每个人都感到困惑了... - Programmer
显示剩余5条评论
2个回答

3
我会采用不同的方法:
  • 在[0;2]范围内生成一个代表红、绿、蓝的随机整数
  • 将第一步中确定的颜色加上0.5,但如果大于1则减去-1
  • 在[0;1]范围内生成两个独立的随机浮点数,分别作为剩余两个颜色分量

例子:假设我们有C1 = (R1; G1; B1) = (0.71; 0.22; 0.83)

  • 假设第一步产生索引0,即红色
  • 所以我们取R1 + 0.5 = 0.71 + 0.5f = 0.21f
  • 我们将G2和B2创建为新的绿色和蓝色分量,并得到(0.21f; G2; B2)

即使G2和B2与它们的前任相同,由于R2被移动,新颜色仍然明显不同

更新代码

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;
    }
}

测试:

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);
        }
    }
}

在运行编辑器测试后,在场景视图中(0; 0; 0)周围的结果为: enter image description here

是的,只要它是独立的,那就没问题。我今天有时间会用这个解决方案重新实现,但如果你有代码示例,那就更好了。 - Programmer
1
这里我们进行一个小编辑器测试,包括可视化 :) - Kay
很好。有截图更好。谢谢。 - Programmer

2
这里有另一个想法:
不要生成RGB分量,而是生成一个HSV组合(并进行转换)。就人眼而言,色相差异约为15度(在0-1刻度上约为0.05)足以被认为是不同的颜色。这是唯一重要的值。
例如,这是一个图像,顶部是红色(“0度”),底部是4种颜色:沿着“色相”滑块的7、15、30和60度。

Color distinction

“7度的差异可以看到,但看起来太接近了。15度的偏移肯定是不同的颜色(即使我会称它和下一个颜色都是“橙色”,它们至少是不同的橙色)。
您可以选择任何阈值,并生成新的颜色,直到新颜色与旧颜色相比超过您的阈值为止。对于亮度和饱和度,您几乎可以生成从0到1的任何值,因为即使值与前一个颜色完全相同,最小强制色相差异将导致产生足够不同的颜色。
然后,从HSV转换为RGB非常容易。”
我在两种不同的语言中做过这种事情,其中一种是为了一个项目而生成两到三种“不同颜色”,同时确保亮度足够高(即我不想让“黑色”成为可能),所以HSV是唯一明智的解决方案:我可以生成随机色调,将值和饱和度设为最大(例如随机范围[0.8,1.0])。另一个项目中,我将两个图像合并成一个纹理:我希望保持第一个图像的相同色调和饱和度,但根据第二个图像调整价值。
根据您的目标,您还可以偏置饱和度和价值数字,如果饱和度或价值过低(低于0.5),则色调会开始模糊在一起,并且过于相似(颜色会变得混浊)。或者,您可以生成介于0和1之间的单个浮点数,并将饱和度设置为该数字,将价值设置为1减去该数字

可以的,这也很容易实现。谢谢。 - Programmer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接