Unity任务系统笔记

任务类的数据结构设计

基本字段:

string 任务内容;

任务目的地(Vector3或transform,transform更直观);

有的还有任务的详细描述。

基本方法:

开启任务:把这个任务加入任务列表;

完成任务:把这个任务移出任务列表;

触发、完成的条件

任务触发和完成的条件都很多样:对话、战斗、取得物品、到达地点、刺杀等。要触发任务,需要那些可能触发任务的行为能指向任务,这些行为都要加上可能触发的任务字段。触发任务可能是特定行为也可能是一个任务完成。也就是说任务系统不是只加一个系统,还需要对其他系统的行为加回调。

触发、完成的效果

开启和完成任务会有各种效果:某些NPC的出现、消失、移动位置,某些NPC更新可触发的对话、开启下一个任务、生成敌人。

讨论到这里可以发现触发、完成任务很相似,所以能不能把它们做成相同的结构,把可能的效果都包括进去?

[Serializable]
    public class MissionEffects{
        [Tooltip("要更新对话的NPC")]
        public NPCTalk npcTalk;
        [Tooltip("相关NPC要说的对话")]
        public TalkData triggerTalk;
        [Tooltip("要移动的NPC")]
        public List<MoveNPC> moveNPCs;
        [Tooltip("要触发的无线电通话")]
        public List<MonologData> radioTalk;
    };

对话完成任务

任务开启后相关NPC需要记录所属的任务,玩家找ta触发对话时完成任务,且把这个任务变量置空,防止再次对话再次完成任务。为此NPC对话脚本要加一个任务变量。

对话触发任务

则需要在对话结束后开启任务,然后NPC不能再触发这段对话,可能是触发任务进行中的对话,或者没有对话。

和上面触发对话完成任务加起来,对话的开始和结束都要加一个UnityEvent。

战斗完成任务

字段:要消灭的敌人列表;

敌人列表的每个敌人也记录自己所属的任务,每个敌人死时,把敌人移出敌人列表,然后判断列表是否为空,若是,则任务完成。

由于任务是依次触发的,一个关卡的任务的数据结构可以是1.链表,每个任务记录自己完成后下一个任务。但是这样一个关卡的任务先后顺序不直观;但是这样支持多任务分支;2.列表,一个中心任务管理器有一个任务列表,这样不支持多任务分支。然而我做了任务列表界面,也就是我一直想做多分支,这就和使用关卡流程任务列表冲突了。现在问题就是,没有一个图形化的表示多分支关卡的工具,用一连多的“链表”做多分支很混乱。所以在有合适工具前,只能先放弃做多分支。

有一个列表记录此关依次要执行的任务对象,关卡流程会清楚很多:

但是多任务分支用列表就不行了。

两种对话

对话分为两类:能反复触发的对话,对关卡没有推进的对话(简称氛围对话);对关卡有推进,不同阶段触发不同对话的npc(简称推进对话)。

对于推进对话,在关卡不同阶段有不同对话,在一个任务进行中触发同一个对话(如“拜托你了”),实际上出现了完成任务和进行中的多分支。这么多对话的存储位置有几种方案。

1.用List全部记录在NPC对象上。由任务对象指定完成后NPC该说哪一段对话。这样NPC对象脚本的检查器上会存一大堆对话数据。也难以看出一段对话对应哪个任务,是触发任务的对话还是任务进行中的对话。

2.NPC上只记录一段对话数据,就是当前去找ta会触发的对话。任务对象上记录两段对话,任务触发、进行中要说的对话。当关卡进度管理器显示该触发一个任务时,任务对象把触发任务对话写入npc脚本的对话变量,任务触发后把任务进行中对话写入npc对话变量。

这样氛围npc因为没有任务系统修改ta们的对话数据,自然就一直触发同一段对话,不用修改。

很明显2是最优方案,既防止推进进度的npc的脚本里的对话数据过多,又兼容两种npc,而且不推进进度的npc只有一段对话,给ta们声明一个一段对话的List完全是浪费。总之对于氛围NPC,只有一个一段对话变量,对于推进NPC,不同任务阶段对话不同,对话应该记录在任务对象。

