【问题标题】:Best way to generate random Color and exclude old one生成随机颜色并排除旧颜色的最佳方法
【发布时间】:2018-05-16 23:08:43
【问题描述】:

不完全确定如何表达标题,但我想每秒在一个对象上生成一种随机颜色。我还希望这种颜色与对象已有的旧颜色不相似。如果当前的随机颜色与对象已有的颜色相同,则再次重新生成随机颜色。该对象只是一个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);
}

为了比较颜色是否相似,我移植并使用了this对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);
}

一切似乎都在解决一个小问题。

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

有什么更好的方法可以在不冻结游戏或等待几秒钟的情况下生成与对象的新颜色不相似的随机颜色?

【问题讨论】:

  • 为什么不提前生成一个颜色数组并从中选择随机值呢?这应该可以节省一些运行时间...
  • 我想过,但我不希望颜色是预先确定的或仅限于几种颜色。我只是希望随机颜色尽可能不同。
  • Zohar 说的一样。我不能在另一个线程中使用 Unity 的 API(随机)线程。虽然,我可以使用 C# 标准随机 API,但我试图避免使用存储在集合中的预定颜色。就像我的第一个回复一样,我希望颜色尽可能独特,而不是有时重复使用一些颜色。您的 Thread 建议是个好主意,我会研究一下。
  • 我没有很好地解释最后的评论。请记住,我不是在生成随机数。如果那是我正在做的事情,我会走这条路。我这样做是为了颜色。如果我必须预先填充它们,那么我更有可能在数组中看到相似的颜色,或者一遍又一遍地回收 HashSet。我想减少再次看到相同颜色的机会。
  • @mjwills 您的第一条评论给了我一个想法,即每次我需要同时使用 ThreadPool 生成多种颜色,然后选择与对象当前颜色不相似的颜色。这应该给我更多独特的颜色,然后预先填充颜色。

标签: c# unity3d random colors


【解决方案1】:

这是另一个想法:

与其生成 RBG 组件,不如生成 HSV 组合(并转换)。就人眼而言,hue 的差异约为 15 度(在 0-1 范围内约为 0.05)足以被视为不同的颜色。这是唯一重要的值。

例如,这是一张顶部为红色(“0 度”)的图像。沿着底部是 4 种颜色:沿着“色调”滑块的 7、15、30 和 60 度。

7 度的差异可以看到,但看起来太近了。 15 度偏移绝对是一种不同的颜色(即使我将它和下一个都称为“橙色”,它们至少是不同橙色)。

您可以选择所需的任何阈值并生成新颜色,直到新颜色与旧颜色相差的度数大于您的阈值。对于值和饱和度,您几乎可以生成 0 到 1 之间的任何值,因为即使这些值与之前的颜色完全相同,强制执行的最小色调差异也会导致颜色完全不同。

那么,converting from HSV to RGB 就很简单了。

我用两种不同的语言做过这种事情,其中​​一种是针对我特别想生成两种或三种“不同颜色”的项目,同时还要确保亮度足够高(即我没有希望“黑色”成为可能),因此 HSV 是唯一明智的解决方案:我可以生成随机色调、设置值和饱和度达到最大值(例如 random range [0.8,1.0])。我将两个图像合并为一个纹理的另一个项目:我想保持第一张图像的相同色调和饱和度,但根据第二张图像调整

您可能还需要根据您的目标来调整饱和度和值的数值,如果饱和度或值过低(低于 0.5),色调将开始模糊在一起并且过于相似(颜色都会变得浑浊)。或者,您可以生成一个介于 0 和 1 之间的浮点数,并将饱和度设置为 that number,将值设置为 one minus that number

【讨论】:

  • 是的。这也是可行且易于实施的。谢谢
【解决方案2】:

我会采取不同的方法:

  • 在[0]中生成一个随机整数; 2]分别代表红、绿、蓝
  • 在第一步确定的颜色上加 0.5,如果大于 1 则减去 -1
  • 在 [0; 范围内生成 2 个独立的随机浮点数; 1] 用于其余两个颜色分量

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

  • 假设步骤 1 产生索引 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) 附近出现场景视图

【讨论】:

  • 是的,只要是不同的,就可以了。今天有时间我会用这个解决方案重新实现它,但是如果你有一个代码示例,那就更好了。
  • 这里我们用一个小编辑器测试,包括可视化:)
  • 不错。有截图就更好了。谢谢。
猜你喜欢
  • 2010-11-12
  • 1970-01-01
  • 1970-01-01
  • 2014-12-21
  • 2010-12-07
  • 1970-01-01
  • 1970-01-01
  • 2015-11-07
相关资源
最近更新 更多