VicL993 2022-01-07 01:49 采纳率: 100%
浏览 671
已结题

如何在Unity中动态添加按钮后,出现报错”An item with the same key has already been added."(语言-c#)

我正在做一个简单的文字冒险游戏的prototype。基本的机制是,玩家可以通过选择按钮在房间之间行动。每个房间都可以通往若干其他房间。
根据当前房间可通往的其他房间的数量,生成这个数量的按钮。
点击按钮时,进入这个房间,再次根据当前房间可通往的其他房间的数量,生成这个数量的按钮。
如此往复。
另外,房间是作为scriptable object的方式存在的。
我遇到的问题是:我使用的delegate方法之后,游戏可以正确地根据当前房间出口数量生成按钮。但是我不知道具体该怎么写Button.OnClick()。
我的思路是在生成按钮时,把按钮的名字改成对应的房间出口的名字。然后创建一个Dictionary来“翻译”房间和对应的名字(字符串)。最后在Button.OnClick()里用Dictionary.ContainsKey(string)来根据按钮的名字来找到Dictionary中对应的名字,再找到对应的房间。然后把当前房间设置为这个目标房间。
但是,这会导致报错:“ArgumentException: An item with the same key has already been added.”
我大概明白问题的原因是因为Dictionary出现了问题,但是我怎么看也找不到哪里出现了重复的key。
此外,除了我这个思路以外,是不是有更好的办法?求指教!

这是我用来生成按钮的代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;

public class ButtonManager : MonoBehaviour
{

    List<string> buttonNameList = new List<string>();


    GameObject newButtonHolder;
    bool isOpen = false;
    
    public PositionNavigation positionNavigation;
    public GameObject buttonPrefab;
    public GameObject buttonHolderPrefab;
    public GameObject buttonManager;

    public GameController controller;


    void Awake()
    {
        positionNavigation = positionNavigation.GetComponent<PositionNavigation>();

    }


    void Update()
    {
        CheckInput();
    }

    void CheckInput()
    {
        if (isOpen == false)
        {
            if (Input.GetKeyDown(KeyCode.F))
            {
                InitOptions();
                isOpen = true;
            }
        }
        else
        {
            if (Input.GetKeyDown(KeyCode.F))
            {
                Destroy(newButtonHolder);
                isOpen = false;
            }
        }
    }

    void InitOptions()
    {

        InitButtonHolder();

        for (int i = 0; i < positionNavigation.currentPosition.exits.Length; i++)
        {
            GameObject newButton = Instantiate(buttonPrefab) as GameObject;
            newButton.transform.SetParent(newButtonHolder.transform);

            Text buttonText = newButton.GetComponentInChildren<Text>();
            buttonText.text = positionNavigation.currentPosition.exits[i].keyString;
            newButton.name = buttonText.text;
           
            buttonNameList.Add(positionNavigation.currentPosition.exits[i].keyString);

            foreach (string buttonName in buttonNameList)
            {
                Button buttonComponent = newButton.GetComponent<Button>();

                buttonComponent.onClick.AddListener(delegate ()
                {
                    this.OnClick(newButton);
                });
            }
        }
    }

    void InitButtonHolder()
    {
        newButtonHolder = Instantiate(buttonHolderPrefab) as GameObject;

        newButtonHolder.transform.SetParent(buttonManager.transform);
        newButtonHolder.name = "ButtonHolder";

    }

    public void OnClick(GameObject sender)
    {
        
        Destroy(newButtonHolder);

        positionNavigation.ChangePosition(sender.name);


        controller.DisplayPositionText();
        controller.DisplayLoggedText();


        Debug.Log(sender.name);

        isOpen = false;
    }
}

这是我用来存储和修改位置信息的代码:

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

public class PositionNavigation : MonoBehaviour
{
    public Position currentPosition;

    Dictionary<string, Position> exitDictionary = new Dictionary<string, Position>();

    GameController controller;

    void Awake()
    {
        controller = GetComponent<GameController>();
    }