问题:回复时开启任务 ,直接把下一个任务NPC要说的话写入了NPC的对话数据,导致当前对话变成下一个任务阶段的

需要在回复时记下要触发任务,这段对话结束后再开启任务。所以在一段对话数据结构里放一个List<UnityEvent>,回复的结构体里有一个UnityEvent用于在检查器配置回调函数,选择该回复时把这个UnityEvent加入列表,对话结束后执行。

问题:如何标记一段对话是氛围的还是推进的?

氛围对话可以反复触发,一般没有回调。推进对话只能触发一次,触发后执行回调。那么有没有回调能作为一段对话是氛围还是推进的标记吗?会遇到一些问题:

  1. 如果回调是通过unityEvent.AddListener()添加的非永久监听,怎么知道有没有非永久监听?查了一下,要自己记录。
  2. unityEvent没有添加过监听也不一定是null,无法通过不添加监听然后判断unityEvent==null判定没有回调;

综上,没有准确方法通过UnityEvent判断有没有回调。最终我选择规定不使用AddListener()添加非永久监听,一律使用检查器添加永久监听,使用GetPersistentEventCount()>0判断有回调。这意味着不能用代码写“对话完成任务”的功能,而必须在检查器配置来完成。

然后“对话型任务”这个任务子类也没有必要存在了。

任务和对话的关系

然后我们发现任务触发可以更新对话、任务完成可以更新对话、对话可以触发任务、对话可以完成任务,还有对话的回调在对话开始还是结束时执行的问题。二者可以调用对方,现在是任务更新对话,对话要触发的下一个任务也记在任务里。面临一些选择问题,比如一个任务会给NPC写入一段对话,触发对话会在对话开始或结束时完成这个任务,完成任务的函数是写在任务完成的UnityEvent,还是对话开始或结束的UnityEvent?如果还要开启下一个任务,那么就是有两个函数要填入两个可能的的UnityEvent,选项变多了。

有多个子任务的任务

每完成一个子任务,提示一下,任务界面显示X/Y。比如子任务是移动到某区域,子任务需要触发器,完成后通知父任务,更新完成的子任务数,任务面板提示一下,更新任务列表显示。

父任务要能显示X/Y,要有子任务的列表,检查里面完成的数量和总数。

任务列表

要完成的功能有:

  1. 按一个键显示当前开启的任务列表,再按一次隐藏;
  2. 选中一个任务时,该任务选项高亮,显示任务详细描述,并把目的地标记设为显示为该任务的目的地;
  3. 开启任务时,把该任务加入列表,任务完成时把该任务移除;
  4. 开启任务时,弹出一个提示;
  5. 对追踪中任务的管理;

对追踪中任务的管理

  1. 完成的任务是追踪中任务时,追踪中任务要更新,任务列表有任务则把第一个设置为追踪中任务,没有则设null;
  2. 接取任务时,如果没有追踪中任务,则把这个设为追踪中任务;

任务对象在hierarchy里的层级

所有任务对象放在一个父对象下面比较清楚。但是一些任务在某目标对象的同一位置,作为目标对象的子对象然后reset位置比较方便。可以给任务加一个public Transform target,Start()时把自己的位置设置到目标处。

问题

协程冲突

在修改NPC位置等突变操作时我写了一个画面渐变为黑色,执行操作,再变透明的函数:

public UnityAction blackoutCallback;
[ContextMenu("画面变黑")]
    public void Blackout(){
        StartCoroutine(BlackoutCoroutine(blackoutCallback));
    }
    float fadeSpeed=.04f;

IEnumerator BlackoutCoroutine(UnityAction callback=null){
        while(blackBack.color.a<1){
            blackBack.color+=new Color(0,0,0,fadeSpeed);
            yield return 0;
        }
        if(callback!=null){
            callback.Invoke();
        }
        while(blackBack.color.a>0){
            blackBack.color-=new Color(0,0,0,fadeSpeed);
            yield return 0;
        }
    }

