我试图创建一个任务系统。我有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);
}
}
答案 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
都有共同点的其他东西。)
之后你应该继承较小的目标(如TalkObjective
,ItemObjective
)并覆盖方法的内部实现 - &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
}
}