存储多种类型的数据

时间:2017-01-26 07:16:49

标签: c# unity3d

我试图创建一个任务系统。我有QuestCreator,Quest和多个继承接口的目标类(TalkObjective,LocationObjective等)

在Quest课程中&#39;构造函数,我创建了一个像List<IObjective>这样的列表。 它没有用。

然后我创建了一个类来保存所有不同类型的列表。但是我失去了订购目标的能力。

我的问题是;有没有更好的方法/设计呢?

[编辑]

对不起,我没详细说明。由于我更改了代码,因此无法在此处发布。我试图创建相同的代码,但这次代码没有给我错误。所以我自己解决了这个问题。

我使用的教程没有完成/放弃。 Here is the link to github

我用抽象类构建了我的物品/库存系统,这是我想到的第一件事。但我的目的是按照教程的创建者的方式创建这个任务系统,这样我就可以学习他的方式。

我想将不同Objective Classes的对象放在一个列表中,并使用它们以常用方式使用的接口。

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

    }
}

4 个答案:

答案 0 :(得分:1)

你需要研究继承和多态。

在您的情况下,您的IObjective类包含所有常见逻辑:

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

CommonMethod必须由子类覆盖。可以覆盖或按原样使用OverrideIfNeeded。 UseAsIs无法覆盖(虽然可以隐藏)。

然后你有一个集合:

IEnumerable<IObjective> collection;

它包含所有IObjective的不同对象,你可以迭代并调用IO目标中的所有方法:

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

答案 1 :(得分:0)

首先是所有的游戏编程,我强烈建议您阅读这本网页 - http://gameprogrammingpatterns.com/contents.html(它还提供PDF /图书版本以获得一些收益)。它可以为您提供一些游戏模式示例,您将了解游戏是如何制作的。

你的问题有点广泛,与每个人的意见有关,不应该在SO上列为Q,但是:

逻辑上对我来说就是:有1个Quest(由工厂QuestCreator创建),其中包含List<Objectives>

目标应该是一个抽象类,包含一些变量和方法(目标是否完成? - 所有Objectives都有共同点的其他东西。)

之后你应该继承较小的目标(如TalkObjectiveItemObjective)并覆盖方法的内部实现 - &gt; 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);
    }
}

一旦你想要开始新的任务,你将调用工厂,它将创建包含目标组的Quest。在每个目标上,每当用户执行某些操作/获取新项目时,您将CheckIfDone。

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

答案 2 :(得分: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);
    }
}

答案 3 :(得分:0)

更好的设计,将使用有限自动机来完成此任务,而不是某种目标列表。 因此,您的任务将使用谓词图表(条件在何处移动到状态,如果您想要移动到事件监听器)和状态(任务中的伴随)来描述。例如,让我们想象英雄进入一些小酒馆,然后他也进入一些不同的任务线。其中一个描述了城镇强盗任务:

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

正如您所看到的,这些都是用简单的自动机规则描述的,您可以毫不费力地完成相当复杂的任务。如果您的目标列表不能将您的任务分支到不同的状态,那么只有完成任务的可能方法是逐个完成所有描述的操作,即使它有两个可能的成功结果。 / p>

此示例中的谓词可以描述为事件和状态 - 简单数字或字符串。

这只是我看到它的一个非常缓慢的例子:

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