游戏任务系统设计:从分类到代码实现的完整指南
游戏任务系统设计:从分类到代码实现的完整指南
任务系统是游戏的核心驱动之一,它不仅为玩家提供了明确的游戏目标,还通过完成任务带来的奖励和成就感,极大地提升了游戏的可玩性和吸引力。本文将从任务分类、基础数据定义、系统设计等多个维度,详细介绍游戏任务系统的实现方案,并通过具体的代码示例,帮助读者更好地理解任务系统的开发过程。
1. 任务的分类
任务的需求就不多说了,先说下之前的SLG 游戏的任务类型。
根据任务类型分为 主线任务,支线任务,公会任务,势力任务,每日任务,还有各种不同的功能根据需求的任务类型
根据进度类型分为 累计型进度,递减型进度,达到型进度,激活型任务进度,自定义类型进度等等。
任务是游戏里比较复杂的系统,所以设计的时候应该尽可能的方便扩展,在增加任务的时候能简单的配置出来。
总结下设计原则:尽可能的解决问题,如果特殊需求可以有接口简单可以实现。
让我们操练起来吧!
2. 基础数据的定义
配置是基础数据,是策划配置的,因此最简单的配置就是
任务id 一 任务达成条件一奖励
任务id 是测试自己创建唯一的id,任务达成条件是在游戏内任务的判断条件,当达到条件的时候发给玩家奖励。
3. 系统的设计
根据任务的需求,我们对任务进行拆分。
根据进度类型进行拆分:
4. 代码实现
对任务拆分之后,现在得想办法实现我们的任务系统了。
首先先解决任务进度定义的问题:
1. 任务进度定义:
package org.pdool.task;
import com.google.common.base.Joiner;
public enum TaskProgress {
/**
* 格式:x_n
* 专指完成某一任务几次(完成一次扣一次)
* eq:送花{n}次
*/
PROGRESS_TYPE1 {
@Override
public String checkProgress(int roleId, String config, String current, Object paramArr) {
String configId = config.split("_")[0];
Object[] ext = (Object[]) paramArr;
String param = ext[0].toString();
long var = Long.parseLong(param);
int needNum = Integer.parseInt(current.split("_")[1]);
if (needNum > var) {
return "";
}
return Joiner.on("_").join(configId, needNum - var);
}
},
;
/**
* 检测任务进度
* @param roleId 玩家id
* @param config 配置的任务条件
* @param current 当前的任务进度
* @param param 传进的参数
* @return 当前的任务进度
*/
public abstract String checkProgress(int roleId, String config, String current, Object param);
}
2. 定义任务类型枚举
/**
* @author 香菜
*/
public enum TaskTypeEnum {
// 1:送花 eg:1_100
SEND_FLOWER_1(TaskProgressEnum.PROGRESS_TYPE1, "1"),
;
// 任务进度处理类型
private TaskProgressEnum progressType;
// 任务事件激活类型
private String taskType;
TaskTypeEnum(TaskProgressEnum taskType, String actType) {
this.progressType = taskType;
this.taskType = actType;
}
public TaskProgressEnum getProgressType() {
return progressType;
}
public String getTaskType() {
return taskType;
}
}
策划可以配置的任务就是 1_100 表示 送花100 次则完成任务。
3. 定义任务模块枚举
举个例子,我们的创角七日任务,在服务器启动的时候加载就加载基础数据,并且注册到每日任务的处理器上,这样在有任务过来的,就不会分配到其他的任务上,因此我们实现一个处理器的抽象类,一个任务类型枚举。
/**
* @author 香菜
*/
public enum TaskHandlerEnum {
// 主线任务
MAIN_TASK(1),
// 日常任务
DAILY_TASK(2),
;
private int type;
public int getType() {
return type;
}
private TaskHandlerEnum(int type) {
this.type = (byte) type;
}
}
4. 定义任务处理器的抽象类
至少得三个方法,一个构造函数,一个handle方法,一个注册方法。
/**
* @author 香菜
*/
public abstract class AbsTaskHandler {
public TaskHandlerEnum handlerEnum;
private Map<TaskTypeEnum, Set<Integer>> canHandMap = Maps.newHashMap();
public AbsTaskHandler(TaskHandlerEnum handlerEnum) {
this.handlerEnum = handlerEnum;
}
/**
* 注册任务到处理器
* @param taskType
* @param taskId
*/
protected void registerTask(TaskTypeEnum taskType, int taskId) {
Set<Integer> taskIdSet = canHandMap.computeIfAbsent(taskType, k -> Sets.newHashSet());
taskIdSet.add(taskId);
}
/**
* 每种任务类型单独实现
* @param roleId
* @param paramMap
*/
protected abstract void handle(int roleId, Map<TaskTypeEnum, Object> paramMap);
}
5. 实现主线任务处理类
import java.util.Map;
public class MainTaskHandler extends AbsTaskHandler {
public MainTaskHandler(TaskHandlerEnum handlerEnum) {
super(handlerEnum);
}
@Override
protected void handle(int roleId, Map<TaskTypeEnum, Object> paramMap) {
// do what you want
}
}
6. 在服务器启动的时候将所有的处理器加入到map
Map<TaskHandlerEnum ,AbsTaskHandler> handlerMap
7. 服务器启动后加载基础数据,将对应的任务注册到对应的处理器
registTaskToHanlder(TaskHandlerEnum,TaskType,TaskId)
8. 通知任务系统做检测
sendMsgToTask(TaskType,param)
遍历所有的处理器,能处理这个这个任务,则处理,不能处理则过
5. 总结
由于任务系统的复杂性,设计上必然有一些挑战,但是这种系统如果理解了,也都是套路的问题,做的久了自然而然就会了,并且可以根据需求自由的调整,理解最重要。
基本的介绍了任务系统的设计实现,后面一些没有做具体的展示,大家可以自己发挥,如果觉得有困难,并且又感兴趣的小伙伴可以联系我。或者你有其他的更好的设计我们可以一起交流。坚持写不容易,希望能获得大家的支持,点赞,转发 三连,谢谢。