【问题标题】:Unity: flash at frequency with variable on/off ratio统一:以可变开/关比的频率闪烁
【发布时间】:2019-03-26 15:16:51
【问题描述】:

我希望能够以特定频率闪烁内容。例如,假设为 2Hz。我还希望能够指定一个比率,我可以在其中显示事物,比如说循环的 2/3,并将其隐藏 1/3,因此比例为 2:1。这是一堆疯狂的闪光,所以我需要保持灵活的方式。可能有一些以 3:5 的比率和 2Hz 的频率闪烁,还有一些以 4Hz 的比率以 1:1 的频率闪烁,等等。

另外,我需要能够同步闪光。因此,如果一个对象已经在闪烁,而我开始闪烁另一个对象,则它们需要同步(或者更确切地说,它们的周期需要同步,闪烁可能会因比率不同而有所不同)。但如果在同一个频率下,它们需要同时“开启”,即使它们的比率不同。此外,它们都需要在最慢开启的同时开启。

我目前的方法:我有一个 GameObject FlashCycle,它本质上是在它的更新方法中计算我拥有的 3 个频率(2Hz、4Hz 和 8Hz)的进度。

 float time = Time.time;
 twoHerzProgress = (time % twoHerzInSeconds) / twoHerzInSeconds;
 fourHerzProgress = (time % fourHerzInSeconds) / fourHerzInSeconds;
 eightHerzProgress = (time % eightHerzInSeconds) / eightHerzInSeconds;

我尝试了不同的times,但这并不重要,所以如果你认为这不是一个坏主意,我们就坚持那个吧!

现在,每当我想在它自己的Update() 中闪烁对象时,我都会这样做:

switch (flashRate.herz)
    {
        case FlashRateInterval.twoHerz:
            show = flashCycle.oneHerzProgress <= onTimePercentage;
        case FlashRateInterval.fourHerz:
            show =flashCycle.twoHerzProgress <= onTimePercentage;
        case FlashRateInterval.eightHerz:
            show =flashCycle.fourHerzProgress <= onTimePercentage;
        default:
            show =true;
    }

如果show == true,则继续并显示对象。

不幸的是,这不会以平滑且规则的间隔闪烁对象。我测量了 2Hz 的间隔,得到了高达 48ms 的比率差异,虽然看起来差别不大,但它确实在屏幕上产生了影响。

所以问题归结为:如何在保持灵活性(比率和频率方面)的同时获得快速、定期的闪光并拥有同步闪光?

感谢您的帮助!

