5

当我在开发我的小游戏时,我取得了很大的进步,但对很多事情感到沮丧。最新的事情是creating a list of required items,为了让您了解,我将为您提供我创建Explanation的以及Code我创建的,但显然不起作用......

一 - 解释

为了让玩家建造一座建筑物,他必须进行一些必要的研究,每次研究都需要更多的研究才能被研究……就像一棵研究树,玩家将通过探索游戏和做一些任务...

因此,为了更准确地想象它,您可以在这里查看我的小代码

II - 代码

    //Available Main Elements
    var carbon = new Element {Name = "Carbon"};
    var hydrogen = new Element {Name = "Hydrogen"};
    var oxygen = new Element {Name = "Oxygen"};
    var nitrogen = new Element {Name = "Nitrogen"};

        //Example Research
var steam = new Research(name : "Steam", requiredElements: null, requiredResearches: /*Fire*/ & /*Water*/ & /*Iron*/);

所以从最后的代码片段[这只是为了解释]玩家想要研究Steam例如需要3个研究才能被研究......其中一个the Iron还需要1个研究才能被研究等等[也许更少,也许更多,或者根本没有要求] ...

得出的结论是问题是:我如何创建这样的嵌套,以便当玩家尝试进行研究时,系统会快速查看他所做的研究和他想要做的研究[包括嵌套的研究],如果玩家做了不满足要求它只是返回一棵树,上面有他想要实现的东西

毕竟,我只是想提前感谢你,我正在等待你非常宝贵的支持......

4

7 回答 7

5

我会从研究对象本身中删除需求逻辑。为简单起见说是这样的:

public class Research
{
    public string Name { get; set; }
}

Dictonary然后,我会在每个Research包含一个桶的地方保留一个需求列表Researches

Dictionary<Research, List<Research>> requiredResearches = 
    new Dictionary<Research, List<Research>>();

// the list of Researches the player has completed
List<Research> playersResearched = new List<Research>;

例如,“蒸汽”将包含“火”、“水”和“铁”。也许“铁”包含“锡”。

接下来,给定一项研究,我们可以查看它的所有要求,包括要求的要求:

// e.g. research is "Steam" and returns "Fire", "Water", "Iron", "Tin"
var chainOfRequirements = GetReq(requiredResearches, research);

这会调用这样的递归函数:

public IList<Research> GetReq(Dictionary<Research, List<Research>> reqs, 
                              Research target)
{
    var chain = new List<Research>();
    if(reqs.ContainsKey(target))
    {
        foreach(var item in reqs[target])
        {
            chain.Add(item);
            chain.AddRange(GetReq(reqs, item));
        }
    }
    return chain;
}

您返回的是一个平面的需求列表(包括需求的需求)。那时,对球员名单的一个小查询Researches可以返回给你哪些是丢失的:

var missing = chainOfRequirements.Where (c => 
                  playerResearches.Where (r => r == c).Any () == false).Distinct();

启用使用“名称”比较字典

public sealed class NameEqualityComparer : IEqualityComparer<Research>
{
    public bool Equals(Research x, Research y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (ReferenceEquals(x, null)) return false;
        if (ReferenceEquals(y, null)) return false;
        if (x.GetType() != y.GetType()) return false;
        return string.Equals(x.Name, y.Name);
    }

    public int GetHashCode(Research obj)
    {
        return (obj.Name != null ? obj.Name.GetHashCode() : 0);
    }
}

这是概念证明。

于 2013-07-12T20:45:24.370 回答
1

我想不出任何可以阻止这种情况发生的具体事情,但我可以理解为什么你会犹豫直接跳入编码而不考虑这些案例。为了尝试给出一些指示,这里有一些我想你最终拥有的功能:

// Having a function to retrieve a unique variable by its name (backed by a Dictionary<string, Research>)
// is often handy for me, especially if you decide to script the requirements tree in a text file.
// If you have enough references passed around that you can do this without a static method, all
// the better.
Research.getResearchByName(string name)

// A recursive function. If this research has not been completed, it adds itself to the set, as well
// as any prerequisites (by calling this function on its prerequisites). The top-level, public
// version of this function would create the set, and then return it after performing this check. If
// the Set is empty, then the player can start the research.
Research.addUnresearched_Internal(Set<Research> unresearched)

我认为我的方法的主要问题是我只想使用 Set,而不是 Tree;但有了一些变化,你可能会比我做得更好。

