2013-07-12 6 views
5

Как я развиваю свою маленькую игру, я многое сделал, но разочаровал многое. Последняя вещь была creating a list of required items и для вас, чтобы понять, что я предоставлю вам как Explanation, а также Code, который я создал, но, очевидно, не работает ...Создание дерева необходимых предметов

I - Объяснение

Для того игрока чтобы построить здание, он, должно быть, сделал некоторые необходимые исследования с каждым исследованием, требует больше исследований для его изучения ... Это похоже на дерево исследований, которое игрок проведет через них, исследуя игру и выполняя некоторые задачи ...

Чтобы представить его более точно, вы можете посмотреть на мой маленький код h прежде чем

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 также нуждается в еще одном исследовании, которое нужно исследовать и т. д. [возможно, может быть, больше или может быть никаких требований вообще) ...

Заключение, что вопрос такой: Как создать такую ​​вложенность, когда игрок пытается провести исследование, система быстро смотрит на исследование он сделал и исследование, которое он хочет сделать, [включая его вложенные], и если игрок не выполнил требования, он просто возвращает дерево, с чем он хочет достичь?

В конце концов, я просто хочу поблагодарить вас заранее, и я жду вашей очень ценной поддержки ...

+0

Would можно предположить, что если у игрока есть необходимое исследование «А», то у него также есть все исследования, от которых зависит «А», или вы хотите, чтобы все «суб-исследования» также выполнялись каждый раз? – Cemafor

+0

У вас есть список исследований? – gwin003

+1

Чтобы завершить исследование «А», не было ли у него всех его суб-исследований? –

ответ

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>; 

Например, «Пар» будет содержать «Огонь», «Вода» и «Железо». И, возможно, «Железо» содержит «Олово».

Далее, учитывая научный, мы могли бы посмотреть на все его requierements включая требования требования:

// 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(); 

ОБЕСПЕЧИТЬ СРАВНЕНИЕ СЛОВАРЬ С ИСПОЛЬЗОВАНИЕМ «NAME»

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); 
    } 
} 

Here is proof of concept.

+0

Извините, но что, если я хотел добавить необходимое условие, я имею в виду, что, если «Tin» тоже нужно что-то другое. Потому что, как я сказал, «Исследование может иметь 1 предварительное условие, ни одно, ни даже до 10 предварительных условий, в которых каждый из них нуждается в одном и том же ... –

+0

@ Эрик, да, это полностью сработает. Хотя, вероятно, вы захотите настроить метод GetReq, чтобы он не добавлял никаких дубликатов (или не удалял их до вашего последнего теста). Я включил ссылку на какой-то код. Проверьте это и играйте с ним. –

+0

пока ваш ответ наиболее ясен, но вы просто заставило меня задуматься, что делать, если есть дубликаты, и я хочу различать их: D. Извините меня за ту тупость, которую у меня есть в этой теме, но я надеюсь, что у вас есть некоторое терпение для меня: D –

1

Я не знаю C# достаточно хорошо, чтобы предоставить код, но вы могли бы сделать каждый Research есть a Array типа Research, который должен содержать Research, который должен быть выполнен, чтобы иметь возможность сделать фактическое Research, тогда вам нужно будет только выполнить итерацию Array, если все они будут заполнены.

Вам даже не нужно проверять требования требований, как если бы они были завершены, у них уже есть все требования.

Если Research не имеет каких-либо требований, вы только даете Array пустым.

+0

В моем я должен проверить требования требований, так как игрок может столкнуться с слишком высоким уровнем для своего уровня или что-то еще, поэтому я должен дать ему подробную информацию о том, что он должен сделать, чтобы иметь возможность завершить исследование. –

1

Один из возможных вариантов - добавить bool completed в классы и Research. Тогда у вас может быть функция, которая проверяет наличие 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 вы можете использовать один-линейный трюк.

EDIT: Это работает только в том случае, если дерево исследований не используется одновременно в нескольких местах (т. Е. Если у вас были объекты исследования, разделяемые несколькими экземплярами нескольких игроков).

