1 前言

都2022年了,元宇宙的热风,把游戏开发给带火了。说不定打开文章的你,就是在做元宇宙游戏,哈哈

先说一下【XX是什么】。任务系统是一个引导玩家进行游戏的系统。有些玩家进入游戏,不知道怎么玩,那可以点开任务按钮,出来一个弹窗,就能告诉你一步步该做啥。

任务系统需要策划同学,写好任务线。复杂的游戏,还需要主任务线,和子任务线。

游戏基本上都需要任务系统,从头自己写一个,是不合算的,也不靠谱。可以基于一个好用的框架,扩展开发。本人也一样,先找了个不错的任务系统(Unity插件),然后做扩展开发(特别是任务跟后台的交互)。

本文主要目的,就是讨论这个任务系统(名字叫DialogueQuests)的原理,以及怎么用。这对设计一个任务系统,也是有很大的参考意义的。

插件的地址:Unity Assets Store - DialogueQuests

插件是收费的,也不算贵。咱们要支持正版嘛,当然了,如果你是纯学习用(反正我相信你真的是学习用^^),可以私信我获取源码喔.

需要说明的是,这个系统只是纯客户端使用,任务不从后台下发。如果你需要和后台交互,需要区分主任务,子任务,只要按需来定制即可,还是很好扩展的。

本文分三部分,一个是任务系统的介绍,一个是怎么用该任务系统,最后做一些分析。

先上效果图,再继续废话哈。

2 任务系统介绍

我们先无脑想一下,一个任务系统需要啥?(不考虑和后台的交互)
(a) 全局管理类,管理所有的任务进度,触发对应的弹窗;
(b) 事件监听类,用于监听任务是否完成;
© 弹窗显示类,用于显示/隐藏弹窗;

好了,接下来,我们看看DialogueQuests任务系统有没有这些。这个插件,打开后,发现包含一些预制件,和脚本文件。我们先把这些拿出来看一下。

2.1 重要的预制件

2.1.1 DQManager

这个预制件,用于做各种初始化。你需要把这个预制件,拖到游戏的scene中。
我们打开这个预制件看看。

发现一些全局脚本,都在这加载初始化。所以我们要用这个任务系统,首先就是把DQManager预制件,拖到scene中。

TheLoader脚本,加载了其他的预制件!特别是DQCanvas!

2.1.2 DQCanvas

这个主要是UI相关。如果你要修改UI,比如弹窗样式,只要打开这个预制件修改。

2.2 重要的脚本类

2.2.1 NarrativeData

单例。记录数据,特别是任务进度,任务数量。还包括保存任务,加载历史任务。

//Quest data
public Dictionary quests_status = new Dictionary(); //0=NotStarted, 1=Ongoing, 2=Completed, 3=Failed
public Dictionary quests_progress = new Dictionary(); //ID is quest_id+titlepublic void Save();//存储历史数据,把NarrativeData转为二进制数据存到文件
public static NarrativeData Load(string filename);//加载历史数据
public static NarrativeData Get();//获得数据
//设置进度
public void SetQuestProgress(string quest_id, string progress, int value);

2.2.2 NarrativeManager

单例。最核心的类,启动任务,完成任务,轮询状态,触发UI显示(例如对话框)。

public UnityAction onQuestStart;//任务开始的函数处理
public UnityAction onQuestComplete;//任务结束的函数处理
public UnityAction onQuestFail;//任务失败的函数处理//任务参与者
public List actor_list = new List();
//任务数据
public List quest_list = new List();
//被触发的event列表
private List trigger_list = new List();
//event的处理,例如弹窗,结束任务等
private Queue event_line_queue = new Queue();//逐帧更新,如果trigger_list或event_line_queue有数据,则处理
public void Update();
//启动event
public void StartEvent(NarrativeEvent narrative_event);
//显示弹窗
public void StartDialogue(DialogueMessage dialogue);
//启动任务
public void StartQuest(QuestData quest);
//完成任务
public void CompleteQuest(QuestData quest);

2.2.3 NarrativeEvent

NarrativeEvent 是Monobehavior,有Awake, Start和Destroy等函数。用于接收一个事件,并做对应的处理(通过NarrativeEffect),事件触发还有条件,通过NarrativeCondition判断。