于 2013-07-12T20:16:05.083 回答
1

听起来你想做这样的事情:

public class Research
{
    private Collection<Research> prerequisites 
    public Collection<Research> Prerequisites 
    {
        get { return this.prerequisistes; }
    }

    public bool IsComplete { get; set; }

    public IEnumerable<Research> RequiredResearch()
    {
        if (this.IsComplete)
        {
            return new Research[0];
        } 
        else 
        {
            return new[] { this }.Concat(
                this.Prerequisites.SelectMany(r => r.RequiredResearch()));
        }
    }
}

如果你正在制作多人游戏,它可能必须是这样的:

public class Player
{
    private Collection<Research> completedResearch
    public Collection<Research> CompletedResearch
    {
        get { return this.completedResearch; }
    }
}

public class Research
{
    private Collection<Research> prerequisites 
    public Collection<Research> Prerequisites 
    {
        get { return this.prerequisistes; }
    }

    public IEnumerable<Research> RequiredResearch(Player player)
    {
        if (player.CompletedResearch.Contains(this))
        {
            return new Research[0];
        } 
        else 
        {
            return new[] { this }.Concat(
                this.Prerequisites.SelectMany(r => r.RequiredResearch(player)));
        }
    }
}

如果尚未完成,这两种方法都将返回当前研究(而不仅仅是它的先决条件)。不过,您可以简单地.Skip(1)对结果执行 a 以忽略当前结果。

于 2013-07-12T20:16:40.187 回答
1

一种可能性是添加bool completedElementResearch类。然后,您可以拥有一个检查completed == false其任何子研究的功能。

bool CanCompleteResearch(Research r)
{
    for (Research sub in r.requiredResearches)
    {
        // No need to recursively check 'sub'; in order for 'sub' to be marked as
        // completed, its sub-researches must've been already completed.            
        if (!sub.completed)
            return false;
    }
    return true;
}

我敢肯定,您可以使用 LINQ 完成一些单行技巧。

编辑:这仅在研究树没有同时在多个地方使用时才有效(即,如果您在多个玩家实例之间共享研究对象)。

编辑 2:为了找出所有缺失的研究,您可以使用一个简单的递归函数:

void ListMissingResearch(Research r)
{
    for (Research sub in r.requiredResearches)
    {
        if (!sub.completed)
        {
            // print message saying "Research <r> requires research <sub>"
            ListMissingResearch(sub); // list any missing sub-sub-research
        }
    }
}
于 2013-07-12T20:09:27.820 回答
1

我建议你创建自己的requirementsTree类,它需要是一个至少包含以下成员的链表:

  • 一个requirementsTree[] prerequisiteResearch属性,包含该特定项目所需的所有研究。
  • 一个bool isResearched属性,表明玩家是否已经研究过该物品。
  • 一个方法,比如说,bool isEligibleToResearch()循环遍历所有项目prerequisiteResearch以检查玩家是否已经研究过它们。

有了这个,我认为您将拥有一个结构良好且可扩展的需求类。

于 2013-07-12T20:09:35.923 回答
1

我不太了解C#提供代码,但你可以让每个Research都有一个Arraytype Research,这将保持Research必须做的事情才能制作实际的Research,然后你只需要迭代Array检查它们是否都是完全的。

您甚至不需要检查需求的需求,就好像它们已经完成一样,它们已经完成了所有需求。

如果Research没有任何要求,你就让Array空。

于 2013-07-12T20:07:39.310 回答
1

你可以在你的研究类中使用这样的函数,它只接收一个可枚举的Researchs 列表,用于检查玩家是否具有先决条件。这假设在类中Research调用了一个本地列表或其他一些可枚举的 es 。PrereqsResearch

public bool CanHas(IEnumerable<Research> researches)
{
   return Prereqs.All((pr) => researches.Contains(pr) && pr.CanHas(researches));
}

该函数只是递归检查每个 Prereq 以查看它是否在传入的列表中,然后检查 prereqs prereqs。All是一种 linq 扩展方法,如果可枚举中的所有元素都满足条件,则仅返回 true。如果没有元素,它会返回 true(所有零个元素都符合条件),因此当您进入Research没有 Prereqs 的 a 时,这将终止。

注意:这会进行深度优先搜索,并且对于同一研究是多个先决条件的子研究并不明智。

于 2013-07-12T20:36:38.623 回答