【问题标题】:C# - Change array values inside a functionC# - 更改函数内的数组值
【发布时间】:2017-10-22 13:38:11
【问题描述】:

我将 C# 脚本附加到场景中 NPC 的触发器。基本上我只是试着让我的 NPC 与玩家“交谈”。如果玩家在 NPC 的触发器内并按下“E”键,那么 NPC 应该说出第一条未告知的消息,定义在我的数组 messages

消息在 3 秒后消失,然后用户可以再次激活 NPC,并且应该选择并返回数组中的下一条消息。


为了解决这个问题,我创建了一个 bool 数组 messageStatus,它保存了消息已被告知的状态。

所以messages的索引0对应messageStatus的索引0。

我编写了一个名为selectMessage 的函数,我在其中传递了两个数组,然后获取状态数组的第一个索引位置,其中值为false,然后返回带有该索引的消息。然后我将该索引上的状态设置为true,因为消息已被告知。

但是,初始化 (status[i] = true;) 似乎并未更改原始数组,而只是更改了参数。因为我的 NPC 总是告诉最后一条消息。

我认为我必须将数组作为指针传递,但我不确定这是如何工作的,我的尝试到目前为止都失败了。

我该如何解决这个问题?

    string selectMessage(string[] messages, bool[] status)
    {
        for (int i = 0; i <= status.Length-1; i++) {

            if (status[i] == false) {
                status[i] = true;    //<-- problem
                return messages[i];
            }
        }

        return "Is everything ok?";
    }

完整脚本:

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

public class TalkMalon : MonoBehaviour {

    public float displayTime = 3;
    public bool showMessage = false;

    public string[] messages = {
        "Hello, my name is Heinrich.",
        "I have nothing more to say."
    };

    public bool[] messageStatus = {
        false,
        false
    };

    void Update()
    {
        if (showMessage) {
            displayTime -= Time.deltaTime;
            if (displayTime <= 0.0) {
                showMessage = false;
                displayTime = 3;
            }
        }
    }

    void OnTriggerStay()
    {

        if (Input.GetKeyDown(KeyCode.E)) {
            showMessage = true;
        }
    }

    void OnGUI()
    {
        if (showMessage) {  
            GUI.Label(new Rect(Screen.width / 2, Screen.height / 2, 200f+100f, 200f), selectMessage(messages, messageStatus));
        }
    }

    string selectMessage(string[] messages, bool[] status)
    {
        for (int i = 0; i <= status.Length-1; i++) {

            if (status[i] == false) {
                status[i] = true;
                return messages[i];
            }
        }

        return "Is everything ok?";
    }
}

【问题讨论】:

  • 使用C#的ref参数支持。
  • 与您的问题略微无关,但为什么不创建一个小类,其中包含描述是否被告知的消息和布尔值。然后你有这些而不是 2 个集合?
  • 使用上面 Dave 的建议或使用KeyValuePair
  • 这里到底有什么问题?
  • 谢谢你的提示,我真的很感激他们 :) 问题是我的 NPC 总是告诉最后一条消息

标签: c# arrays pointers unity3d unity5


【解决方案1】:

我真的不认为需要两个数组。在您展示的逻辑中,您使用消息数组按顺序将它们添加到集合中。所以你可以只使用一个索引:

int index = 0;
string[] messages;
string GetNextMessage()
{
    return (++index < messages.Length) ? messages[index] : "Is everything ok?"; 
}

这实际上更好,因为您不必迭代集合来查找下一条消息。在算法中,您的解决方案是 O(n),这意味着最坏的情况是集合的大小。我的解决方案是 O(1),这意味着最坏的情况是一个动作。

【讨论】:

    【解决方案2】:

    //第一个可能的修复

    string selectMessage(string[] messages,ref bool[] status)
    {
        for (int i = 0; i <= status.Length-1; i++) {
    
            if (status[i] == false) {
                status[i] = true;
                return messages[i];
            }
        }
    
        return "Is everything ok?";
    }
    

    另一个更可取的选择是将 messageStatus 数组设为静态。

    更好的选择是实现ObservableCollection

    但这对你的目的来说有点开销......

    //第二个可能的修复

    public static bool[] messageStatus = {
        false,
        false
    };
    

    【讨论】:

    • 这里为什么需要 ref?他影响的是数组的内容而不是数组引用。为什么推荐静态?也不需要。
    【解决方案3】:

    我发现问题出在其他地方,甚至没有必要使用ref 或指针将数组作为引用传递。但我很高兴你们推荐它并感谢您提供的非常有用的提示!

    这是一个逻辑错误。我认为最后一条消息总是显示,因为 OnGUI 是每帧执行一次(afaik)?

    我根据@Everts 的建议更改了逻辑,这显然要好得多。我删除了根本不需要的状态数组。

    固定脚本:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class TalkMalon : MonoBehaviour
    {
        private int index = 0;
        private string currentMessage = "";
        private float displayTime = 3;
        private bool showMessage = false;
        private bool selectNextMessage = false;
    
        public string[] messages = {
            "Hello, my name is Heinrich.",
            "I have nothing more to say."
        };
    
        void Update()
        {
            if (showMessage) {
                displayTime -= Time.deltaTime;
                if (displayTime <= 0.0) {
                    showMessage = false;
                    displayTime = 3;
    
                    Debug.Log(messages.Length);
    
                    if (index != messages.Length - 1) {
                        index++;
                    }
                }
            }
        }
    
        void OnTriggerStay()
        {
            if (Input.GetKeyDown(KeyCode.E)) {
                showMessage = true;
            }
        }
    
        void OnGUI()
        {
            if (showMessage) {
                GUI.Label(new Rect(Screen.width / 2, Screen.height / 2, 200f, 200f), messages[index]);
            }
        }
    }
    

    【讨论】:

    • 您将遇到一些超出范围的异常。在 OnTriggerStay 中,showMessage = (index
    • 我不确定您指的是什么?在 OnTriggerStay 中没有像 showMessage = (index &lt; messages.Length); 这样的代码。我测试了代码,没有任何期望。
    • 如果你一直去NPC,按E,因为索引永远不会设置回0,它会一直增加。您需要防止索引高于消息。长度。如果索引太大,我给出的行会阻止你运行代码。
    • @Everts,我已经用 if (index != messages.Length - 1) { 行阻止它
    猜你喜欢
    • 2020-10-20
    • 1970-01-01
    • 2016-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-14
    • 2017-03-27
    相关资源
    最近更新 更多