我希望能以一定的频率闪光。举个例子,比如说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;
我尝试过不同的time
,但这并不重要,所以如果您不认为这是个坏主意,那么让我们继续使用它吧!
现在,每当我想要闪现一个对象时,在它自己的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的间隔,得到了高达48毫秒的差值,虽然看起来不太大,但在屏幕上确实有很大的不同。
因此,问题归结为:如何在保持灵活性(比率和频率)的同时,获得快速、可调整的闪光灯,并具有同步闪存?
谢谢你的帮忙!
发布于 2018-10-22 14:30:42
您可以使用协同线和WaitForSeconds
来实现这一点。
// 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(on):2(off)和8Hz
StartCoroutine(Flash(8.0f, onRatio = 1, offRatio = 2));
通过这种设置,Coroutine在while(true)
-loop中“永远”运行。因此,在启动具有不同参数的新Coroutine之前,不要忘记
StopAllCoroutines();
现在,如果要在Update方法中动态地更改这个值,则必须在roder中添加一些控制标志和附加变量,以确保只有在发生更改时才会调用新的Coroutine:
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;
}
}
备注:
FlashRateInterval
,但如果你出于某种原因需要使用它,你可以让它
公共枚举FlashRateInterval { AllwaysOn,twoHerz = 2,fourHerz = 4,eightHerz =8}
以便直接使用正确的值。flashRate.herz
。您也不会调用size值cube.meters
。我建议把它重命名为flashRate.frequency
。为了证明同步,你将需要访问所有行为并比较它们的价值(所以我要说一些static List<YourBehavior>
),而不是在Coroutine中,等待所有的bools被设置为真,然后再继续自己的行为。为此,您需要额外的bool,因为可能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<YOUR_COMPONENT>()
(有点慢)不同,您还可以做一些类似的事情
public static List<YOUR_COMPONENT> Components = new List<YOUR_COMPONENT>();
private void Awake()
{
if(!Components.Contains(this)){
Components.Add(this);
}
}
因此,您现在还禁用了组件和对象。
发布于 2018-10-22 14:19:18
您得到了一些差异,因为您正在使用<=条件在Update()循环中执行所有操作。在较慢/更快的机器上,您将有更多/更少的差异,因为帧的持续时间永远不会等于您的频率。
尝试在Corotine中做所有的事情:统一协同工作文档
//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);
}
}
https://stackoverflow.com/questions/52931177
复制相似问题