首页 > 解决方案 > Unity:附加到 DontDestroyOnLoad 对象的子对象的脚本被调用两次

问题描述

我正在尝试根据一些触发器和一些数据填充 Unity 中的各种对象。我使用的数据看起来像这样Key === O2 Values are ==== { collected, collected, collected, absent}Key === O3 Values are ==== { collected, collected, present }存储在 Dictionary 对象Dictionary<string, List<string>> textMap中。以下脚本附加到这些对象:

using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class SensePlayerProximity : MonoBehaviour {

    bool disableEntry = false;
    bool disableExit = false;
    public bool isCollected = false;
    //List<Collider2D> triggerList = new List<Collider2D>();
    // Use this for initialization
    static Dictionary<string, List<string>> textMap = new Dictionary<string, List<string>>
    {
        { "O2", new List<string>() { "present", "absent", "absent", "absent" }},
        { "O3",new List<string>() { "absent", "absent", "absent" }}
    };

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (disableEntry || isCollected)
            return;
        StartCoroutine(disableTriggersForThisCollectible(10));

        List<String> values;
        foreach (KeyValuePair<string, List<string>> kvp in textMap)
        {
            values = kvp.Value;
            int foundAtIndex = values.IndexOf("absent");
            if (foundAtIndex > -1)
            {
                gameObject.GetComponent<TextMeshProUGUI>().text = kvp.Key;
                values[foundAtIndex] = "present";
                textMap.Remove(kvp.Key);
                textMap.Add(kvp.Key, values);
                logTextMap();
                return;
            }
        }
        // if nothing is found, then default the text to empty, since nothing left to be collected now
        gameObject.GetComponent<TextMeshProUGUI>().text = "";
    }

    //called when something exits the trigger
    private void OnTriggerExit2D(Collider2D collision)
    {
        if (disableExit || isCollected)
            return;
        //Debug.Log("Player is leaving me.. :(");
        string key = gameObject.GetComponent<TextMeshProUGUI>().text;
        StartCoroutine(disableTriggersForThisCollectible(0.1f));
        List<string> values = textMap[key];
        int index = -1;
        if (values != null)
            index = values.IndexOf("present");
        if(index >= 0)
        {
            values[index] = "absent";
            textMap.Remove(key);
            textMap.Add(key, values);
            gameObject.GetComponent<TextMeshProUGUI>().text = "";
            logTextMap();
        }
    }

    IEnumerator disableEntryTrigger(float t)
    {
        disableEntry = true;
        // disable the trigger collider for t seconds
        yield return new WaitForSeconds(t);
        disableEntry = false;
    }

    IEnumerator disableExitTrigger(float t)
    {
        disableExit = true;
        // disable the trigger collider for t seconds
        yield return new WaitForSeconds(t);
        disableExit = false;
    }

    void logTextMap()
    {
        string debugString = "";
        foreach (KeyValuePair<string, List<string>> kvp in textMap)
        {
            debugString += "Key === " + kvp.Key + " Values are ==== { " + String.Join(", ", kvp.Value.ToArray()) + " }";
        }
        Debug.Log(debugString);
    }
}

该脚本检测到与附加到我的播放器的 BoxCollider2D 发生的触发碰撞,并且附加了“传感器”标签名称。每当事件发生时我禁用触发器 10 秒,每当OnTriggerEnter2D事件发生时禁用触发器 0.1 秒OnTriggerExit2D

我有一些固定的文本对象分散在我的关卡中,并试图根据上面的这个脚本填充其中的文本。此脚本附加到每个此类文本对象。在这些事件的帮助下,我检测玩家是否在文本对象附近。如果找到了玩家,那么将填充一个随机密钥,textMap前提是该密钥具有至少一个“不存在”的值。每个键都有一个值列表,可以是“不存在”、“存在”或“已收集”。“缺席”意味着该键不在相机视图中,因此可以分配给新的可收藏文本对象。“present”表示该键存在于当前相机视图中,并且不可用于其他文本对象。“集” 表示密钥已被收集,也不可用。在示例值中textMap例如,我在上面显示的地图中可以有 4 个键“O2”的副本,以及 3 个键“O3”的副本。其中,已经收集了 3 个“O2”和 2 个“O3”。只能将 1 个“O2”副本分配给新触发的文本对象,并且没有“O3”副本可供它们使用。该脚本主要按预期工作,但有几次我无法调试。调试日志显示视图中已经存在“O3”的一份副本,但我去了我的场景,在任何地方都找不到“O3”。我担心这可能会发生,因为所有触发的文本对象(上面的脚本所附加的)都试图修改textMap同时。我浪费了很多时间试图弄清楚这一点,但我只是把头撞在墙上。如果有人能指出我正确的方向,我将不胜感激。我的带有这些对象的场景如下所示: 在此处输入图像描述

编辑:我发现问题是DontDestroyOnLoad. SensePlayerProximity附加脚本的上述游戏对象都是DontDestroyOnLoad名为的游戏对象的子对象ScenePersist。只有当我重新加载场景时,玩家死亡时才会出现问题。当场景重新加载时,newScenePersist被加载到场景中,并且在它被销毁之前调用 children 的 triggerentry 方法。正因为如此,OnTriggerEnter2D被调用了两次,而不是一次。我该如何解决这个问题?解决此问题的一种方法是将所有这些对象远离玩家生成点,这样触发器就不会发生,但这不是解决问题的好方法。另一种是在启动方法中运行协程enableTriggers,即默认禁用触发器,但这也不是一个好的解决方案。

void Start () {
    disableEntry=true;
    disableExit = true;
    StartCoroutine(enableTriggers());
}
IEnumerator enableTriggers()
{
    yield return new WaitForSeconds(0);
    disableEntry = false;
    disableExit = false;
}

这就是我的场景层次结构的样子:

在此处输入图像描述

这里ScenePersist设置为,并且它有许多附加DontDestroyOnLoad的子对象(突出显示为收藏品) 。SensePlayerProximity

标签: c#unity3d

解决方案


问题是因为您禁用了触发器,Enter 和 Exit 方法没有成对调用。但是没有代码可以保护它。

输入 -> 存在 +1,不存在 -1
退出 -> 在场 -1,不在场 +1

如果方法 Enter 被调用两次,但方法 Exit 被跳过,现在字典中的 2 个元素出现,但场景中只有一个对象(带有文本 O2/O3)。

输入 -> 存在 +1,不存在 -1
离开没有触发退出
等待 10 秒
输入 -> 存在 +1,不存在 -1
退出 -> 在场 -1,不在场 +1

推荐阅读