我正在做一个简单的文字冒险游戏的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)注释掉之后,报错消失。
请帮帮忙,谢谢了!