2017-01-26 2 views
1

퀘스트 시스템을 만들려고합니다. QuestCreator, Quest 및 인터페이스 (TalkObjective, LocationObjective 등)를 상속하는 여러 객관적인 클래스가 있습니다.여러 유형의 데이터 저장

퀘스트 클래스의 생성자에서 List<IObjective>과 같은 목록을 생성했습니다. 작동하지 않았습니다.

그런 다음 모든 유형의 목록을 보유 할 클래스를 만들었습니다. 그러나 나는 나의 목표를 주문하는 능력을 잃어 버렸다.

내 질문은; 거기에 더 좋은 방법/디자인을 할 수 있습니까?

[편집]

나는 그것을 충분히 설명하지 않았다 미안 해요. 코드를 변경 한 이후로 여기에 게시 할 수 없습니다. 같은 코드를 만들려고했으나 이번에는 코드가 오류를주지 않습니다. 그래서 나는 스스로 문제를 해결한다.

완료/포기되지 않은 자습서를 사용하고있었습니다. Here is the link to github

필자는 추상 클래스를 사용하여 내 Item/Inventory 시스템을 구축했으며 이것이 내 마음에 처음으로 나타났습니다. 그러나 나의 의도는 튜토리얼의 제작자가 설계 한 방법으로이 퀘스트 시스템을 만들어서 그의 길을 배울 수있게하는 것이 었습니다.

일반적인 목적으로 사용하는 인터페이스가있는 다른 목표 클래스의 개체를 목록에 넣기를 원했습니다. 모든 게임 프로그래밍에 먼저

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

몇 가지 코드를 표시하도록 질문을 수정, 또한, 당신이 "작동하지 않는"무슨 뜻인지 설명해주십시오. –

+0

리스트 으로 올바른 방향으로 가고있는 것 같습니다. 그러나 Zohar가 저에게 말씀 드렸듯이, 귀하의 코드를 보여주고 수신 한 오류를 알려주십시오. – Ben

답변

0

, 내가보기 엔이 웹 책을 통해 갈 제안 해요 - http://gameprogrammingpatterns.com/contents.html을 (그것도 돈을 위해 PDF/책 변형을 제공합니다). 그것은 게임 패턴 예제로 당신을 돕고 게임이 어떻게 만들어 졌는지 알게 될 것입니다. 나에게 논리적으로

은 같은 것입니다 :

귀하의 질문은 그러나, 종류 SO에 Q로 표시해서는 안 각 사람의 의견의 광범위하고 관련이있다 (1) 퀘스트 (공장 QuestCreator 만든이)이며 List<Objectives>을 포함합니다.

Objective는 일부 변수와 메서드를 포함하는 추상 클래스 여야합니다 (객관적인 작업입니까? Objectives에 공통적으로있는 다른 항목).

그런 다음 작은 목표 (예 : TalkObjective, ItemObjective)를 상속하고 메서드의 내부 구현을 재정의해야합니다. ->IsObjectiveDone.

켜기 사실 게임 프로그래밍에서 개발자는 가능한 한 상속을 피하기 위해 발걸음을 옮깁니다. 상속 트리를 만들고 코드를 살펴 보는 것은 너무 어렵습니다. 대신 그들은 Component 같은 패턴 (위와 같은 소스)에 의존하려고합니다.당신은 위의 예를 일단

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 */ } 
} 

는, 당신은 "특별한"목표의 각 유형을 정의 할 수 있습니다 :

몇 가지 예제를 추가

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

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

당신은 새로운 퀘스트를 시작하는 것이 좋습니다되면, 목적의 그룹을 포함하는 퀘스트를 만들 공장을 호출합니다. 각 목표에 대해 사용자가 몇 가지 조치를 취할 때마다 확인/완료하거나 새 항목을 얻습니다.

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

마지막 단락을 검토해야합니다. 잘못되었습니다. 게임 프로그래머들은 상속에서 벗어나지 않습니다. 반대로, 많은 문제를 해결하고 OP 문제를 해결할 가능성이 큽니다. – Everts

+0