然后这个函数在ContextMenu调用时正常,但是完成任务调用时画面就不变透明了。NPC被正确移动了。然后在第二个while循环里加了个Debug.Log(),发现第二个while循环一直在执行,但是alpha值没有变。

 然后又在第一个while循环加了个打印,发现两个while循环都在一直执行。

在协程开头加打印,发现协程被执行了两次。因为一个很笨的错误。

 这说明写淡入淡出时如果以imag.color.a作为循环条件,如果在一个淡入淡出完成前开始另一个,两个协程就会打架,淡入淡出永远完不成。实际开发中如果无法避免一个淡入淡出进行中开始另一个,就根据计算好的循环次数,或者直接规定循环次数,并且循环结束后直接把alpha值写成目标值,因为如果不这么做就算循环能结束,最终的alpha会是一个半透明值。

改进后的淡入淡出:

int fadeStep=10;
    IEnumerator BlackoutCoroutine(UnityAction callback=null){
        for(int i=0;i<fadeStep;i++){
            blackBack.color+=new Color(0,0,0,1/(float)fadeStep);
            yield return 0;
        }
        blackBack.color=Color.black;
        if(callback!=null){
            callback.Invoke();
        }
         for(int i=0;i<fadeStep;i++){
            blackBack.color-=new Color(0,0,0,1/(float)fadeStep);
            yield return 0;
        }
        blackBack.color=new Color(0,0,0,0);
    }

对话完后要让屏幕渐变黑,把NPC移到下一个位置并能触发新对话,但是现在对话完后立即就打开交互,能触发下一段对话了

根据设计,对话后这一段对话失效,应该在屏幕变黑、把NPC移到新位置后再让下一个任务把对话赋值给NPC。当前的设计是对话结束后执行回调。

需要改成对话结束后当前对话立即失效,而新对话在屏幕变黑、移动NPC后生效。又不能给对话数据设null使对话失效,因为对话数据里包含对话结束的回调,里面是下一个任务和对话数据。我选择只把对话数据里的句子列表设null,而对话数据里的对话结束回调保留。触发对话时也判断对话数据的句子列表长度是否大于0。

为此,给对话结束回调加一个选项,是直接执行回调还是屏幕变黑执行回调。

### Unity 开发推荐笔记本配置 对于从事Unity开发工作的用户来说,选择合适的笔记本至关重要。为了确保流畅的开发体验以及高效的图形处理能力,以下是基于当前需求所建议的具体硬件规格。 #### 处理器 (CPU) 处理器的选择直接影响到编译速度和多任务处理效率。对于Unity开发者而言,Intel i7 或者更高性能级别的CPU是理想之选[^2]。这类处理器不仅提供了足够的核心数来支持复杂的计算任务,还拥有较高的频率以加快程序运行的速度。 #### 图形卡 (GPU) 考虑到Unity引擎对图形渲染的要求,配备NVIDIA GTX 1060或更高级别的独立显卡是非常必要的。这样的GPU可以提供强大的着色器性能和支持最新的DirectX版本,从而使得场景构建更加高效并能实现更好的视觉效果展示。 #### 内存 (RAM) 至少应具备8GB以上的内存容量;然而,鉴于现代项目规模日益增大,16GB甚至更多会更为合适。充足的内存有助于减少加载时间,并允许同时打开多个大型工程文件而不会造成系统崩溃。 #### 存储设备 固态硬盘(SSD)相比传统机械硬盘有着明显的优势,在启动应用程序、读取资源等方面都能显著提升响应速度。因此,建议选用带有快速NVMe SSD存储选项的游戏本作为首选目标之一[^1]。 #### 屏幕分辨率与尺寸 屏幕分辨率达到1920×1080像素以上可保证良好的显示质量,便于查看细节丰富的UI界面或是调试复杂代码逻辑时获得清晰视野。至于具体大小,则取决于个人偏好及便携性考虑因素。 综上所述,针对Unity开发用途挑选一台高性能游戏笔记本将是明智之举。它能够在满足日常编程工作的同时也兼顾娱乐休闲方面的需求。 ```csharp // 示例:查询计算机信息以便确认是否符合上述标准 SystemInfo.systemMemorySize; Debug.Log($"Total RAM: {SystemInfo.systemMemorySize} MB"); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值