任务是程序应用中常见的系统,它有助于用户代入角色,也有助于研发、运营和用户的互动,通常完成任务之后会给予用户一定的奖励。
1,基本数据结构
早期的任务系统设计的都很简单,大部分都是线性结构,偶尔会有环任务,我们只需要很少的数据格式遍可以存储当前任务进度,比如如下类:
public class TaskDate
{
public int max;//最大进度
public int current;//完成进度
}
然后我们需要一个字典去存储该任务对象,既如下数据结构:
public Dictionary<int, TaskDate> currentTaskDic = new Dictionary<int, TaskDate>();//当前显示任务
public Dictionary<int, TaskDate> CurrentTaskDic { get => currentTaskDic; }
到此为止,我们已经完成了最基本的数据结构,同时可以处理基本的任务单元。比如:击杀指定怪物50只。
当然这都是些最基本的数据结构,对于任务的处理,我们还需要与策划确定类型与结构。某些场景中,还要存储已完成任务,以判断用户是否重复领取等。这些工作我们都可以放到之后处理。
2,基本函数
一般的任务处理,都类似于打点机制。我们拿从《永劫无间》中截图的任务举例:
以上任务实际上可以拆分为两个类型:1,在装备XX的时候使用XX武器造成击杀;2,使用XX武器造成XX点伤害。
对于第一种,则需要在造成击杀的时候,判断是否符合“装备XX”、“使用XX武器”两个条件。如果满足的话则向管理器传递完成信息。
对于第二种,需要在造成伤害的时候,判断“是否使用武器XX”,符合的话向管理器传递信息。
这一段的意思是,我们先将相同触发条件的任务抽象为一个类型,然后统一处理该类型的任务。即先将数据抽象为行为,再用某种算法去处理该行为。
于是,我们的TaskProp(任务管理器)中就需要类似这样一个总得处理函数,它负责处理种种复杂的任务进度,并负责判断处理结构:
/// <summary>
/// 任务类型
/// </summary>
enum TaskType
{
type2,//2,X1个X2设施达到Y级(其中X2任意X1数量即可满足)
type3,//3,X建筑下的设施总等级达到Y级
}
/// <summary>
/// 任务进度判断
/// </summary>
public void TaskJudgment(int _id,int _num)
{
//获取任务类型,通过方法计算
}
比如ID为15的任务为“击杀100只蜘蛛”:那么每击杀一只蜘蛛的时候,我们就调用一次TaskJudgment(15,1)。在该函数体类,则会找到currentTaskDic[15].current并进行加值处理。当符合条件的时候,则进行该任务完成的判断。
最后我们需要一个完成时候领取的判断,则可以获得以上任务的奖励:
/// <summary>
/// 任务完成回调
/// </summary>
public void TaskComplateCallBack()
{
//任务完成之后刷新界面
}
至此,我们完成了一个任务完整的生命周期,即生成任务=>推进任务=>领取奖励。
3,任务红点等附属结构
当我们完成任务,或者运营推出新任务的时候,通常会使用红点来提醒玩家。虽然现在红点系统已经被滥用到一个有些令人生厌的地步,但某些场合它又是必要的。
红点一般使用树状图结构的数据来处理,即子节点有红点的时候,父节点的红点也会被点亮。
回归到基本的任务单元,我们只要判断是否符合红点条件即可。比如完成某项任务需要领取的时候出现红点,则基本代码如下:
bool _isHint = _taskD.current >= _taskD.max;
然后基于这个判断,我们可以处理UI上相应的红点系统。
4,扩展延伸
以上都是针对最基本的任务单元处理,现在游戏中的任务已经设计的比较复杂,我们需要针对具体的任务结构具体分析。比如环任务、日常任务、周任务、赛季任务、主线任务、支线任务等等,它们有这诸如时间、前置等等限制。
但万变不离其宗,我们只要搞懂基本的数据类型和数据结构,便可以处理这些看似复杂的任务系统。
另外任务系统不单单应用于游戏,硬件单元处理、操作软件这些都包含任务系统的范畴。大到国际运行,小到日常活动,任务系统与我们每个人都息息相关。
5,总结
写任务系统代码的时候,先认真分析任务的结构,然后根据表格抽象出我们需要的数据结构。最后层层处理,以保证系统的正常运行。