How to Develop a Game
Data Driven Design(DDD)
PublishDate: 2025-06-01 | CreateDate: 2025-06-01 | LastModify: 2025-06-01 | Creator:ljf12825

Data-Driven Design(数据驱动设计)在Unity和游戏开发中是一种将逻辑与数据分离的编程范式,它强调用数据来控制行为和流程,而不是把所有逻辑写死在代码中

尤其在Unity这样一个组件化的引擎中,数据驱动设计可以提高灵活性、可扩展性、可复用性、可配置性,广泛用于角色配置、关卡编辑、AI行为、技能系统、特效系统、任务系统等

核心思想

传统方式

void Attack()
{
    if (type == "Fire") DoFireAttack();
    else if (type == "Ice") DoIceAttack();
}

上面是硬编码方式,未来加新类型必须改代码

数据驱动方式

{
    "attackType": "Fire",
    "damage": 10,
    "cooldown": 2.5
}

在代码中只需要

AttackConfig config = LoadAttackConfig("Fire");
Execute(config);

在Unity中的实践方式

层级方法描述
数据定义ScriptableObject / JSON / XML / Excel / CSV描述数据结构和配置项
数据驱动器MonoBehaviour / System / Manager根据配置文件动态生成游戏逻辑或行为
运行时绑定Addressable / Resources.Load / AssetBundle运行时加载配置和资源
编辑器扩展CustomEditor / ScriptedImporter使数据更好地可视化配置

实例

  1. ScriptableObject驱动角色技能系统
[CreateAssetMenu(fileName = "SkillData", menuName = "GameData/Skill")]
public class SkillData : ScriptableObject
{
    public string skillName;
    public float damage;
    public float cooldonw;
    public GameObject effectPrefab;
}

技能执行器

public class SkillExecutor : MonoBehaviour
{
    public SkillData skill;

    void Cast()
    {
        Debug.Log($"Casting {skill.skillName}")
        Instantiate(skill.effectPrefab, transform.position, Quaternion.identity);
    }
}
  1. JSON配置敌人AI行为
{
    "enemyType": "Goblin",
    "health": 100,
    "aggressive": 3,
    "attackRange": 3,
    "patrolPath": ["(0,0)", "(5,0)", "(5, 5)"]
}

通过反序列化加载为结构体

public class EnemyData
{
    public string enemyType;
    public int health;
    public bool aggressive;
    public float attackRange;
    public Vector3[] patrolPath;
}

运行时构建敌人

public class EnemySpawner : MonoBehaviour
{
    void Spawn(string jsonPath)
    {
        string json = File.ReadAllText(jsonPath);
        EnemyData data = JsonUtility.FromJson<EnemyData>(json);
        GameObject enemy = Instantiate(enemyPrefab);
        enemy.GetComponent<EnemyController>().Init(data);
    }
}

与Addressable / AssetBundle搭配

如果使用Addressable / AssetBundle管理资源,可以通过配置文件映射资源路径,使得整个资源系统也数据驱动

{
    "prefabName": "EnemyGoblin",
    "addressableKey": "Enemies/Goblin"
}

运行时动态加载

Addressables.LoadAssetAsync<GameObject>(config.addressableKey);

Unity中数据驱动设计的典型应用场景

优势

注意

常见的配置表格式

格式优点缺点
CSV简单、轻量、跨平台不支持嵌套结构,数据易出错
Excel (XLS/XLSX)支持复杂结构,便于策划编辑Unity运行时读取需处理依赖或转换
JSON可嵌套、结构清晰,跨语言文件体积相对大,读取开销稍大
XML结构化强,支持嵌套比JSON繁琐,不够灵活
ScriptableObjectUnity原生支持,性能高策划不易编辑,编辑器依赖强
二进制(自定义格式)高效、节省空间可读性差,调试困难

CSV(Comma-Separated Values)

CSV是一种纯文本格式,用逗号,分隔数据,用于表示表格结构的数据

id,name,hp,attack,defense
1001,Warrior,500,70,30
1002,Archer,300,100,10

规则

列1,列2,列3
值1,值2,值3
值4,值5,值6

要素组成

元素说明
分隔符一般是英文逗号 ,,也可以是 ;\t(Tab)等,称为“分隔符”
换行符每一行代表一条记录,通常使用 \n\r\n 结尾
字段一行中的每个值称为字段,可以是数字、文本等
表头第一行通常是字段名(列名)
引号如果字段中包含逗号、引号或换行,应使用双引号 " 包住
转义符字段中出现 ",需要写成 ""(两个双引号表示一个双引号)

