unity任务系统

读取策划配置表

一般策划都是通过EXCEL来配置,如下图所示:
在这里插入图片描述

Unity读取EXCEL

游戏中并不是直接读取Excel配置,实际项目中一般都是将Excel转为xmljsonlua或自定义的文本格式的配置,这样不会让Excel打包时占据额外空间。

对于 Json格式,在 Excel中能够安装额外的插件,直接导出为 Json格式,当然也可以用 Python批量化处理,这里不赘述

读取json

我们将json文件都放入Resource文件夹中。但一般不推荐按这样做,因为Resource文件夹只读,而且打包时会全部进行压缩后打包。建议将资源打成AssetBundle放在StreamingAssets目录或服务器中,方便热更资源

string txt = Resources.Load<TextAsset>("task_cfg").text;

引入LitJson能帮助我们更好的配置 地址:https://hub.fastgit.org/LitJSON/litjson

使用时引入命名空间: using LitJson;

任务配置读取

LitJson提供了一个JsonMapper.ToObject<T>(jsonString)方法,可以直接将json字符串转为类对象,前提是类的字段名要与json的字段相同

/// <summary>
/// 任务配置结构
/// </summary>
public class TaskCfgItem
{
    public int task_chain_id;
    public int task_sub_id;
    public string icon;
    public string desc;
    public string task_target;
    public int target_amount;
    public string award;
    public string open_chain;
}

public class TaskCfg
{
	// 任务配置,(链id : 子任务id : TaskCfgItem)
	private Dictionary<int, Dictionary<int, TaskCfgItem>> m_cfg;
    
    // 单例模式
    private static TaskCfg s_instance;
    public static TaskCfg instance
    {
        get
        {
            if (s_instance == null)
                s_instance = new TaskCfg();
            return s_instance;
        }
    }

	// 读取配置
    public void LoadCfg()
    {
        m_cfg = new Dictionary<int, Dictionary<int, TaskCfgItem>>();

        var txt = Resources.Load<TextAsset>("task_cfg").text;
        // 直接转换为类对象
        var jd = JsonMapper.ToObject<JsonData>(txt);

        // 根据转换之后的类对象的个数循环
        for (int i = 0, cnt = jd.Count; i < cnt; ++i)
        {
            // 读取每个类对象,将其视作JsonData格式 
            var itemJd = jd[i] as JsonData;
            // 转换为任务配置对象
            TaskCfgItem cfgItem = JsonMapper.ToObject<TaskCfgItem>(itemJd.ToJson());

            // 任务配置中字典的相应操作
            if (!m_cfg.ContainsKey(cfgItem.task_chain_id))
            {
                m_cfg[cfgItem.task_chain_id] = new Dictionary<int, TaskCfgItem>();
            }
            m_cfg[cfgItem.task_chain_id].Add(cfgItem.task_sub_id, cfgItem);
        }
	}
    
    // 获取任务配置
    public TaskCfgItem GetCfgItem(int chainId, int taskSubId)
    {
        if (m_cfg.ContainsKey(chainId) && m_cfg[chainId].ContainsKey(taskSubId))
            return m_cfg[chainId][taskSubId];
        return null;
    }
}

在这里插入图片描述

任务数据的更新

目前只考虑客户端进行任务数据的管理,与服务端进行交互不在本篇的介绍范围内

public class TaskDataItem
{
    // 链id
    public int task_chain_id;
    // 任务子id
    public int task_sub_id;
    // 进度
    public int progress;
    // 奖励是否被领取了,0:未被领取,1:已被领取
    public short award_is_get;
}

打算采用的是PlayerPrefs进行持久化储存,虽说这会污染注册表,当时我们将所有任务的数据编写在一个string中,这样产生的影响就会少很多

public class TaskData
{
    public TaskData()
    {
        m_taskDatas = new List<TaskDataItem>();
    }
    