【问题讨论】:

    标签: c# unity3d flashing


    【解决方案1】:

    您可以使用 CoroutinesWaitForSeconds 来实现这一目标

    // onRatio and offRatio are "optional" parameters
    // If not provided, they will simply have their default value 1
    IEnumerator Flash(float frequency ,float onRatio = 1, float offRatio = 1)
    {
    
        float cycleDuration = 1.0f / frequency;
        float onDuration = (onRatio/ (onRatio + offRatio)) * cycleDuration;
        float offDuration = (offRatio/ (onRatio + offRatio)) * cycleDuration; 
    
        while(true)
        {
            show = true;
    
            yield return new WatForSeconds(onDuration);        
    
            show = false;
    
            yield return new WatForSeconds(offDuration);
        }
    }
    

    所以你可以用一个频率来调用它,例如8赫兹

    StartCoroutine(Flash(8.0f));
    

    这实际上等于您设置 onRatio = offRatio 的任何调用,例如

    StartCoroutine(Flash(8.0f, onRatio = 1, offRatio = 1));
    
    StartCoroutine(Flash(8.0f, onRatio = 2, offRatio = 2));
    
    ....
    

    或具有频率和比率,例如1(开):2(关),8Hz

    StartCoroutine(Flash(8.0f, onRatio = 1, offRatio = 2));
    

    通过这种设置,协程在while(true)-loop 中“永远”运行。所以,在你启动一个新的 Coroutine 之前,不要忘记先用不同的参数停止所有的例程

     StopAllCoroutines();
    

    现在,如果您想在 Update 方法中动态更改它,则必须在 roder 中添加一些控制标志和其他变量,以确保仅在发生更改时才调用新的协程:

    FlashRateInterval currentInterval;
    float currentOnRatio = -1;
    float currentOffRatio = -1;
    
    void Update()
    {
        // if nothing changed do nothing
        if(flashRate.herz == currentInterval
           //todo && Mathf.Approximately(<yourOnRatio>, currentOnRatio)
           //todo && Mathf.Approximately(<yourOffRatio>, currentOffRatio)
        ) return;
    
        StopAllCoroutines();
    
        currentInterval = flashRate.herz;
        //todo currentOnRatio = <yourOnRatio>;
        //todo currentOffRatio = <yourOffRatio>;
    
        switch (flashRate.herz)
        {
            case FlashRateInterval.twoHerz:
                StartCoroutine(2.0f);
                //todo StartCoroutine(2.0f, onRatio = <yournRatio>, offRatio = <yourOffRatio>);
            case FlashRateInterval.fourHerz:
                StartCoroutine(4.0f);
                //todo StartCoroutine(4.0f, onRatio = <yournRatio>, offRatio = <yourOffRatio>);
            case FlashRateInterval.eightHerz:
                StartCoroutine(8.0f);
                //todo StartCoroutine(8.0f, onRatio = <yournRatio>, offRatio = <yourOffRatio>);
            default:
                show =true;
        }
    }
    

    注意事项:

    1. 我不知道你的FlashRateInterval,但如果你出于某种原因需要使用它,你可以让它喜欢

      public enum FlashRateInterval
      {
          AllwaysOn,
      
          twoHerz = 2,
          fourHerz = 4,
          eightHerz = 8
      }
      

      为了直接使用正确的值。

    2. 我将调用频率变量flashRate.herz。您也不会调用大小值cube.meters。我建议将其重命名为 flashRate.frequency


    要实现同步,您需要以某种方式访问​​所有行为并比较它们的值(所以我会说一些 static List&lt;YourBehavior&gt;),而不是例如在协程中等到所有布尔值都为例如在继续你自己的之前设置为true。为此,您需要一个额外的布尔值,因为 show 可能在一个组件上永久为真。

    public bool isBlinking;
    
    IEnumerator Flash(float frequency ,float onRatio = 1, float offRatio = 1)
    {
        //todo: You'll have to set this false when not blinking -> in Update
        isBlinking = true;
    
        float cycleDuration = 1.0f / frequency;
        float onDuration = (onRatio/ (onRatio + offRatio)) * cycleDuration;
        float offDuration = (offRatio/ (onRatio + offRatio)) * cycleDuration; 
    
        // SYNC AT START
        show = false;
    
        // wait until all show get false
        foreach(var component in FindObjectsOfType<YOUR_COMPONENT>())
        {
            // skip checking this component
            if(component == this) continue;
    
            // if the component is not running a coroutine skip
            if(!component.isBlinking) continue;
    
            // Now wait until show gets false
            while(component.show)
            {
                // WaitUntilEndOfFrame makes it possible
                // for us to check the value again already before
                // the next frame
                yield return new WaitForEndOfFrame;
            }
        }
    
        // => this line is reached when all show are false
    
        // Now lets just do the same but this time wating for true
        // wait until all show get false
        foreach(var component in FindObjectsOfType<YOUR_COMPONENT>())
        {
            // skip checking this component
            if(component == this) continue;
    
            // if the component is not running a coroutine skip
            if(!component.isBlinking) continue;
    
            // Now wait until show gets false
            while(!component.show)
            {
                // WaitUntilEndOfFrame makes it possible
                // for us to check the value again already before
                // the next frame
                yield return new WaitForEndOfFrame;
            }
        }
    
        // this line is reached when all show are getting true again => begin of loop
    
        while(true)
        {
    
        .........
    

    除了使用有点慢的FindObjectsOfType&lt;YOUR_COMPONENT&gt;(),您还可以执行类似的操作

    public static List<YOUR_COMPONENT> Components = new List<YOUR_COMPONENT>();
    
    private void Awake()
    {
        if(!Components.Contains(this)){
            Components.Add(this);
        }
    }
    

    因此您还可以获得当前禁用的组件和对象

    【讨论】:

    • 我想用sync 解释的是,当我让另一个对象闪烁时,它不会自行启动,而是与其他对象同步闪烁。并且跨频率,因此当最慢的闪烁关闭时,其他所有人也需要关闭(然后快速闪烁首先打开)。我有点想在一个正弦波中,频率乘以它,但是当“最长”波再次处于“0°”时,所有其他波都太长了,即使它们之前已经通过了“0°”。我不知道这是否更容易理解,但我已经很感谢你的长回答了!
    • 也许这样做可以:它们都需要同时打开,即使它们具有不同的比率。如果它们具有不同的频率,则它们都需要在最慢的时刻打开(然后更频繁地然后最慢)这就是为什么频率都是 2^x。
    • 好吧,如果您将show 公开,那么您就可以访问它们,您可以按照我在上一节中所说的以及在开始新的协程之前检查show 是否在所有组件上为真.在您可以添加另一个检查它们是否关闭之前 - >它们都同时开始。因为你有 2^x 他们应该在最后同时再次关闭(当然这将不再适用于灵活的浮动请求)
    • 我试着把它找出来看看我更新的底部
    【解决方案2】:

    您有一些不同之处,因为您在具有

    尝试在 Corotine 中执行所有操作:unity coroutine docs

    //bad code below but i think its more understandable like this
    IEnumerator Flash() 
    {
       while(true)
       {
         BlinkOn();
         Sync();//sync here another cicle if you want to sync when on starts
         yield return new WaitForSeconds(yourDuration);// yourDuration*multiplier/something+0.5f....ecc
    
         BlinkOff()
         Sync();//sync here another cicle if you want to sync when of starts
         yield return new WaitForSeconds(yourDuration);
       }
    }
    

    【讨论】:

    • 你可能想使用yield return 否则你的协程会在第一帧之后结束。
    • 如果我使用以最快的速率(在我的情况下为 8Hz)“触发”的协程并在 4Hz 和四分之三的 2Hz 中跳过一个协程,它会很聪明吗? ?我有点喜欢在一个完成闪烁的集中位置的想法,并考虑让那段代码(即使重写为所有可能的美丽!)在每个 GameObject 中运行也一样糟糕?
    • 啊,好吧,所以你的建议的问题是,我很难让多个对象同步闪烁同时具有不同的闪烁比率,除非我为每个对象运行一个协程(即使那样他们可能会在某个时候离开)。即使它们的比率不同,我也需要它们同时“开启”所有(也就是它们的周期开始相同)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多