建议

CSV的特点

优点说明
简单直观可以用 Excel、Google Sheets 打开、编辑
小文件比 JSON、XML 体积更小,加载快
易于版本控制文本文件可直接使用 Git 管理
策划友好策划可以直接编辑,不依赖 Unity 编辑器
缺点说明
不支持嵌套结构不能直接表示数组、对象等复杂结构
容易出错格式不规范容易出错(多写一个逗号、漏引号)
类型信息丢失所有字段都是字符串,需自行转换成 int、float 等
转义麻烦逗号、换行、双引号等内容要转义
Unity中如何读取CSV

示例CSV文件(Resource/Configs/Character.csv)

id,name,hp,attack,defense
1001,Warrior,500,70,30
1002,Archer,300,100,10

对应的C#类

public class ChaaracterConfig
{
    public int id;
    public string name;
    public int hp;
    public int attack;
    public int defense;
}

CSV读取工具(简化版)

using System.Collections.Generic;
using UnityEngine;

public class CSVReader
{
    public static List<CharacterConfig> ReadCharacterConfigs(stirng path)
    {
        List<CharacterConfig> configs = new List<CharacterConfig>();
        TextAsset data = Resources.Load<TextAsset>(path);
        string[] lines = data.text.Split('\n');

        for (int i = 1; i < lines.Length; i++)
        {
            if (string.IsNullOrWhiteSpace(lines[i])) continue;
            string[] values = lines[i].Trim().Split(',');
            CharacterConfig config = new CharacterConfig
            {
                id = int.Parse(value[0]),
                name = values[1];
                hp = int.Parse(values[2]),
                attack = int.Parse(values[3]),
                defense = int.Parse(values[4])
            };
            configs.Add(config);
        }
        return configs;
    }
}

使用方式

List<CharacterConfig> characters = CSVReader.ReadCharacterConfigs("Configs/Character");
建议

Excel(XLS/XLSX)

.xls 全称Excel Binary File Format,Excel 97-2003的旧格式,基于二进制,不支持新特性 .xlsx 全称Excel Open XML Format,Excel 2007之后的默认格式,基于XML,体积更小,支持更多功能

推荐使用.xlsx因为:

Excel格式的特点(用于配置表)
优点描述
结构清晰表格直观,易于管理字段
可读性好策划可以直接编辑,无需代码知识
支持复杂数据结构多 sheet、合并单元格、数据校验等
富格式信息可做备注、着色、隐藏行列,提升协作效率
可通过工具自动导出 CSV / JSON / ScriptableObject
缺点描述
Unity 运行时无法直接读取 .xls.xlsx,需要依赖插件或转格式
相对复杂文件体积比 CSV/JSON 大,处理也更复杂
跨平台读取麻烦Mono/IL2CPP 不原生支持解析 Office 格式
Unity中使用Excel的常见方式

推荐方式:Excel -> JSON/CSV -> Unity\

  1. 策划编辑.xlsx
  2. 程序通过工具自动导出为.json.csv
  3. Unity运行时解析.json.csv -> 转为C#对象
配套工具
  1. NPOI
  1. ExcelDataReader
  1. 自研导出工具链(Editor脚本)
弱类型

在用Excel配置表做游戏开发时,每一列的“数据类型”并不是自动明确的,因为Excel本质是弱类型的(所有单元格默认都是文本或数字),不像编程语言中那样有严格的类型系统
那Unity如何判断配置表中的每一列的类型是什么呢
常见方式

  1. 程序根据第一行或第二行手动指定类型

示例Excel表

namehpisBosstags
stringintboolstring[]
Goblin200falseweak

第一行为字段名,第二行为字段类型,第三行开始才是数据

  1. 程序根据字段的值自动推断类型 缺点:不可靠
  1. 通过外部配置文件指定类型 可以使用一个.json.xml文件作为类型描述文件
{
    "MonsterConfig":
    {
        "id": "int",
        "name": "string",
        "hp": "int",
        "isBoss": "bool",
        "tags": "string[]"
    }
}

常见字段类型说明

类型Excel中的写法Unity对应类型
int100int
float12.5float
booltrue / false / 1 / 0bool
stringhellostring
int[]12
string[]fireice
enumWarrior自定义 enum
object{“x”:1,“y”:2}需要自定义解析

JSON(JavaScript Object Notation)

在Unity游戏开发中,JSON是非常常见的配置表格式,用于轻量级的数据交换和加载配置数据

JSON是一种结构化的数据格式,它使用键值对(K-V)和数组(Array)来表示数据,语法简洁,易于阅读
示例:

