事件功能未按预期工作

Event function not working as expected

首先我会解释发生了什么,然后是我期望发生的事情,最后是它背后的代码

所以当我按下回车键时,文本的颜色是绿色的

我希望发生的是颜色变红

这是基于我是否在字段中输入 "Bad"

//Please note I have edited uni9mportant code out

//Event Listener
inputField.onEndEdit.AddListener (delegate {
            VerifyWords();
});

//Clss that handles the dictionary
public abstract class WordDictionary: MonoBehaviour{
    public static Dictionary<string,bool> _wordDictionary = new Dictionary<string,bool> ();

    private void Start(){
        _wordDictionary.Add ("Bad",true);
    }
}

//Function that handles the word verification
private void VerifyWords(){
        if (openChat == false) { //If we done have open chat
            bool hasBadWords = false; //Reset boolean
            string[] stringSplit = inputField.text.Split (' '); //Split text string

            for (int i = 0; i < stringSplit.Length; i++) { // Go through each word in the string array
                if (WordDictionary._wordDictionary.ContainsKey (stringSplit[i])) { //If the word is in the dictionary
                    hasBadWords = true; //Then there is a bad word
                }
            }

            if (hasBadWords == true) { //If a bad word was found
                inputField.textComponent.color = Color.red; //Then the text should be red
            } else {
                inputField.textComponent.color = Color.green; //The text should be green
            }
        }
    }

编辑 我编辑了带有注释的代码,以便让我的想法更直观

问题是 class 被标记为 abstract。无法实例化抽象 class,因此 Unity 无法在无法实例化的 class 上调用 Start。最简单的解决方法是简单地从 class 定义中删除 abstract

public class WordDictionary: MonoBehaviour{
    public static Dictionary<string,bool> _wordDictionary = new Dictionary<string,bool> ();

    private void Start(){
        _wordDictionary.Add ("Bad",true);
    }
}

但是你现在有一个新问题。 WordDictionary 有一个由 non-static 方法初始化的静态成员。这意味着每次创建新的 WordDictionary 时,都会调用 Start() 并将所有单词添加到词典中 again (或者至少会尝试,在这种情况下你会得到一个重复的键异常,以避免你也可以写 _wordDictionary["Bad"] = true 替换现有的键(如果存在)。

这里更好的选择是使用静态构造函数。这将确保字典只被初始化一次:

public class WordDictionary: MonoBehaviour{
    public static Dictionary<string,bool> _wordDictionary = new Dictionary<string,bool> ();

    static WordDictionary() {
        _wordDictionary.Add ("Bad",true);
    }

    private void Start(){
    }
}

现在您可以使用 WordDictionary 而不必担心每次实例化 class 时字典都会增长。但是在这一点上,将 WordDictionary 变成 MonoBehavior 确实没有用,因为它实际上只是一堆单词的持有者。所以你的 class 现在变成了:

public class WordDictionary: {
    private static Dictionary<string,bool> _wordDictionary = new Dictionary<string,bool> ();

    public static Dictionary<string, bool> Words {
        get { return _wordDictionary; }
    }

    static WordDictionary() {
        _wordDictionary.Add ("Bad",true);
    }
}

我在这里添加了一个 属性 因为你真的应该使用属性,并且具有下划线名称(在我的代码世界中)意味着它是一个私有字段。你可以扩展你的字典来做其他事情:

public class WordDictionary: {
    private static List<string> _wordList = new List<string> ();

    static WordDictionary() {
        _wordList.Add ("Bad");
    }

    public static Contains(string word) {
        return _wordList.Contains(word);
    }

    public static ContainsAny(IEnumerable<string> words) {
        return words.Any(w => Contains(w));
    }
}

我看不出有什么理由在这里使用 Dictionary,如果它包含这个词那么它就是 "bad",如果它不包含这个词那么它就是 "good"。因此,更改为列表会使事情变得更简单。如果你隐藏 "Dictionary" 在后台的工作方式,只暴露 "contains" 和 "contains any" 方法,你有两个好处,使用变得更简单,你可以改变底层的 "engine" 不改变接口和下游代码。

现在你的着色函数变得简单多了:

private void VerifyWords() {
    if (openChat)
        return;

    var stringSplit = inputField.text.Split(' ');

    if (WordDictionary.ContainsAny(stringSplit))                
        inputField.textComponent.color = Color.red;
    else
        inputField.textComponent.color = Color.green;
}

首先,我建议您不要将 MonoBehaviour 用于您的 WordDictionary。我认为没有理由这样做。 Insted 使用 ScriptableObject.

其次,它不需要是抽象的,我认为你根本不需要字典,假设你只存储 'bad' 个单词,列表就可以了。

例如(我在这里使用了词典,假设您的要求只适用于存储的坏词,可以列出)

[CreateAssetMenu(menuName = "Custom Object/Word Dictionary")]
public class WordDictionary : ScriptableObject
{
    public Dictionary<string, bool> Values = new Dictionary<string, bool>();
}

可编写脚本的对象是在您的资产中创建的,并且可以被场景中的游戏对象引用。所以在需要使用dicitonary的gameObjects中只需添加一个

public WordDictionary gameDictionary;

并将其设置为您在资产中创建的词典。通常 singletons/static classes 和使 monoBehaviour 像静态对象一样行为的类似方法会导致问题。

可编写脚本的对象是一个很好的解决方法。它们在游戏开始时可用,无需像单例一样进行初始化,它们可以像任何其他 class 一样包含数据或函数,但与 MonoBehaviour 不同,它们没有启动、更新等......并不意味着你可以调用但是,您根据游戏中的某些特定初始化行为在 Scriptable 对象中编写的 AtStart()。

1 重要说明,在编辑器中,可编写脚本的对象中更新的数据会持续存在,但在运行时不会。例如在编辑器中测试你的游戏时,'bad' 单词将在会话之间保留......在构建中它不会但我假设你正在准备你的游戏初始化字典所以这应该是 non-issue .

也许这会对你有所帮助

//Please note I have edited uni9mportant code out

//Event Listener
inputField.onEndEdit.AddListener (delegate {
            VerifyWords();
});

//Clss that handles the dictionary
public abstract class WordDictionary: MonoBehaviour{
    public static Dictionary<string,bool> _wordDictionary = new Dictionary<string,bool> ();

    private void Start(){
        _wordDictionary.Add ("Bad",true);
    }
}

//Function that handles the word verification
private void VerifyWords(){
        if (openChat == false) { //If we done have open chat
            bool hasBadWords = false; //Reset boolean
            string[] stringSplit = inputField.text.Split (' '); //Split text string
            int i=0;
            for (i = 0; i < stringSplit.Length; i++) { // Go through each word in the string array
                if (WordDictionary._wordDictionary.ContainsKey (stringSplit[i])) { //If the word is in the dictionary
                     inputField.textComponent.color = Color.red; //Then the text should be red
                }
            }

            if (i == stringSplit.Length) { //If a bad word was found
             inputField.textComponent.color = Color.green; //The text should be green  
            }
        }
    }