    public void UnpackExitsInPosition()
    {
        for (int i = 0; i < currentPosition.exits.Length; i++)
        {
            exitDictionary.Add(currentPosition.exits[i].keyString, currentPosition.exits[i].targetPosition);
            controller.interactionDescriptionInPosition.Add(currentPosition.exits[i].exitDescription);
        }
    }

    public void ChangePosition(string targetPositionName)
    {
        if (exitDictionary.ContainsKey(targetPositionName))
        {
            currentPosition = exitDictionary[targetPositionName];
            controller.LogStringWithReturn("You chose this position: " + targetPositionName);
            controller.DisplayPositionText();
        }
        else
            controller.LogStringWithReturn("This is an invalid option!");
    }


    public void ClearExits()
    {
        exitDictionary.Clear();
    }
}

这是game controller的代码:

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

public class PositionNavigation : MonoBehaviour
{
    public Position currentPosition;

    Dictionary<string, Position> exitDictionary = new Dictionary<string, Position>();

    GameController controller;

    void Awake()
    {
        controller = GetComponent<GameController>();
    }

    public void UnpackExitsInPosition()
    {
        for (int i = 0; i < currentPosition.exits.Length; i++)
        {
            exitDictionary.Add(currentPosition.exits[i].keyString, currentPosition.exits[i].targetPosition);
            controller.interactionDescriptionInPosition.Add(currentPosition.exits[i].exitDescription);
        }
    }

    public void ChangePosition(string targetPositionName)
    {
        if (exitDictionary.ContainsKey(targetPositionName))
        {
            currentPosition = exitDictionary[targetPositionName];
            controller.LogStringWithReturn("You chose this position: " + targetPositionName);
            controller.DisplayPositionText();
        }
        else
            controller.LogStringWithReturn("This is an invalid option!");
    }


    public void ClearExits()
    {
        exitDictionary.Clear();
    }
}

这是ScriptableObject房间的信息:

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

[CreateAssetMenu(menuName = "Position/New Position")]
public class Position : ScriptableObject
{
    [TextArea]
    public string description;

    public string positionName;

    public Exit[] exits;
}

这是ScriptableObject房间出口的信息:

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

[System.Serializable]
public class Exit
{
    public string keyString;
    [TextArea]
    public string exitDescription;
    public Position targetPosition;
}

我测试之后,我觉得问题出在了脚本ButtonManager的这一部分:

    public void OnClick(GameObject sender)
    {
        
        Destroy(newButtonHolder);

        positionNavigation.ChangePosition(sender.name);


        controller.DisplayPositionText();
        controller.DisplayLoggedText();


        Debug.Log(sender.name);

        isOpen = false;
    }

当我把positionNavigation.ChangePosition(sender.name)注释掉之后,报错消失。
请帮帮忙,谢谢了!

  • 写回答

2条回答 默认 最新

  • 陈言必行 Unity领域优质创作者 2022-01-07 13:31
    关注

    应该是这两个位置添加键的时候重复添加,检查一下调用添加逻辑或者添加的时候做一个判断,不存在则添加,存在则更新值:

    Dictionary<string, string> dic = new Dictionary<string, string>();
    
    if (dic.ContainsKey("Key"))
    {
        dic.Add("Key", "Value");
    }
    else
    {
        dic["Key"] = "Value";
    }
    

    img

    img

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论 编辑记录
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 1月15日
  • 已采纳回答 1月7日
  • 修改了问题 1月7日
  • 修改了问题 1月7日
  • 展开全部

悬赏问题

  • ¥15 用ns3仿真出5G核心网网元
  • ¥15 matlab答疑 关于海上风电的爬坡事件检测
  • ¥88 python部署量化回测异常问题
  • ¥30 酬劳2w元求合作写文章
  • ¥15 在现有系统基础上增加功能
  • ¥15 远程桌面文档内容复制粘贴,格式会变化
  • ¥15 关于#java#的问题:找一份能快速看完mooc视频的代码
  • ¥15 这种微信登录授权 谁可以做啊
  • ¥15 请问我该如何添加自己的数据去运行蚁群算法代码
  • ¥20 用HslCommunication 连接欧姆龙 plc有时会连接失败。报异常为“未知错误”