我正在为我们正在制作的一款游戏的项目系统工作,该系统类似于古老的经典生化危机游戏。目前,我正在实现项目组合,您可以将不同的项目相互组合以获得新的东西。复杂性来自这样一个事实,即存在具有多个转换级别的项目,并且每个级别都有多个配对。请允许我澄清一下,让我们假设我们有绿色、红色和蓝色的药草。你不能结合 red+blue,但是你可以结合 G+B,你会得到类似GreenBlueHerb或 G+R 得到GreenRedHerb的东西,现在如果你将这些结果中的任何一个结合到蓝色药草中,你会得到一颗灰草. 正如你从这个例子中看到的,绿色药草有 2 级转化,要达到第一级,有两个可能的伴侣,(红色|蓝色),从那个点到第二级,只有一个伴侣(蓝色的)。
所以我想出了一个有趣的树,它涵盖了nLevels的所有可能性,而不仅仅是 2,级别越多树越复杂,看看这个 3 级别的例子,你在中间看到的三角形代表一个项目,它周围的其他颜色形状代表它可能达到下一个级别的伙伴:
有很多不同的组合,我可以先将我的项目与蓝色的组合,然后是红色,然后是绿色,或者绿色,然后是红色,然后是蓝色,等等。达到我的最终水平。我想出了代表所有可能组合的这棵树:
(右侧的数字是#levels,左侧是每个级别的#nodes)但是正如您所看到的,它是多余的。如果您查看末端节点,它们应该都是一个,因为它们都导致相同的最终结果,即 G+R+B。这种情况实际上总共有 7 种可能的状态,这里是正确的树:
这很有意义,请注意节点数量的巨大差异。
现在我的问题是,什么是正确的数据结构?- 我很确定没有内置的,所以我将不得不制作我自己的自定义一个,我实际上做了,并设法让它工作但有一个问题。(值得一提的是,我正在从 XML 文件中获取节点信息,信息是指达到节点/级别的itemRequired以及该节点上我的项目的名称,例如:对于绿色药草要达到RedGreenHerb状态,它“需要”一个RedHerb,当这种组合发生时,名称“ GreenHerb ”将更改为“ RedGreenHerb ”,如果您想知道RedHerb会发生什么,它就消失了,我不再需要它了),这是我的数据结构:
public struct TransData
{
public TransData(string transItemName, string itemRequired)
{
this.transItemName = transItemName;
this.itemRequired = itemRequired;
}
public string transItemName;
public string itemRequired;
}
public class TransNode
{
public List<TransNode> nodes = new List<TransNode>();
public TransData data;
public TransNode(TransNode node): this(node.data.transItemName, node.data.itemRequired) { }
public TransNode(string itemName, string itemRequired)
{
data = new TransData(itemName, itemRequired);
}
}
public class TransLevel
{
public List<TransNode> nodes = new List<TransNode>();
public TransNode NextNode { get { return nodes[cnt++ % nodes.Count]; } }
int cnt;
}
public class TransTree
{
public TransTree(string itemName)
{
this.itemName = itemName;
}
public string itemName;
public TransNode[] nodes;
public List <TransLevel> levels = new List<TransLevel>();
// other stuff...
}
让我解释一下:TransTree
实际上是基础节点,它在开始时具有项目的名称(例如 GreenHerb),树有许多级别(您在图片中看到的黑色线条),每个级别都有许多节点,每个节点都带有一个新的项目数据和一些要指向的节点(子节点)。现在您可能会问在类中放置节点列表有什么需要TransTree
?- 在我向您展示如何从我的 XML 文件中获取数据后,我会回答这个问题:
public TransTree GetTransItemData(string itemName)
{
var doc = new XmlDocument();
var tree = new TransTree(itemName);
doc.LoadXml(databasePath.text);
var itemNode = doc.DocumentElement.ChildNodes[GetIndex(itemName)];
int nLevels = itemNode.ChildNodes.Count;
for (int i = 0; i < nLevels; i++) {
var levelNode = itemNode.ChildNodes[i];
tree.levels.Add(new TransLevel());
int nPaths = levelNode.ChildNodes.Count;
for (int j = 0; j < nPaths; j++) {
var pathNode = levelNode.ChildNodes[j];
string newName = pathNode.SelectSingleNode("NewName").InnerText;
string itemRequired = pathNode.SelectSingleNode("ItemRequired").InnerText;
tree.levels[i].nodes.Add(new TransNode(newName, itemRequired));
}
}
tree.ConnectNodes(); // pretend these two
tree.RemoveLevels(); // lines don't exist for now
return tree;
}
这是一个 XML 示例,可以让一切变得清晰:ItemName->Level->Path(只不过是一个节点)->Path data
<IOUTransformableItemsDatabaseManager>
<GreenHerb>
<Level_0>
<Path_0>
<NewName>RedGreenHerb</NewName>
<ItemRequired>RedHerb</ItemRequired>
</Path_0>
<Path_1>
<NewName>BlueGreenHerb</NewName>
<ItemRequired>BlueHerb</ItemRequired>
</Path_1>
</Level_0>
<Level_1>
<Path_0>
<NewName>GreyHerb</NewName>
<ItemRequired>BlueHerb</ItemRequired>
</Path_0>
</Level_1>
</GreenHerb>
</IOUTransformableItemsDatabaseManager>
现在这样做的问题是,节点之间没有相互连接,那又如何,这意味着什么?好吧,如果一个项目通过某个路径到达某个级别,那么我们当然不需要继续存储它没有采用的其他路径,那么为什么要将它们保存在内存中呢?(没有变回,一旦你走了一条路,就是你有义务沿着那条路走,永远不要回头)我想要的,是当我取出一个节点时,它下面的所有其余节点也会下降,这是有道理的,但目前我目前的做法是这样的:
正如你所看到的,节点没有连接,它们是关卡!这意味着,我目前无法以取出所有子节点的方式取出节点。(在没有节点连接的情况下这样做非常困难,并且会大大降低性能)这让我们:
tree.ConnectNodes();
tree.RemoveLevels();
我先连接节点,然后删除关卡?为什么,因为如果我不这样做,那么每个节点都有两个对它的引用,一个来自其父节点,另一个来自当前级别。现在ConnectNode
实际上是针对我展示的长树,而不是针对具有 7 个状态的优化树:
// this is an overloaded version I use inside a for loop in ConnectNodes()
private void ConnectNodes(int level1, int level2)
{
int level1_nNodes = levels[level1].nodes.Count;
int level2_nNodes = levels[level2].nodes.Count;
// the result of the division gives us the number of nodes in level2,
// that should connect to each node in level1. 12/4 = 3, means that each
// node from level1 will connect to 3 nodes from level2;
int nNdsToAtch = level2_nNodes / level1_nNodes;
for (int i = 0, j = 0; i < level2_nNodes; j++)
{
var level1_nextNode = levels[level1].nodes[j];
for (int cnt = 0; cnt < nNdsToAtch; cnt++, i++)
{
var level2_nextNode = levels[level2].nodes[i];
level1_nextNode.nodes.Add(new TransNode(level2_nextNode));
}
}
}
这最终是我想在我的另一棵树上拥有的,但我不知道如何去做。我想连接节点并形成我展示的第二棵树,这比 4 级相对简单,我什至无法连接绘画中的节点!(当我尝试了 4 个级别时)
如果你盯着它看,你会发现它与二进制数有一些相似之处,这是我的二进制形式的树:
001
010
100
011
101
110
111
每个“1”代表一个实际项目,“0”表示空。从我的树中,'001'=Blue,'010'=Green,'100'=Red,'011' 表示 Green+Blue,...'111'=Grey(最终级别)
所以现在我得到了解释,首先:我的方法正确吗?如果不是,那是什么?如果是这样,那么我可以使用/制作的数据结构是什么?如果我想出的数据结构在他们的位置,我如何将数据从 XML 文件存储到我的数据结构中,以便将节点连接在一起,这样每当我取出一个节点时,它就会取出它的子节点用它?
非常感谢您的帮助和耐心:)
编辑:有趣的是,整个系统适用于在整个游戏中仅出现一次的物品(被拾取一次)。这就是为什么每当我选择一条路径时,我都会将它从内存中删除,而且每当我拿起一个项目时,我都会从数据库中删除它的条目,因为我不会再遇到它了。
编辑:请注意,我不仅通过字符串表示我的项目,它们还有很多其他属性。但在这种情况下,我只关心他们的名字,这就是我处理字符串的原因。