    public List<TaskDataItem> taskDatas
    {
        get { return m_taskDatas; }
    }
    // 定义存储数据的容器
    private List<TaskDataItem> m_taskDatas;


    // 从数据库读取任务数据
    public void GetTaskDataFromDB(Action cb)
    {
        // 正规是与服务端通信,从数据库中读取,这里纯客户端进行模拟,直接使用PlayerPrefs从客户端本地读取
        var jsonStr = PlayerPrefs.GetString("TASK_DATA");
        var taskList = JsonMapper.ToObject< List<TaskDataItem> >(jsonStr);
        for (int i = 0, cnt = taskList.Count; i < cnt; ++i)
        {
            AddOrUpdateData(taskList[i]);
        }
        cb();
    }

    // 获取某个任务数据
    public TaskDataItem GetData(int chainId, int subId)
    {
        for (int i = 0, cnt = m_taskDatas.Count; i < cnt; ++i)
        {
            var item = m_taskDatas[i];
            if (chainId == item.task_chain_id && subId == item.task_sub_id)
                return item;
        }
        return null;
    }

    // 添加或更新任务
    public void AddOrUpdateData(TaskDataItem itemData)
    {
        bool isUpdate = false;
        for (int i = 0, cnt = m_taskDatas.Count; i < cnt; ++i)
        {
            var item = m_taskDatas[i];
            if (itemData.task_chain_id == item.task_chain_id && itemData.task_sub_id == item.task_sub_id)
            {
                // 更新数据
                m_taskDatas[i] = itemData;
                isUpdate = true;
                break;
            }
        }
        if(!isUpdate)
            m_taskDatas.Add(itemData);
        // 排序,确保主线在最前面
        m_taskDatas.Sort((a, b) => 
        {
            return a.task_chain_id.CompareTo(b.task_chain_id);
        });
        SaveDataToDB();
    }
}

在这里插入图片描述

任务逻辑

使用Action cb回调是为了模拟实际场景中从服务端数据库读取数据(异步)的过程, 仅在客户端交互的情况可以直接忽略

public class TaskLogic
{
    public TaskLogic()
    {
        m_taskData = new TaskData();
    }
    
    // 先把TaskData作为成员变量,并提供一个数据属性taskDatas,方便访问
    private TaskData m_taskData;
    public List<TaskDataItem> taskDatas
    {
        get { return m_taskData.taskDatas; }
	}

    
    // 获取任务数据
    public void GetTaskData(Action cb)
    {
        m_taskData.GetTaskDataFromDB(cb);
    }
    
    // 更新任务进度
   	// 使用Action<int, bool>回调是为了模拟实际场景中与服务端通信(异步)
    // 处理结果会有个返回码ErrorCode(回调函数第一个参数),客户端需判断ErrorCode的值来进行处理,一般约定ErrorCode为0表示成功
    // 回调函数第二个参数是是否任务进度已达成,如果任务达成,客户端需要显示领奖按钮
    public void AddProgress(int chainId, int subId, int deltaProgress, Action<int, bool> cb)
    {
        var data = m_taskData.GetData(chainId, subId);
        if (null != data)
        {
            data.progress += deltaProgress;
            m_taskData.AddOrUpdateData(data);
            var cfg = TaskCfg.instance.GetCfgItem(data.task_chain_id, data.task_sub_id);
            if (cfg != null)
                cb(0, data.progress >= cfg.target_amount);
            else
                cb(-1, false);
        }
        else
        {
            cb(-1, false);
        }
    }
    
    // 领取任务奖励,解锁下一系列任务
    // cb用于与服务端交互,解决错误产生原因
    public void GetAward(int chainId, int subId, Action<int, string> cb)
    {
        var data = m_taskData.GetData(chainId, subId);
        if (data == null)
        {
            cb(-1, "{}");
            return;
        }
        if (data.award_is_get == 0)
        {
            data.award_is_get = 1;
            m_taskData.AddOrUpdateData(data);
            GoNext(chainId, subId);
            var cfg = TaskCfg.instance.GetCfgItem(data.task_chain_id, data.task_sub_id);
            cb(0, cfg.award);
        }
        else
        {
            cb(-2, "{}");
        }
    }
    
