2017-01-26 2 views
1

Ich versuche ein Quest-System zu erstellen. Ich habe QuestCreator, Quest und mehrere objektive Klassen, die eine Schnittstelle erben (TalkObjective, LocationObjective usw.)Speichern von mehreren Datenarten

Im Konstruktor der Quest-Klasse habe ich eine Liste wie List<IObjective> erstellt. Es hat nicht funktioniert.

Dann habe ich eine Klasse erstellt, um alle Arten von Listen zu halten. Aber ich verlor die Fähigkeit, meine Ziele zu ordnen.

Meine Frage ist; Gibt es einen besseren Weg/Design, das zu tun?

[Bearbeiten]

Es tut mir Leid, dass ich es nicht detailliert genug war. Seit ich meinen Code geändert habe, kann ich ihn hier nicht mehr posten. Ich habe versucht, den gleichen Code zu erstellen, aber dieses Mal gibt mir der Code keinen Fehler. Also löse ich das Problem alleine.

Ich verwendete ein Tutorial, das nicht abgeschlossen/aufgegeben wurde. Here is the link to github

Ich baute mein Item/Inventory System mit abstrakten Klassen und es war das erste, was mir in den Sinn kam. Aber meine Absicht war es, dieses Quest-System so zu gestalten, wie es der Ersteller des Tutorials entworfen hat, damit ich seinen Weg kennen lernen kann.

Ich wollte Objekte verschiedener Objective-Klassen in einer Liste mit der Schnittstelle, die sie in der üblichen Weise verwenden.

public class QuestCreator : MonoBehaviour { 
#region fields 
private List<IQuestObjective> objectives; 
private GameObject itemManager; 
private ItemDatabase itemdb; 
private Location location; 
private Quest quest; 

//For Saving Quest 
private Quest_data quests; 
#endregion 


void Update() 
{ 
    //Just for the test purpose 
    if (Input.GetKeyDown (KeyCode.E)) 
    { 
     itemManager = GameObject.Find ("GameManager"); 
     itemdb = itemManager.GetComponent<ItemDatabase>(); 
     Item item = new Item(); 
     Item item2 = new Item(); 
     item = itemdb.weapon_database[0]; 
     item2 = itemdb.weapon_database [1]; 

     CollectionObjective collectionObjective = new CollectionObjective ("Find", 3, item, "Find this precious item"); 
     CollectionObjective collectionObjective2 = new CollectionObjective ("Find", 1, item2, "Find Sword of Warrior from Dark Passage"); 
     LocationObjective locationObjective = new LocationObjective ("Go to Green Valley", "Go to " + location, location, false); 
     objectives = new List<IQuestObjective>(); 
     objectives.Add(collectionObjective); 
     objectives.Add (collectionObjective2); 
     objectives.Add (locationObjective); 

     QuestText questText = new QuestText(); 
     QuestIdentifier questIdentifier = new QuestIdentifier(); 
     questText.Title = "Finding Sword of Warrior"; 
     questText.DescriptionSummary = "Summary..."; 
     questText.Hint = "Hint..."; 
     questIdentifier.QuestID = 1; 
     questIdentifier.SourceID = 1; 
     quest = new Quest (questIdentifier, questText, objectives); 
     Debug.Log (quest.Objectives[1].Description); 
     Debug.Log (quest.Objectives.Count); 

    } 
} 
+0

Bearbeiten Sie Ihre Frage etwas Code zu erhalten, und auch, erklären, was Sie unter „nicht funktioniert“. –

+0

Es klingt, als ob Sie mit der Liste in die richtige Richtung gingen. Wie Sohar vor mir gesagt hat, bitte zeigen Sie Ihren Code und teilen Sie uns alle Fehler mit, die Sie erhalten – Ben

Antwort

0

zunächst an all Spiele-Programmierung, bin ich hoch gehen durch dieses Web Buch darauf hindeutet - http://gameprogrammingpatterns.com/contents.html (es bietet auch PDF/Buch Variante für etwas Geld). Es hilft dir bei einigen Spielbeispielen und du wirst eine Idee bekommen wie Spiele gemacht werden.

Ihre Frage ist eine Art breit und in Bezug auf jede Person Meinung, die nicht als Q auf SO aufgeführt werden sollte, aber:

mir Logischerweise ist es wie: Es gibt 1 Quest-(erstellt von Fabrik QuestCreator), die List<Objectives> enthält.

Ziel sollte eine abstrakte Klasse sein, die einige Variablen und Methoden enthält (Ist Ziel getan? - andere Dinge, die alle Objectives gemeinsam haben).

Danach sollten Sie kleinere Objektiv erben (wie TalkObjective,) und interne Implementierung der Methoden überschreiben ->IsObjectiveDone.

Auf Um ehrlich zu sein, in der Spiel-Programmierung, Entwickler sind wegtreten, um Vererbung so weit wie möglich zu vermeiden. Es ist zu schwierig, einen Vererbungsbaum zu erstellen und dann den Code zu durchlaufen. Stattdessen versuchen sie sich auf ein Muster wie Component (dieselbe Quelle wie oben) zu verlassen.

Hinzufügen einiger Beispiel:

public abstract class Objective 
{ 
    public bool IsObjectiveDone { get; private set; } 