EDIT 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 
     } 
    } 
} 
+0

В моем случае я должен проверить требования требований, так как игрок может столкнуться с слишком высоким уровнем для своего уровня или что-то еще, поэтому я должен дать ему подробную информацию о том, что он должен сделать, чтобы иметь возможность завершить что исследования ... Поэтому я должен проверить рекурсивные субресурсы каждого исследования –

+0

Да, вы могли бы (опять же, используя LINQ) захватить список всех незавершенных требуемых исследований и сделать все, что рекурсивно с эти исследовательские деревья. –

+0

Можете ли вы объяснить это с помощью кодов. Также я повторю то, что хочу, чтобы было ясно ...у каждого исследования есть предпосылки, и у каждого предпосылки есть дополнительные предпосылки и так далее, это может измениться в зависимости от ситуации ... Так, например, у вас может быть исследование без каких-либо предварительных условий, с другой стороны, у вас может быть исследование с 10 предварительными условиями. Итак, что заставило меня хотеть также получить список всех этих дополнительных предпосылок, так это то, что я хочу, чтобы пользователь знал, чего ему не хватает, чтобы закончить свою миссию, и я хочу использовать их в других методах ... –

1

Я предлагаю вам сделать свой собственный requirementsTree класс, который должны были бы быть связанный список, по меньшей мере, со следующими членами:

  • a requirementsTree[] prerequisiteResearch property, содержащий все исследования, необходимые для данного предмета.
  • a bool isResearched property, что указывает, действительно ли игрок изучил этот предмет.
  • способ, скажем, bool isEligibleToResearch(), который проходит через все предметы в prerequisiteResearch, чтобы проверить, не исследовал ли игрок их.

С этим я думаю, что у вас будет хорошо структурированный и расширяемый класс требований.

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) 

Я думаю, что главный вопрос с моим подходом здесь является то, что я только думал использовать набор, а не дерево ; , но с некоторым изменением, вы могли бы сделать лучше, чем я.

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) на результат, чтобы игнорировать текущий.

+0

То, что я хочу сделать, это рекурсивно проверить вспомогательные предварительные требования, если функция возвращает false, чтобы я мог вернуть игроку список исследований, которые он пропустил, чтобы выполнить задачу ... –

+0

, чтобы сделать это немного ясным. I объяснит это еще раз ... У каждого исследования есть предпосылки, и у каждого предпосылки есть вспомогательные предпосылки и так далее. Это может варьироваться в зависимости от ситуации ... Так, например, у вас может быть исследование без каких-либо предварительных условий, в то время как, с другой стороны, у вас может быть исследование с 10 предпосылками [каждый с его собственным предварительным условием (s)] ... Итак, что заставило меня хотеть получить список всех этих вспомогательных предпосылок, так это то, что я хочу, чтобы пользователь знал, чего ему не хватает, чтобы закончить свою миссию, и я хочу использовать их в других методах ... –

+0

@ErricJManderin См. Мой обновленный ответ. Теперь метод вернет все «Исследования», которые необходимы для достижения цели. –

1

Вы можете использовать такую ​​функцию в своем исследовательском классе, которая просто включает перечислимый список Research, который вы используете, чтобы проверить, что у игрока есть предпосылки. Это предполагает наличие локального списка или другого перечисляемого из Research es, который называется Prereqs в классе Research.

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

Функция только recursivly проверяет каждый PREREQ, чтобы увидеть, если он находится в списке прошел, и затем проверяют, что PreReqs предварительных условий. All - метод расширения linq, который возвращает true, только если все элементы перечислимого удовлетворяют критериям. Если нет элементов, он возвращает true (все нули элементов соответствуют критериям), поэтому это прекратится, когда вы перейдете к Research без предварительных условий.

Примечание: Это делает первый поиск глубины и не соображает, что одно и то же исследование является суб-исследованием более чем одного предпосылки.

+0

Не стесняйтесь менять имя функции;) – Cemafor