{
    "id": 101,
    "name": "Goblin",
    "hp": 100,
    "skills": ["slash", "bite"],
    "isBoss": false
}
JSON数据类型
类型示例
数字123, -5, 3.14
字符串"hello", "Goblin"
布尔值true, false
数组[1, 2, 3], ["a", "b"]
对象{ "x": 1, "y": 2 }
nullnull
Unity中如何使用JSON

Unity中通过JsonUtilityNewtonsoft.Json来处理JSON

  1. JsonUtility(Unity自带,性能好,功能简单) 适合简单的数据结构(基础类型、List、数组、自定义[Serializable]类) 定义类
[System.Serializable]
public class Monster
{
    public int id;
    public string name;
    public int hp;
    public string[] skills;
}

解析JSON->对象

string json = {\"id\":101,\"name\":\"Goblin\",\"hp\":100,\"skills\":[\"slash\",\"bite\"]}";
Monster monster = JsonUtility.FromJson<Monster>(json);
Debug.Log(monster.name); // Goblin

对象->JSON

string newJson = JsonUtility.ToJson(monster, true); // true表示格式化输出

JsonUtility API

Static Methods

MethodDescriptionAddOn
FromJson<T>(string json)从JSON反序列化成对象
FromJsonOverwrite(string json, object objectToOverwrite)从JSON数据反序列化到现有对象不会新创建对象
ToJson(object obj ,bool prettyPrint = false)将对象序列化为JSON字符串prettyPrint为是否美化输出,true会有换行和缩进

注意事项

示例

using UnityEngine;
using System.IO;

[System.Serializable]
public class PlayerData
{
    public string playername;
    public int score;
}

public class SaverManager : MonoBehaviour
{
    privte string filePath;

    void Awake()
    {
        // 持久化路径(跨平台安全)
        filePath = Application.persistentDataPath + "playerData.json";
    }

    public void Save(PlayerData data)
    {
        // 1.序列化成JSON
        string json = JsonUtility.ToJson(data, true); // true = 格式化输出

        // 2.写文件
        File.WriteAllText(filePath, json);
        Debug.Log("保存成功: " + filePath);
    }

    public PlayerData Load()
    {
        if (File.Exists(filePath))
        {
            // 1. 读文件
            string json = File.ReadAllText(filePath);

            // 2. 反序列化
            return JsonUtility.FromJson<PlayerData>(json);
        }
        return nulll;
    }
}
  1. Newtonsoft.Json(功能强大,支持Dictionary、List、动态字段等) 需要安装Json.NET for Unity
    优点:支持Dictionary、泛型、多态等 缺点:外部库,序列化速度比Unity内置的略慢
using Newtonsoft.Json;

解析JSON->对象

Monster monster = JsonConvert.DeserializeObject<Monster>(json);

对象->JSON

string json = JsonConvert.SerializeObject(monster, Formatting.Indented);

示例

using UnityEngine;
using Newtonsoft.Json;
using System.IO;
using System.Collections.Generic;

public class SaveManagerNewton : MonoBehaviour
{
    private string filePath;

    void Awake()
    {
        filePath = Application.persistentDataPath + "/playerData.json";
    }

    public void Save(Dictionary<string, int> scores)
    {
        string json = JsonConvert.SerializeObject(scores, Formatting.Indented);
        File.WriteAllText(filePath, json);
    }

    public Dictionary<string, int> Load()
    {
        if (File.Exists(filePath))
        {
            string json = File.ReadAllText(filePath);
            return JsonConvert.DeserializeObject<Dictionary<string, int>>(json);
        }
        return new Dictionary<string, int>();
    }
}

XML

在Unity游戏开发中,XML也可以作为配置表格式,虽然现在主流是使用JSON或ScriptableObject,但XML仍然常用于:

优点缺点
可读性强、结构清晰文件体积较大
支持复杂嵌套解析性能差
C# 自带完整支持(XmlSerializer写法冗长,维护麻烦
与很多旧系统兼容不适合热更数据场景
示例

假设一个怪物配置文件monsters.xml,内容如下

<Monsters>
 <Monster>
  <ID>101</ID>
  <Name>Goblin</Name>
  <HP>100</HP>
  <Attack>30</Attack>
 </Monster>
 <Monster>
  <ID>102</ID>
  <Name>Orc</Name>
  <HP>200</HP>
  <Attack>50</Attack>
 </Monster>
</Monsters>

创建C#数据类并添加[XmlElement]标签

using System;
using System.Xml.Serialization;
using System.Collections.Generic;

[Serializable]
public class Monster
{
    public int ID;
    public string Name;
    public int HP;
    public int Attack;
}

[XmlRoot("Monsters)]
public class MonsterList
{
    [XmlElement("Monster")]
    public List<Monster> Monsters;
}

Unity中读取XML文件

  1. monster.xml放到Assets/Resources/Data.monsters.xml
  2. 使用TextAsset加载,再用XmlSerializer解析
using UnityEngine;
using System.Xml.Serialization;
using System.IO;

public class MonsterXmlLoader : MonoBehaviour
{
    void Start()
    {
        // 1. 读取 XML 文件
        TextAsset xmlText = Resources.Load<TextAsset>("Data/monsters");

        // 2. XML 序列化器
        XmlSerializer serializer = new XmlSerializer(typeof(MonsterList));

        // 3. 反序列化为 C# 对象
        using (StringReader reader = new StringReader(xmlText.text))
        {
            MonsterList monsterList = serializer.Deserialize(reader) as MonsterList;

            // 4. 使用数据
            foreach (var m in monsterList.Monsters)
            {
                Debug.Log($"ID: {m.ID}, Name: {m.Name}, HP: {m.HP}, ATK: {m.Attack}");
            }
        }
    }
}

ScriptableObject

ScriptableObject

二进制

在Unity游戏开发中,二进制格式配置表是一种高效的配置数据存储加载方式,常用于:

二进制配置

将原本的结构化数据(如JSON/XML/CSV)序列化为字节流(byte[]),再通过代码反序列化回来

常用二进制序列化方法
技术说明特点
BinaryFormatterUnity 自带(不推荐使用)不安全,已过时
ProtoBufGoogle 提出的高效协议快速、跨平台,适合大型项目
MessagePack新一代高性能格式小巧、快、Unity 支持好
自定义格式自己写 byte[] 结构极致优化,维护成本高
Unity中简单的二进制序列化方法(BinaryFormatter示例)

虽然BinaryFormatter被标记为不安全,但对于离线开发或工具链仍可使用

  1. 定义数据类并标记[Serializable]
[System.Serializable]
public class Monster
{
    public int ID;
    public string Name;
    public int HP;
}
  1. 将数据写入二进制文件(在编辑器工具中使用)
using System.IO;
using System.Runtime.Serialization.Formatter.Binary;
using UnityEngine;

public class SaveToBinary
{
    public static void Save(Monster data, string path)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        FileStream stream = new FileStream(path, FileMode.Create);
        formatter.Serialize(stream, data);
        stream.close();
    }
}
  1. 在Unity中加载.byte文件 将二进制文件保存为Assets/Resources/Data/monster.bytes
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;

public class LoadBinary : MonoBehaviour
{
    void Start()
    {
        TextAsset binaryFile = Resources.Load<TextAsset>("Data/monster");
        MemoryStream stream = new MemroyStream(binaryFile.bytes);

        BinaryFormatter formatter = new BinaryFormatter();
        Monster m = (Monster)formatter.Deserialize(stream);

        Debug.Log($"{m.ID} - {m.Name} - {m.HP}");
    }
}
二进制格式 vs 文本格式
对比项文本(CSV/JSON/XML)二进制(.bytes)
可读性人类可读不可读
加载速度
体积大小较大小(压缩)
安全性易被篡改可加密、难修改
调试方便可直接打开不可直接看内容
跨平台通用标准格式需统一解码方案

使用二进制的场景\

配置表的加载方式

  1. 编辑器预处理加载
  1. 运行时加载
  1. 资源管理系统配合适用

数据结构与访问

示例
角色属性配置

[
    {
        "id": 1001,
        "name": "Warrior",
        "hp": 500,
        "attack": 70,
        "defense": 30
    },

    {
        "id": 1002,
        "name": "Archer",
        "hp": 300,
        "attack": 100,
        "defense": 10
    }
]

对应的C#结构体

[System.Serializable]
public class CharacterConfig
{
    public int id;
    public string name;
    public int hp;
    public int attack;
    public int defense;
}

加载方式

CharacterConfig[] configs = JsonUtility.FromJson<Wrapper<CharacterConfig>>(jsonText).list;

辅助封装类

[System.Serializable]
public class Wrapper<T>
{
    public T[] list;
}

进阶做法

  1. 配置表导入工具链(Excel转JSON/ScriptableObject)
  2. 自动生成C#结构体和索引器
  3. 内存优化(如按需加载、只保留当前关卡配置)
  4. 热更系统集成(版本控制 + CRC校验)
  5. 编辑器工具扩展(策划可视化编辑器)