最近每天猛猛搓游戏demo,刚好碰到游戏任务系统这个问题,花了三天时间终于折腾明白了这个任务系统是怎么个事遂写个devlog记录一下思路历程。
这个devlog会分成几个部分,这部分(Part1)我们就来设计一下这个任务系统的结构。
叠甲:非专业人士,内容仅供参考
一张UI设计
在开始之前,让我们思考这样一个问题:如果现在要让你为某个游戏设计一个任务系统,你会怎么做?
这个问题从表面上看可能非常简单。那无非就是加载任务、看玩家做了什么然后更新这些任务,再做点什么花里胡哨的UI特效,对吧?对...对吗?
那么,先让我们来想一想一个任务系统都有哪些职责吧
1.1 任务的储存和读取
首先肯定是储存和读取任务嘛。我们的任务系统总得先把任务读取进来才能管理它们。
那么问题来了:具体的任务数据应该怎么存储呢?一个任务里面有那么多描述、数据、关系、要求,怎么才能让它们以一种结构化的方式存在我们的游戏里呢?
这个时候就要请出这方面的专家了:JSON格式。
JSON是个什么东西呢?让我们看看大D老师怎么说
只需要知道它对于人和机器来说,读写都很简单就好了
JSON刚好能够满足我们的要求:既能被程序结构化解析,人也能轻松的看懂。那么我们就用JSON来存储任务吧
1.2 任务的结构
现在我们解决了怎么存的问题,那自然下一个问题就是“存什么”了。一个任务里面有什么呢?让我们看看其他游戏是怎么做的。
超级柱头2,有需要优先完成的子任务和最后完成的主任务
2077,一个任务的某个阶段可以包含多个子目标
我们可以大致总结一下任务在这些游戏里的表现:
一个任务由多个任务阶段组成。
以上面HD2为例,“建立通信上行链路”可以分为 和终端互动-拧信号塔-再次和终端互动 这三个阶段。
每个任务阶段中都存在一系列需要玩家完成的目标。
比如,和终端互动这个阶段就可以包含输入启动指令和升起信号塔这两个目标。
每个目标都有目标描述和完成条件。
当完成一定的目标以后,任务从一个阶段进入下一个阶段。
当完成了任务的所有阶段以后,这个任务就算完成了。
一个任务可以拥有一定的前提条件。只有当前提条件满足一定的规则时,这个任务才能被玩家接受(处于“解锁状态”)。
一个任务还可以拥有一个接取条件,只有当满足这个接取条件时,任务才算真正的被玩家“接受”了。
上述两种条件的区别可能会有点让人困惑。举一个例子:
一个“清剿某地区怪物”的任务可能需要玩家满足 “到达等级10”和 “探索过XX地区”这两个条件,才会在当地的冒险家协会NPC头顶上 显示有可以接取的任务。
当玩家和这个NPC 对话结束,这个任务才能正式被玩家所接受,进入到玩家的人物列表里。
在这个例子里,“到达等级10”和“探索过XX地区”就是这个任务的前提条件 ,“和冒险家协会NPC完成关于城郊怪物的对话”就是这个任务的接取条件。
到这里,任务内容我们已经总结的差不多了。但是为了让我们的任务系统能够正确的读入、分类和管理这些任务,我们还需要回答几个问题:
对于任务系统来说,没有什么简单可靠的方法来区分和辨别不同的任务呢?
游戏里会不会有主线任务、支线任务、委托任务等任务种类的区别呢?
为了回答上面这两个问题,我们可以再为任务添加两个属性:
每个任务都有自己唯一的任务ID。这样,只要通过判断ID,我们就能辨别不同的任务了。
每个任务都有一个任务类型。
现在,我们的任务结构大概看起来会像这样:
任务ID(必需)
任务类型(必需)
前置条件(多个,可选)
接取条件(一个,可选)
任务阶段(一个或多个,必需)
这个阶段的任务目标(一个或多个,必需)
目标描述
完成条件
本阶段的完成条件
现在,让我们来试着定义一下一个任务的JSON应该是什么样的吧
大概长这样...?
现在我们又遇到问题了
我们怎么知道一个前置条件(或者一个任务目标)完成了呢?完成标准千变万化,怎么把它们统统写成一个统一的结构呢?
现在我们只能支持通过“完成所有前置条件”(或者“完成所有目标”)来判定任务可用或者一个任务阶段完成。如果需要更复杂的逻辑呢?
举个例子:
假设进入最终决战任务需要:
1. 完成“打败不死人将军”或者“召唤恶灵”两个任务其一;
2. 角色等级达到100级;
3. 角色身上不能有“不洁”这个属性。
这种“前置条件之间的关系”就是 “任务前置条件的满足规则”;“任务阶段的满足规则”也是同理。
1.3 拆解“条件”
其实,不管是目标也好,还是前置条件也好,它们都可以被统一抽象为“条件”,也就是“需要某个特定的条件被满足”。
上面提到的第一个问题,就可以简化成一个根本问题:如何把一个“条件”拆解成一个可序列化的结构
游戏里有些什么条件呢?我们可以列举一下:
对于玩家角色自身属性的要求(如,等级)
需要玩家持有某个物品(如,“秘密房间的钥匙” / “逝去英雄的佩剑” / ...)
需要玩家完成某个前置任务
需要玩家进行某个互动(如“打开墙上的电闸”)
需要玩家完成某个对话 / 剧情过场(如“和老铁匠交谈”)
需要玩家击杀指定的NPC (如“杀死3个史莱姆”)
可以发现,这些条件都可以被抽象成一种 “做什么”+ 参数的形式。以上面列举的为例:
要求玩家等级 >= 5(等级, 5)
要求玩家持有3个“失落的残片”(拥有物品, 失落的残片, 5)
需要玩家完成某个前置任务(完成任务, <任务ID>)
需要玩家打开墙上的电闸(场景互动, 电闸)
需要玩家和老铁匠交谈(完成对话, 老铁匠, <对话ID>)
需要玩家杀死3个史莱姆(杀死敌人, 史莱姆, 3)
那么,我们就可以把“条件”抽象成一个这样的结构:
使用插件:CodeSnap
1.4 拆解“完成逻辑”
现在,我们解决了条件怎么存的问题,那么条件之间逻辑的问题又该怎么解决呢
让我们再回到上面提到的例子:
进入最终决战需要:
1. 完成“打败不死人将军”或者“召唤恶灵”两个任务其一;
2. 角色等级达到100级;
3. 角色身上不能有“不洁”这个属性。
按照上一节的内容,这个要求可以拆分成四个条件:
A: 完成任务, 打败不死人将军
B:完成任务, 召唤恶灵
C: 等级, 100
D: 属性, 不洁
那么最终的条件就可以抽象成“A或者B选一个,并且需要C,并且不能有D”。
前有数学,接下来布尔逻辑很有用。让我们来问问大D老师
想象一下一个电灯开关,它只有两种状态:
开 - 灯亮着
关 - 灯熄灭了
布尔逻辑就是一套专门处理这种“非黑即白”、“是或否”情况的规则。
在计算机世界里,我们用: True (真) 代表“是”、“开”、“成立” False (假) 代表“否”、“关”、“不成立”
什么是布尔表达式?
布尔表达式就是一个最终会得出 True 或 False 的提问或式子。你可以把它想象成一个只能回答“是”或“否”的问题。
一些简单的例子:
今天下雨了吗? → 答案只能是 是 或 否。
我的银行余额大于100元吗? → 答案只能是 是 或 否。
这个苹果是红色的吗? → 答案只能是 是 或 否。
在编程中,这些表达式看起来像这样:
10 > 5 (10大于5吗?)→ 结果是 True
age >= 18 (年龄大于或等于18岁吗?)→ 根据变量 age 的值,可能是 True 或 False
name == "张三" (名字是“张三”吗?)→ 根据变量 name 的值,可能是 True 或 False
布尔逻辑的三大基本操作
为了组合这些“是/否”问题,我们使用三个最基本的逻辑运算符:AND, OR, NOT。
1. AND (逻辑与,符号通常是 && 或 and)
含义: 所有条件 都必须 为真,结果才为真。
口语化: “并且”
例子: 我想找一个“高” **并且** “富有” **并且** “帅”的人。
在这里,三个条件(高、富有、帅)必须全部满足,结果才是 True。
只要有一个不满足(比如不高),结果就是 False。
2. OR (逻辑或,符号通常是 || 或 or)
含义: 至少有一个条件为真,结果就为真。
口语化: “或者”
例子: 进入酒吧需要出示“身份证” **或者** “护照”。
在这里,你只要有身份证 或者 有护照 或者 两者都有,结果就是 True。
只有当你两样都没有时,结果才是 False。
3. NOT (逻辑非,符号通常是 ! 或 not)
含义: 取反。把真的变成假的,假的变成真的。
口语化: “不/不是”
例子: `NOT` 下雨 (即“不下雨”)
如果 下雨 是 True,那么 不下雨 就是 False。
如果 下雨 是 False,那么 不下雨 就是 True。
一个综合的生活实例
想象一个自动门开启的条件: 如果 (有人靠近 **并且**NOT是宠物) **或者** 有人按了开门按钮,那么门就打开。
我们用布尔逻辑来分解一下:
有人靠近 是一个布尔表达式(True/False)
是宠物 是一个布尔表达式(True/False)
NOT 是宠物 就是“不是宠物”
有人靠近 AND (NOT 是宠物) 就是“有人靠近并且不是宠物”
有人按了开门按钮 是另一个布尔表达式
最终,如果第4步的结果 或者 第5步的结果为真,门就会打开。
有了这个,我们就可以把上面那个条件表达成这样一个简单的式子:(A || B) && C && !D
把ABCD字母替换成对应的条件ID,我们现在就可以明确的定义任意复杂的完成逻辑了。
让我们完善一下之前的JSON:
策划看完头都大了(不是
看起来没什么问题
下一篇就写怎么在UE5里面实现这个任务系统吧。
感谢看到这里,喜欢的可不可以点点关注喵
更多游戏资讯请关注:电玩帮游戏资讯专区
电玩帮图文攻略 www.vgover.com