【问题标题】:Rotate object without coroutine?在没有协程的情况下旋转对象?
【发布时间】:2014-05-20 00:50:49
【问题描述】:

我目前正在开发一个时间系统,在该系统中,我希望能够控制日出开始、白天开始、日落开始和夜晚开始的时间。目前这在系统中是可能的,当“时钟”到达下一个开始时间时,我启动一个协程来开始旋转太阳和月亮。

我的协程是这样的:

IEnumerator RotateSun(Vector3 fDegrees, float SecondsDuringTimeSet)
{
    Quaternion quaFromPosition = _lSunLight.transform.rotation;
    Quaternion quaToPostion = Quaternion.Euler(fDegrees);

    for (float t = 0.0f; t < 1; t += Time.deltaTime / SecondsDuringTimeSet)
    {
        _lSunLight.transform.rotation = Quaternion.Lerp(quaFromPosition, quaToPostion, t);
        yield return null;
    }
}

正如你所见,我每次运行它时都会将值传递给它。这样做是这样的:

    if (_fCurrGameHour == _fTimeStartSunrise && _bMoveSunAndMoonOnlyOnce == true)
    {
        // Which TIMESET should it be:
        _en_CurrTimeSet = TimeSet.SUNRISE;

        // How long should the SUN move and how many which degree should it reach before that time ends
        vec3MoveSun = new Vector3(10.0f, 0, 0);
        StartCoroutine(RotateSun(vec3MoveSun, _fConvertSunriseToGTSeconds));

        // How long should the MOON move and how many which degree should it reach before that time ends
        vec3MoveMoon = new Vector3(180.0f, 0, 0);
        StartCoroutine(RotateMoon(vec3MoveMoon, _fConvertSunriseToGTSeconds));

        // Tell bool that we don't need to move the sun and moon more then once
        _bMoveSunAndMoonOnlyOnce = false;
    }

正如我写的那样,这个系统目前可以工作。然而,它是一种静态系统。所以目前我需要确保游戏的开始时间是日出时间。否则我的协程会中断,因为它不会在一天开始时间之前旋转到正确的位置。而且我绝对不能在日落时分开始游戏,然后太阳以完全错误的方式旋转,因为那时它更接近以错误的方式旋转(我假设)。

所以我的第一个问题是:我能否以某种方式使系统更具动态性?我希望仍然能够设置日出、白天、日落和夜晚应该开始的时间,因此在不同的季节,例如我可以在一天中有不同的长度。我希望能够在游戏中更改这些时间,然后如果 SUNSET 稍后设置,太阳将开始旋转慢一点,因为它应该需要更长的时间才能到达它的 SUNSET 位置。

关于我的第二个问题:是否也可以重写我的协程,以便我可以在我想要的任何时间开始游戏,并且太阳仍会以正确的角度旋转开始?

太阳总是以相同的旋转(170 度)落下并以相同的旋转(350 度)升起。我只想控制到达这些旋转所需的时间。也许我可以以某种方式将其移至更新阶段,而不是为此使用协程?我不知道如何在我的系统中更改它,所以如果有人有任何想法。请帮忙。

【问题讨论】:

  • 现在有人了吗?
  • 我尝试了更多想法但没有成功。以前有人做过吗?

标签: unity3d


【解决方案1】:

您可以使用State 设计模式摆脱协程。

public int SunRise = 8;
public int SunSet = 15;
public Quaternion RiseRotation;
public Quaternion SetRotation;
public int StartHour = 8; 

float time; // our time on a 24 hr system
bool wasSun; // what was our previous state?

void Start()
{
    GoToHour(StartHour);
    ToggleState(IsSun());   
}

void Update()
{
    bool isSun = IsSun();

    if (isSun != wasSun)
        ToggleState(isSun);

    time = (time + Time.deltaTime) % 24; // wrap at 24 hrs back to 0

    if (isSun) 
        UpdateSun();
    else
        UpdateMoon();
}

bool IsSun()
{
    return time >= SunRise && time < SunSet
}

void UpdateSun()
{
    float t = (time - SunRise) / (SunSet - SunRise);
    UpdateRotation(_lSunLight.transform, RiseRotation, SetRotation, t);
}

void UpdateMoon()
{
    float t;

    if (time >= SunRise)
        t = time - SunRise;
    else
        t = time + (24 - SunSet);

    t = t / ((24 - SunSet) + SunRise));
    UpdateRotation(_lMoonLight.transform, RiseRotation, SetRotation, t);
}

void GoToHour(int hour)
{
     t = hour / 24;
}

void ToggleState(bool isSun)
{
    if (isSun)
    {
        // Custom logic here, turn on sun, turn off moon?
    }
    else
    {
        // Custom logic here, turn on moon, turn off sun?
    }

    wasSun = isSun;
}

static void SetRotation(Transform target, Quaternion start, Quaternion end, float t)
{
    target.rotation = Quaternion.Lerp(start, end, t);
}

完全未经测试的代码,但这是关于如何使用类来更新太阳或月亮的基本想法。这是一个二进制实现,所以要么显示太阳,要么显示月亮。如果你想有重叠,你当然可以轻松地自定义它。您将希望月亮升起和月亮落下的时间不同,如果/否则月亮和太阳更新则不需要。

代码也很基本,并且做了很多假设(日出永远不会超过 24 小时标记。)所有这些都可以清理。您还可以根据自己的判断创建变量来存储每帧完成的计算(关于日出/设置的持续时间)。如果你想这样做,只需在 GoToHour 方法中设置它。

SetRotation 不是非常必要的,但它有助于保持代码干净。如果您以后决定要使用 Quaternion.Slerp,您只需在一处进行更改。

我在检查器中公开了四元数,您可能宁愿只使用浮点数来表示度数,或者使用 Euler Vecter3。如果您这样做,您可以在开始时进行转换。

我希望这回答了你所有的问题,如果没有,请随时澄清没有回答的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-30
    • 1970-01-01
    相关资源
    最近更新 更多