如何使 C# 脚本按特定顺序执行?

How can I make a C# script execute in a certain order?

我有一个脚本冲突,检查是否有网络连接和游戏版本(如果是最新的);但它会跳过这些条件并直接加载游戏。

如何使脚本遵循这种模式?:

  1. 检查是否有互联网连接
  2. 查看游戏版本
  3. 仅在有互联网连接且游戏版本为最新时才加载游戏

你会注意到有一些加载函数会重复自己;我不确定要申请哪些。

CheckNetwork.cs

public Text AlertText;
public GameObject alert;
public string URL = "";
public string CurrentVersion;
string latestVersion;
public GameObject newVersionAvailable;

private void Start()
{
    StartCoroutine(LoadTxtData(URL));
}

void Awake()
{
    if (Application.internetReachability == NetworkReachability.NotReachable)
    {
        alert.SetActive(true);
        AlertText.text = "Sorry, you have to enable your conection to play this game";
        if (Input.GetKeyDown(KeyCode.Escape))
            Application.Quit();
    }
    else
    {
        alert.SetActive(false);
        CheckVersion();
    }
}

public void CheckVersion()
{
    if(CurrentVersion != latestVersion)
    {
        newVersionAvailable.SetActive(true);
    }
    else
    {
        newVersionAvailable.SetActive(false);
    }
}

private IEnumerator LoadTxtData(string url)
{
    UnityWebRequest www = UnityWebRequest.Get(url);
    yield return www.SendWebRequest();
    latestVersion = www.ToString();
    CheckVersion();
}

public void OpenURL(string url)
{
    Application.OpenURL(url);
}

Loading.cs

private Image progressBar;

private void Start()
{
    Invoke("LoadAsyncOperation", 3f);
    StartCoroutine(LoadAsyncOperation());
}

public IEnumerator LoadAsyncOperation()
{
    AsyncOperation gameLevel = SceneManager.LoadSceneAsync(1);
    while (gameLevel.progress < 1)
    {
        progressBar.fillAmount = gameLevel.progress;
        yield return new WaitForEndOfFrame();
    }
}

Loading2.cs

public GameObject loadingScreenObj;
public Slider slider;

AsyncOperation async;

public void StartGame()
{
    SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}

public void LoadScreen(int LVL)
{
    //StartCoroutine(loadingScreen());
}

IEnumerator LoadingScreen(int lvl)
{
    loadingScreenObj.SetActive(true);
    async = SceneManager.LoadSceneAsync(lvl);
    async.allowSceneActivation = false;

    while (async.isDone == false)
    {
        slider.value = async.progress;
        if (async.progress == 0.9f)
        {
            slider.value = 1f;
            async.allowSceneActivation = true;
        }
        yield return null;
    }
}

SplashScrean.cs

void Start()
{
    Invoke("loadMenuLevel", 3f);
    Screen.sleepTimeout = SleepTimeout.NeverSleep;
}

void loadMenuLevel()
{
    SceneManager.LoadScene("gameplay");
}

void Update()
{
    if (Input.GetKey(KeyCode.Mouse0)) 
    {
        loadMenuLevel();
    }
    if (Input.GetKey(KeyCode.Escape))
    {
        Application.Quit();
    }
}

发生这种情况是因为您在 SplashScrean.cs 脚本的 Start() 方法中调用了 Invoke("loadMenuLevel", 3f); 方法。

Start()MonoBehaviour 附加到对象后很快被调用。表面上,您已将它附加到场景中的 GameObject。所有这些都被一次击中。

您还可以通过在同一脚本的 Update() 方法中单击鼠标左键来调用它:

if (Input.GetKey(KeyCode.Mouse0)) 
{
    loadMenuLevel();
}

我会在每个任务完成时将 bools 添加到每个设置为 true 的加载脚本。然后,在SplashScrean.cs中,将上述条件更改为如果三个都完成则自动加载关卡。或者,保持按键条件并添加 bools.

if (Application.internetReachability != NetworkReachability.NotReachable 
    && loadScriptComplete && isCurrentVersion && Input.GetKey(KeyCode.Mouse0)) 
{
    loadMenuLevel();
}

现在,如果您还需要 Loading MonoBehaviours 等待执行它们的加载逻辑,请考虑向它们添加 Update() 逻辑以检查您在 CheckNetwork.cs 中设置的标志.

//For example. Instead of this
private void Start()
{
    Invoke("LoadAsyncOperation", 3f);
    StartCoroutine(LoadAsyncOperation());
}

//Do this
private void Update()
{
   //Note that we only need to check if it is a CurrentVersion, as if that is true, then we definitely have internet access.
   if(CheckNetwork.IsCurrentVersion) {
      Invoke("LoadAsyncOperation", 3f);
      StartCoroutine(LoadAsyncOperation());
   }
}

Edits/Additional 笔记据我所知:

CheckNetwork.cselse 子句中引用了 CheckVersion();。这似乎毫无意义。 LoadTxtData()不可能在那个时间范围内设置CheckVersion()所依赖的逻辑。一种竞争条件,如果你愿意的话。另外,无论如何你都会处理 LoadTxtData() 的检查,所以我会在那里做。

我不能在这里给你一个完整的复制粘贴代码(主要是因为我还没有完全理解你所有的脚本是如何相互连接的)但我看到你已经在使用 Coroutines .

我会如前所述使用一个中央管理器来处理所有这些事情。

我在这里只给你一个通用的伪代码,希望你能从这里找到自己的方法

public class LoadManager
{
    public CheckNetwork check;
    public Loading loading;
    public Loading2 loading2;
    public SplashScrean splashScreen;

    // suprise: a not well documented fact 
    // you can simply make the Start method an IEnumerator (Coroutine)
    // so you can directly wait for anything you want
    private void IEnumerator Start()
    {
        // Next suprise: You can simply yield other IEnumerators
        // that makes them execute and at the same time waits until they are finished
        // so all methods that you want to wait for from here
        // should be IEnumerators

        // as said this is only an example 
        // you could directly within that single
        // Coroutine do your complete download and check routine together
        yield return check.CheckPermissions();
        if(!check.isReachable)
        {
            yield break;
        }

        // same for chekcing the version
        yield return check.CheckVersion();

        if(!check.isUpToDate)
        {
            // do something for update
            yield break;
        }

        // is up to date so start and wait for loading
        yield return loading.LoadAsyncOperation();

        // and so on
    }
}

还有一些内置的等待方式,例如WaitUntil, WaitForSeconds