@ 솔직히 큰 솔루션과 프로젝트에서, 개발자들은 그것을 벗어나고 있습니다. 상속은 게임에서 다루기에는 너무 복잡합니다. 여기에는 1000 개가 넘는 유형 (클래스)의 객체가 있습니다. 대신 응용 프로그램/게임은 데이터 중심적입니다. 즉 숫자/스크립트 (LUA 언어)로 동작을 정의하는 것이므로 아무 것도 수정하지 않고 응용 프로그램을 다시 컴파일 할 필요가 없으며 단지 소수의 데이터 만 변경하면됩니다 파일. – Tatranskymedved

+0

이것은 OP에서 주제를 벗어나고 있습니다. 당신은 Unity (OP 플랫폼)에서 기본적으로 사용할 수없는 LUA로 아이디어를 지원합니다 (여전히 LUA는 상속을 제공합니다). 상속을 금지하는 것이 아니어야하며 적절하게 사용해야합니다. OP는 OOP, 상속 및 다형성의 적절한 사용으로 문제를 해결할 가능성이 큽니다. – Everts

0

다음은 문제의 코드 예입니다.

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

귀중한 시간을 보내 주셔서 감사합니다. – user3630634

1

상속 및 다형성을 조사해야합니다.

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

CommonMethod는 서브 클래스에 의해 overrriden되어야한다 :

는 귀하의 경우에는 모든 일반적인 로직을 포함하는 IObjective 클래스가 것입니다. OverrideIfNeeded는 재정의되거나 그대로 사용될 수 있습니다. UseAsI는 재정의 될 수 없습니다 (그러나 숨길 수 있음).

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

소중한 시간을 보내 주셔서 감사합니다. – user3630634

0

더 나은 디자인 :

IEnumerable<IObjective> collection; 

는 IObjective에서 모든 방법을 반복하고 호출 할 수있는 모든 IObjective 당신을있는 다른 개체의 모든 종류가 포함되어 다음

당신은 컬렉션이 , 유한 오토 마톤이 작업을위한 것입니다, 어떤 종류의 목표가 아닙니다. 그래서, 당신의 퀘스트는 술어의 그래프로 설명 될 것입니다 (당신이 원한다면 상태로 이동할지 여부, 이벤트 리스너 및 상태 (퀘스트에서의 부대)). 예를 들어 주인공이 선술집에 들어서고 다른 퀘스트 라인을 입력했다고 가정 해 봅시다. 이 모든 간단한 기계적으로 규칙을 설명 참조하고 많은 노력없이 꽤 복잡한 퀘스트를 만들 수 있듯이

[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] 

: 그 중 하나는 마을 강도를 퀘스트를 설명합니다. 당신의 목표 목록의 경우에 당신은 아마도 다른 주에 당신의 퀘스트를 분기 할 수는 없기 때문에 가능한 두 가지의 성공적인 결과가 있다고하더라도 퀘스트를 완료하는 유일한 방법은 하나씩 기술 된 모든 행동을 충족시키는 것입니다.

이 예제의 조건은 이벤트로, 상태는 간단한 숫자 또는 문자열로 설명 할 수 있습니다.

이것은 내가 그것을보고하는 방법의 매우 느린 예를 그냥 :

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

OP가 State-Machine 솔루션과 비슷한지 확실하지 않습니다. 나는 그가 코드의 양을 최소화하는 방법에 대한 몇 가지 초급 문제 이후에 더 많은 것이라고 생각한다. 그것은 당신의 대답을 잘못하지 않습니다. 나는 너무 추측하여 추측 할 것입니다. 나는 틀릴 수 있었다. – Everts

+0

글쎄, 특정 문제를 해결하고 자신의 솔루션이 전혀 융통성이 없다는 것을 알아 차리고 어떤 시점에서 해킹을 강요당하는 것보다이 분야에서 어떻게 일을했는지 ​​보여줄 수 있습니다. 이것은 선호하지 않습니다. 이것은 메모리/성능이 최저 수준으로 낮아지고, 이론적으로 지원되며 (다양한 알고리즘으로 쉽게 최적화 됨) 코드를 최소화하고 다양한 결과로 매우 복잡한 퀘스트를 수행 할 수있을 정도로 유연한 솔루션입니다. – eocron

+0

@eocron 귀중한 시간을 보내 주셔서 감사합니다. 나는 확실히 그 디자인 패턴에 약간의 시간을 할애 할 것입니다. – user3630634

관련 문제