    // 开启下一任务
    private void GoNext(int chainId, int subId)
    {
        var data = m_taskData.GetData(chainId, subId);
        var cfg = TaskCfg.instance.GetCfgItem(data.task_chain_id, data.task_sub_id);
        var nextCfg = TaskCfg.instance.GetCfgItem(data.task_chain_id, data.task_sub_id + 1);

        if (data.award_is_get == 1)
        {
            // 移除掉已领奖的任务
            m_taskData.RemoveData(chainId, subId);

            // 开启下一个任务
            if (nextCfg != null)
            {
                TaskDataItem dataItem = new TaskDataItem();
                dataItem.task_chain_id = nextCfg.task_chain_id;
                dataItem.task_sub_id = nextCfg.task_sub_id;
                dataItem.progress = 0;
                dataItem.award_is_get = 0;
                m_taskData.AddOrUpdateData(dataItem);
            }

            // 开启支线任务
            if (!string.IsNullOrEmpty(cfg.open_chain))
            {
                // 开启新分支
                var chains = cfg.open_chain.Split(',');
                for (int i = 0, len = chains.Length; i < len; ++i)
                {
                    var task = chains[i].Split('|');
                    
                    // 更新子任务状态
                    TaskDataItem subChainDataItem = new TaskDataItem();
                    subChainDataItem.task_chain_id = int.Parse(task[0]);
                    subChainDataItem.task_sub_id = int.Parse(task[1]);
                    subChainDataItem.progress = 0;
                    subChainDataItem.award_is_get = 0;
                    m_taskData.AddOrUpdateData(subChainDataItem);
                }
            }
        }
    }
}

在这里插入图片描述

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的Unity任务系统代码示例: ```csharp using System.Collections.Generic; using UnityEngine; public class TaskManager : MonoBehaviour { private static TaskManager instance; public static TaskManager Instance { get { return instance; } } private List<Task> tasks = new List<Task>(); private void Awake() { if (instance != null && instance != this) { Destroy(this.gameObject); return; } instance = this; } public void AddTask(Task task) { tasks.Add(task); } public void RemoveTask(Task task) { tasks.Remove(task); } private void Update() { for (int i = 0; i < tasks.Count; i++) { Task task = tasks[i]; if (!task.IsFinished) { task.UpdateTask(); } else { tasks.RemoveAt(i); i--; } } } } public abstract class Task { protected bool isFinished; public bool IsFinished { get { return isFinished; } } public abstract void UpdateTask(); } public class ExampleTask : Task { private float timer; private float duration; public ExampleTask(float duration) { this.duration = duration; } public override void UpdateTask() { timer += Time.deltaTime; if (timer >= duration) { isFinished = true; } } } ``` 在这个示例中,我们有一个`TaskManager`类,它负责管理所有任务。每个任务都是一个派生自`Task`类的具体实现。您可以根据需要创建不同的任务类型。 在`Task`类中,我们定义了一个`isFinished`字段和一个`UpdateTask()`方法。任务的实现应该重写`UpdateTask()`方法,并在完成时将`isFinished`字段设置为`true`。 在`TaskManager`类中,我们存储了所有任务,并在每个帧上循环遍历它们。如果任务未完成,则调用其`UpdateTask()`方法。如果任务已完成,则将其从`tasks`列表中删除。 您可以使用以下代码将任务添加到任务管理器: ```csharp TaskManager.Instance.AddTask(new ExampleTask(5f)); ``` 这将添加一个名为`ExampleTask`的新任务,其持续时间为5秒。在这个示例中,我们使用了一个简单的计时器来模拟任务的完成。您可以根据需要替换此行为。 希望这个示例能帮助您开始编写自己的Unity任务系统代码!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值