    public virtual void CheckIfDone(); 
} 

public class ObjectiveGroup 
{ 
    public bool AllObjectivesDone => SubObjectives.All(a => a.IsObjectiveDone); 
    public Objective[] SubObjectives { get; private set; } 

    public static ObjectiveGroup Create(TypeOfQuest aType, Requirements[] aReq) 
    { /* factory implementation */ } 
} 

Wenn Sie das Beispiel oben haben, können Sie jede Art von „speziellen“ Ziel definieren:

public class ItemObjective : Objective 
{ 
    public Item RequiredItem { get; private set; } 

    override public void CheckIfDone() 
    { 
     this.IsObjectiveDone = Player.GetInstance().Inventory.Contains(RequiredItem); 
    } 
} 

Sobald Sie wollen neue Suche starten, Sie Ich rufe die Fabrik an, die die Quest erstellen wird und eine Gruppe von Zielen enthält. Bei jedem Ziel wird CheckIfDone immer dann ausgeführt, wenn der Benutzer eine Aktion ausführt oder ein neues Objekt erhält.

public class Quest 
{ 
    public ObjectiveGroup { get; private set; } 

    public Quest(TypeOfQuest aType, Requirements[] aReq) 
    { 
     this.ObjectiveGroup = ObjectiveGroup.Create(aType, aReq); 
    } 
} 

public class Player 
{ 
    public List<Quest> Quests = new List<Quest>(); 
    public List<Item> Inventory = new List<Item>(); 
} 


public void Main(/* ... */) 
{ 
    Player player = new Player(); 
    player.Quests.Add(new Quest(TypeOfQuest.ItemObtain, new Requirements[] { Item["Sword of Conan"] }); 

    while(true) 
    { 
     player.Quests.ObjectiveGroup.ForEach(a => a.SubObjectives.ForEach(b => b.CheckIfDone())); 

     foreach(var objGrp in player.Quests.ObjectiveGroup) 
      if(objGrp.IsObjectiveDone) Console.WriteLine("Quest completed"); 
    } 
} 
+0

Sie sollten den letzten Absatz überprüfen, weil es falsch ist. Game-Programmierer treten nicht von der Vererbung weg. Im Gegenteil, es löst viele Probleme und würde höchstwahrscheinlich das OP-Problem lösen. – Everts

+0

@Everts, ehrlich in großen Lösungen und Projekten, Entwickler sind davon weg. Vererbung ist zu komplex, um in Spielen gehandhabt zu werden, in denen Sie über 1000 Arten (Klassen) von Objekten haben. Stattdessen sind Anwendungen/Spiele datengetriebener, was bedeutet, dass Sie ihr Verhalten nur anhand von Zahlen/Skripts (LUA-Sprache) definieren. Wenn Sie etwas modifizieren möchten, müssen Sie die App nicht erneut kompilieren, sondern nur wenige Daten ändern Dateien. – Tatranskymedved

+0

Dies wird off-topic vom OP, Sie unterstützen Ihre Idee mit LUA, die standardmäßig nicht in Unity (der OP-Plattform) verwendet werden kann (immer noch bietet LUA Vererbung). Es sollte nicht darum gehen, Vererbung zu verbieten, es sollte darum gehen, sie richtig zu benutzen. Das OP wird höchstwahrscheinlich sein Problem mit der richtigen Verwendung von OOP, Vererbung und Polymorphismus lösen. – Everts

0

Hier ist ein Beispiel für den Code, den Sie für Ihr Problem haben können.

public class Program 
{ 
    public struct Location 
    { 
     // Assumes 2D game location 
     public int X; 
     public int Y; 
    } 

    public struct Character 
    { 
     public int GameCharId; 
    } 

    public class BaseObjective 
    { 
     public string Title; 
     public string Description; 
    } 

    public class TalkObjective : BaseObjective 
    { 
     public Character TargetCharacter; 
    } 

    public class LocationObjective : BaseObjective 
    { 
     public Location TargetLocation; 
    } 

    public static void Main(string[] args) 
    { 
     List<BaseObjective> currentObjectives = new List<BaseObjective>(); 

     TalkObjective obj1 = new TalkObjective(){ Title = "Talk to Bob", Description = "Bob has some useful information for you", TargetCharacter = new Character(){GameCharId = 87}}; 
     LocationObjective obj2 = new LocationObjective(){ Title = "Find the thing", Description = "Bob informed you of a thing, go and find it", TargetLocation = new Location(){ X = 33, Y=172}}; 


     currentObjectives.Add(obj1); 
     currentObjectives.Add(obj2); 
    } 
} 
+0

Vielen Dank, dass Sie Ihre wertvolle Zeit für Hilfe verbracht haben. – user3630634

1

Sie müssen in Vererbung und Polymorphie suchen.

In Ihrem Fall würden Sie einen IObjective Klasse haben, die alle gängigen Logik enthält:

public abstract IObjective : MonoBehaviour 
{ 
    public abstract void CommonMethod(); 
    public virtual void OverrideIfNeeded(){} 
    public void UseAsIs(){} 
} 

CommonMethod hat von der Unterklasse overrriden werden. OverrideIfNeeded kann überschrieben oder so verwendet werden, wie es ist. UseAsIs können nicht überschrieben werden (sie können jedoch ausgeblendet werden).

Dann haben Sie eine Sammlung:

IEnumerable<IObjective> collection; 

es alle Arten von verschiedenen Objekten, die alle IObjective sind und Sie können alle Methoden aus der IObjective iterieren und rufen:

foreach(IObjective obj in collection) 
    { 
     obj.CommonMethod(); 
     obj.UseAsIs(); 
     ... 
    } 
+0

Vielen Dank für Ihre wertvolle Zeit. – user3630634

0

Der besseres Design , wird Finite Automaton für diese Aufgabe verwenden, nicht irgendeine Art von Liste der Zielsetzung. Deine Aufgabe wird also mit dem Diagramm der Prädikate beschrieben (Bedingungen, in die man sich bewegen kann oder nicht, Ereignishörer, wenn du willst) und Zustände (Begleiter in der Suche). Stellen wir uns zum Beispiel vor, dass der Held in eine Taverne kommt, dann gibt er auch verschiedene Questreihen ein. Einer von ihnen beschreibt Stadt Räuber Suche:

[Start] -(talked with barmen about robber)-> [Kill robber] 
[Start] -(talked with robber wife) -> [Ask robber to return items] 

//this is made for karma decision between two quest lines, so you are free to chose what to do with poor robber, take robber money or gain karma in town. 
[Ask robber to return items] -(talked with barmen about robber)-> [Kill robber] 
[Kill robber] -(talked with robber wife) -> [Ask robber to return items] 

//barmen quest line 
[Kill robber] -(robber killed)-> [Success quest (you can take money as reward)] 
[Kill robber] -(robber spared)-> [Fail quest] 

//wife quest line 
[Ask robber to return items] -(robber convinced)-> [Success quest (you can now sleep with his wife for free)] 
[Ask robber to return items] -(robber not convinced)-> [Ask robber to return items] 
[Ask robber to return items] -(robber got bored of your questions)-> [Fail quest] 

Wie Sie sehen das alles mit einfachen Automaten Regeln beschrieben ist, und Sie können ziemlich komplexe Aufgaben ohne viel Aufwand machen. Im Falle deiner Zielliste kannst du deine Suche nicht in verschiedene Zustände verzweigen, also ist es nur möglich deine Erfüllung zu erreichen, indem du ALLE beschriebenen Aktionen nacheinander befolgst, selbst wenn es zwei mögliche und erfolgreiche Ergebnisse gibt.

Prädikate in diesem Beispiel können als Ereignisse und Zustände beschrieben werden - als einfache Zahlen oder Strings.

Dies ist nur sehr langsam Beispiel dafür, wie ich es sehe:

public class QAutomaton 
    { 
     private readonly Dictionary<string, Dictionary<string, string>> _graph = new Dictionary<string, Dictionary<string, string>>(); 

     public void AddState(string state) 
     { 
      _graph.Add(state, new Dictionary<string, string>()); 
     } 

     public void AddCondition(string from, string condition, string to) 
     { 
      _graph[from].Add(condition, to); 
     } 

     public string GetNext(string from, string condition) 
     { 
      var conds = _graph[from]; 
      string nextState; 
      conds.TryGetValue(condition, out nextState); 
      return nextState; 
     } 
    } 

    public class Quest 
    { 
     public string CurrentState = "Start"; 
     private readonly QAutomaton _automaton; 

     public Quest(QAutomaton automaton) 
     { 
      _automaton = automaton; 
     } 

     public void FeedEvent(string condition) 
     { 
      var nextState = _automaton.GetNext(CurrentState, condition); 
      if (nextState != null) 
      { 
       CurrentState = nextState; 
      } 
     } 
    } 

    public static void Main() 
    { 
     var fa = new QAutomaton(); 
     fa.AddState("Start"); 
     fa.AddState("Kill robber"); 
     fa.AddState("Ask robber to return items"); 

     fa.AddCondition("Start", "talked with barmen about robber", "Kill robber"); 
     fa.AddCondition("Start", "talked with robber wife", "Ask robber to return items"); 
     //describe rest here... 
     _quest = new Quest(fa); 
    } 

    public static void OnTalkedWithBarmenAboutRobberEventHandler() 
    { 
     _quest.FeedEvent("talked with barmen about robber"); 

     var state = _quest.CurrentState; 
     if (state == "Kill robber") 
     { 
      //locate him on global map or something 
     } 
    } 
+0

Ich bin nicht sicher, dass das OP nach einer State-Machine-ähnlichen Lösung war. Ich denke, er war nach einer Anfänger-Frage mehr darauf gekommen, wie man die Menge an Code minimiert. Das macht deine Antwort nicht falsch, einfach zu weit hergeholt, denke ich. Ich könnte mich allerdings irren. – Everts

+0

Nun, es ist immer gut zu zeigen, wie die Dinge in diesem Bereich getan werden, als bestimmte Frage zu lösen und ihn herauszufinden, was seine Lösung überhaupt nicht flexibel ist und er wird gezwungen sein, es zu einem bestimmten Zeitpunkt auszuhacken. Dies ist keine Vorliebe, dies ist nur eine Lösung, die den Code auf ein Minimum reduziert, billig in Speicher/Leistung, theoretisch unterstützt (einfach optimiert durch verschiedene Algorithmen) und flexibel genug, um sehr komplexe Quests mit unterschiedlichen Ergebnissen zu erstellen. – eocron

+0

@eocron Vielen Dank für Ihre wertvolle Zeit für Ihre Hilfe. Ich werde definitiv etwas Zeit mit diesem Designmuster verbringen. – user3630634

Verwandte Themen