//记录所有的event
private static List event_list = new List();
//触发event的条件
private List conditions = new List();
//挂载当前gameobject的effects,也可以挂载在子gameobject下,那样的event存在下面的event_lines
private List effects = new List();
//event的子gameobject下的各种对象,例如dialogueMessage,DialogueChoice,NarrativeEffect
private List event_lines = new List();//触发event,条件负责,则添加到NarrativeManager的trigger_list
private void OnTriggerEvent(Actor player);

NarrativeEvent 触发的类型:

    public enum NarrativeEventType {Manual = -2, //By script onlyAutoTrigger = -1, //As soon as conditions are metInteractActor = 0, //When interact with actorNearActor = 2, //Just go near an actorAtStart = 5, //After scene loadEnterRegion = 10, //Enter a regionLeaveRegion = 11, //Exit a resionAfterEvent = 20, //After another event}

2.2.4 NarrativeEventLine

event触发后的处理,请看NarrativeEvent 有个List。可以是弹对话框,可以是完成某个任务,且可以叠加。即,一个event可以产生多个执行效果。

public class NarrativeEventLine
{public GameObject game_obj;public NarrativeEvent parent;//对应的eventpublic DialogueMessage dialogue = null;//对话框public List choices = new List();//选择框public List conditions = new List();//处理条件public List effects = new List();//处理效果
}

2.2.5 Actor

是一个MonoBehaviour,作为Component,绑定在所以跟任务相关的NPC中。

public ActorData data;                //actor关键数据,如is_player
public UnityAction onInteract; //两个actor交流的回调函数
public UnityAction onNear;     //两个actor靠近
private List events_list;//标注trigger_actor为当前actor的event列表
private static List actor_list = new List();//静态全局变量,所有的actorvoid Start()
{events_list = NarrativeEvent.GetAllOf(this);//获得event_listforeach (NarrativeEvent evt in events_list){evt.AddActor(this);//记录onInteract和onNear,指向NarrativeEvent.OnTriggerEvent, 例如当2个actor靠近,就可以触发对应的event}if (NarrativeControls.Get()){NarrativeControls.Get().onPressTalk += OnClick;//对话点击,触发事件NarrativeControls.Get().onPressTalkMouse += OnClick;}
}//检查actor是否距离靠近等事件
void Update();

2.2.6 NarrativeEffect

收到event后的各种处理效果,可以叠加多个。类型很多,一般和UI无关。

public class NarrativeEffect : MonoBehaviour
{//类型public NarrativeEffectType type;//不同类型的处理public void Trigger(Actor player);
}

可处理的类型例子:

public enum NarrativeEffectType
{StartEvent = 20,
StartQuest = 30,
CancelQuest = 31,
CompleteQuest = 32,
PlayMusic=42,
GainGold=80,
OpenShop=84,
}

2.2.7 DialogueMessage

也是收到event后的处理结果数据,主要是弹出对话框。注意该类只有数据,UI由DialoguePanel显示。

public class DialogueMessage : MonoBehaviour
{//对应的参与者,可以是主角,也可以是NPCpublic ActorData actor;//对话框内容public string text;//获得textpublic string GetText();
}

3 任务系统使用

3.1 把DQManager拖到场景中

目的就是做各种初始化工作,加载各种预制件。特别是DQCanvas。

3.2 新建Quest和Actor

Quest代表任务,Actor代表任务参与者。这些都可以提前建立好。

在Project面板,Assets/Resources下,建两个目录。
Quests 和 Actors。
然后,Quests目录下,新建任务数据Quest。

为什么菜单就可以建立呢?
因为QuestData类,做了如下声明:

建立好了,把quest_id,任务图标,标题写上。

同理,Actor也创建一下。需要注意的是,一般只能有一个actor是is_player。代表玩家主角。

3.3 主角和NPC添加Actor组件,然后绑定数据

比如主角,这么添加:

其他NPC,也是同理。

3.4 NPC添加一个事件

我们可以在scene下,建一个DialogueQuest,来专门处理任务相关。
比如,现在就想让NPC等着主角来找它,说第一句话。

如上,建一个节点,加NarrativeEvent和NarrativeCondition组件。用于有条件的接收某个event。
比如,我们写的Event条件是,Near Actor,即,需要2个Actor靠近,才触发该event!

接着,在该节点下,建立处理办法,比如弹出对话框呀,完成某个任务呀啥的。

3.5 事件节点下,建多个子节点,作为处理办法

每个子节点,都是一个事件的处理办法。比如,一个FirstTalk节点,下面挂了3个子节点, Msg1, Msg2, Choices。分别对应2个对话框,和1个选择框。
Msg1子节点,做如下编辑,添加DialogueMessage组件,并填写对话内容。

又比如FirstYes节点,用于监听用户点击了上面的选择框的YES。并挂了3个子节点,对应Msg1,Msg2, Quest。分别对应2个对话框,1个任务启动。

按照上面的步骤,你已经可以让任务启动起来了,并且可以执行完一个任务,启动一个新的任务。

4 代码分析

下面分析任务系统的一些核心功能!

4.1 NarrativeEvent触发流程

以最初的任务,即一个NPC(Actor)等待一个主角(另一个Actor)靠近,从而发起第一次对话,来讨论流程。

4.1.1 Actor的Update函数,查询是否有其他actor靠近

如果有,触发所有trigger_type是NearActorNarrativeEvent

4.1.2 OnTriggerEvent添加事件到list

OnTriggerEvent判断是否条件满足(例如是否触发过),满足,调用

NarrativeManager.Get().AddToTriggerList(this);

4.1.3 NarrativeManager的Update函数,轮询List并处理

关键代码如下图。
Update函数,轮询查trigger_list是否大于0,如果是,则读取到最优的event,调用StartEvent函数。
来看一下StartEvent都干啥了。

public void StartEvent(NarrativeEvent narrative_event) {//1. 触发同在一个gameObject下的effectsnarrative_event.TriggerEffects();//2. NarrativeEventLine存了当前event挂载的gameObject的子gameObject,所挂载的其他effect, dialogueMessage等//存下来,Update将轮询,并一一触发foreach (NarrativeEventLine line in current_event.GetLines())event_line_queue.Enqueue(line);
}

比较简单,先触发挂在同一个节点下的effects。
然后,子节点的effects,记录在NarrativeEventLine,把它们添加到event_line_queue。 这样NarrativeManager的Update函数,又会轮询到,并做处理。

4.2 任务启动流程

4.1只提到了,event如何监听,以及如何处理。
处理最主要的,一个是任务状态修改,一个是弹窗。我们先看下任务状态如何修改,尤其是任务启动流程

4.2.1 NarrativeEvent收到trigger函数

上面4.1节,提到了narrative_event.TriggerEffects();,这就走到了NarrativeEvent的trigger函数。

public class NarrativeEffect : MonoBehaviour
{public void Trigger(Actor player) {if (type == NarrativeEffectType.StartQuest){QuestData quest = (QuestData)value_data;NarrativeManager.Get().StartQuest(quest);}}
}

很简单,判断是否要StartQuest,是的话,则调用NarrativeManager.StartQuest

4.2.2 NarrativeManager 调用StartQuest

这个函数也简单, 一方面,修改quest的状态为1(即任务启动)。
另一方面,onQuestStart类似于函数指针,指向了QuestBoxShowBox。 所以,onQuestStart.Invoke的效果,就是显示一个UI,提醒用户任务开始。

public class NarrativeManager
{public void StartQuest(QuestData quest){if (quest != null && !NarrativeData.Get().IsQuestStarted(quest.quest_id)){//把quest设置为开始NarrativeData.Get().StartQuest(quest.quest_id);//显示一个toast dialogue,提醒任务开始if (onQuestStart != null)onQuestStart.Invoke(quest);if (quest is QuestAutoData)((QuestAutoData)quest).OnStart();}}
}

4.3 任务弹窗显示

当启动任务或完成任务,会显示一个弹窗。

这个怎么实现呢?
首先,弹窗的UI,在前面2.1.2节提到的DQCanvas预制件制作好了(预制件在TheLoader脚本加载)。
打开看一下:

对应的脚本,在QuestBox。我们看下这个脚本。

onQuestStart指针,指向一个匿名函数,函数参数是quest,内容是显示一个弹窗。这和4.2.2节的代码,对上了!

4.4 任务面板的显示

首先,显示任务面板,只要一句话,就实现:

QuestPanel.Get().Show();

怎么实现的呢?
其实和4.3节差不多。
首先,DQCanvas的预制件,先画好UI。

然后,QuestPanel脚本,做好显示/隐藏即可。

一款简单好用的Unity任务系统相关推荐

  1. 手把手教你开发一款简单的AR软件

    文章最早发布于我的微信公众号中,欢迎关注公众号 Android_De_Home 获取更多干货资源. 本文为sydMobile原创文章,可以随意转载,但请务必注明出处! 这篇文章主要是分享怎么开发一款最 ...

  2. 使用Cocos开发一款简单的3D VR抓钱游戏

    使用Cocos开发一款简单的3D VR抓钱游戏 最近VR成为了一个新兴的热点,很多以前从事游戏开发的团队都在关注这个方向.如何在VR时代来临之际快速的掌握开发VR游戏的方法,这对于很多中小团队来说,是 ...

  3. 动手写一款简单的chrome天气插件

    极简天气 一款简单的chrome天气插件. github https://github.com/yohnz/weather 如图: 创建文件 新建weather文件夹,里面包含manifest.jso ...

  4. 如何玩转PDF?5款简单好用的PDF工具推荐

    在办公中会经常遇到PDF文件,也需要将PDF文件进行编辑和转换,但是对PDF直接编辑和转换是不行的,为了高效办公就需要借助工具来完成,很多人想问如何玩转PDF?今天就来给大家推荐5款简单好用的PDF工 ...

  5. Python只需要三十行代码,打造一款简单的人工语音对话

    @Author:Runsen 1876年,亚历山大·格雷厄姆·贝尔(Alexander Graham Bell)发明了一种电报机,可以通过电线传输音频.托马斯·爱迪生(Thomas Edison)于1 ...

  6. 分享8款简单大气的jQuery/CSS3图片特效

    自从jQuery问世以后,网页上就流行很多漂亮的图片效果,像淡入淡出或者焦点图等,但是,现在jQuery结合CSS3,我们可以打造更美观更实用的图片特效.下面分享8款简单而又大气的jQuery/CSS ...

  7. 一款简单易用的mp3录音软件

    MP3 Audio Recorder Mac版是Mac平台上的一款录音应用.MP3 Audio Recorder Mac版是一款简单的录音软件,它可以通过系统内置或外接麦克风将音频文件录制成高品质的M ...

  8. 一款简单实用的桌面电子邮件客户端

    为大家分享一款Gmail 客户端,Mia for Gmail for mac运行在菜单栏,支持添加多个Gmail帐户,你可以快速搜索.阅读和撰写电子邮件,当你收到电子邮件时,Mia for Gmail ...

  9. 一款简单微信小程序个人博客

    简介: 一款简单微信小程序个人博客.后端基于SpringBoot实现 网盘下载地址: http://kekewl.cc/7ZS91CtWjfu0 图片:

最新文章

  1. SAP中有关差异的一些概念
  2. 小程序对象不去重合并
  3. Py之moviepy:python库之moviepy的简介、安装、使用方法详细攻略
  4. pwn学习总结(二) —— 基础知识(持续更新)
  5. 【无标题】科大星云诗社动态20201206
  6. 设置表格边框为1px的方法
  7. leetcode 263. 丑数(Java版)
  8. 阮一峰react demo代码研究的学习笔记 - React.createElement
  9. 学习人工智能不走捷径,走大道的方式
  10. 光线求交加速算法:边界体积层次结构(Bounding Volume Hierarchies)3-LBVH(Linear Bounding Volume Hierarchies)
  11. 【报告分享】未来教育的技术空间研究报告.pdf(附下载链接)
  12. 帆软报表插件开发之fine-decision中的AccessProvider扩展
  13. 数据库中int类型存在空数据开发过程中model和dal层处理方法
  14. 解析Health端点数据获取异常数据
  15. 一、基础篇--1.3进程和线程-CountDownLatch、CyclicBarrier 和 Semaphore
  16. python+opencv简单人脸识别(源码)(有手就行)
  17. 基于Springboot+Vue开发建筑工地用料管理系统
  18. LidarSLAM(三):EVO- SLAM轨迹精度评价工具
  19. SAMBA配置 “你可能没有权限访问网络资源”的问题解决方法
  20. 基于机器学习的车牌识别系统

热门文章

  1. 苹果手机如何微信分身
  2. 百分点科技数据猿联合发布《2022年“3·15”晚会消费维权行业预测报告》
  3. wangEditor的基本使用及踩坑记录
  4. spring boot (tomcat) 指定使用ipv4 netstat -ntupl查询结果为tcp4(不是tcp6)
  5. 科大讯飞语音识别(获取音频流文件中文字)
  6. 用牛顿迭代法求方程的根matlab,牛顿迭代法求方程解 程序如下
  7. 全新一代迅捷文本格式转换器
  8. Vue(07)——slot插槽和自定义事件
  9. 阿里巴巴Java开发手册一周年最终版
  10. 初识 PS CS6(十)___用操控变形修改图像