Unity 2018连接MySQL完整指南:从环境配置到避坑实战
2026/9/15 14:45:38 网站建设 项目流程

如果你已经要用 Unity 连接一次 MySQL,那你大概率不是在写单机 Demo。我做游戏后台项目时,第一个被要求实现的功能就是:Unity 客户端能直接读写数据库里的玩家存档和排行榜。当时 Unity 版本正好卡在 2018,网上资料又杂又乱,有的说需要第三方插件,有的说直接引 DLL 就行,我试了一圈踩了无数坑,最后才把这条链路彻底跑通。

这篇文章我就从 Unity 2018 这个具体版本出发,完整拆一遍连接 MySQL 的方案选型、环境配置、代码实现和发布阶段必须避开的坑。适合正在做排行榜、存档同步、数字孪生数据对接,或者单纯想搞明白"Unity 怎么和数据库打交道"的开发者。我先说清楚一个大前提:直连 MySQL 更适合局域网内部工具、原型验证和学习阶段,公网生产环境建议走中间层 API,这个后面会细讲,但直连的原理和代码,依然是你理解整个数据链路的最好切入点。

1. 先说结论:这个需求该不该直连 MySQL

1.1 直连的场景与门槛

很多人一听到"Unity 连数据库",第一反应是做个登录注册或者排行榜,这确实是典型场景。但实际项目里,需要这种能力的地方远不止这些。我做过的数字孪生展示项目中,Unity 负责接收设备数据、展示三维场景,同时要把设备状态和操作记录写到 MySQL;还有一些内部运营工具,直接用 Unity 做客户端,后端就一个 MySQL,连管理中心都能省掉。

直连 MySQL 的技术门槛其实不高,你需要的基础是:C# 语法基础、SQL 的增删改查、以及一点网络概念(IP、端口、连接字符串)。但门槛不高不代表没有坑,最大的坑集中在驱动版本、Unity 脚本运行时版本、以及打包平台差异上。尤其是 Unity 2018 这个版本,它正好处在 .NET 3.5 到 .NET 4.x 过渡的时期,很多人连接失败,第一原因就是没切脚本运行时。

1.2 三套连接方案对比,我为什么选这套

Unity 连接 MySQL 成熟方案大致有三类,我做了个对比表,方便你根据自己项目情况选。

方案优点缺点适用场景
MySql.Data.dll 官方驱动直连SQL 功能完整、代码直观、能掌握底层原理依赖 DLL 版本、公网部署有安全风险、WebGL 不能用局域网工具、原型验证、学习链路
第三方封装库(如 UnityMySQL 等)API 简单、上手快功能受限、维护不活跃、底层还是包了 MySql.Data简单 Demo 教学
HTTP 中间层 + UnityWebRequest安全、跨平台、WebGL 也可用需要额外写后端接口、多一次网络请求生产环境、公网、多端

我最终选择第一套方案,不是因为第二套第三套不好,而是因为直连方案能让我在最短时间内验证完整链路:Unity 发起查询 → C# 驱动构造 SQL → MySQL 执行 → 结果回传 → UI 展示。这套流程跑通后,你对数据流向的认知是完整的,以后切换到中间层方案,只需要把数据库操作挪到服务端,Unity 侧改成发 HTTP 请求,思路几乎不用变。

1.3 Unity 2018 版本的特殊性

Unity 2018 是个分水岭。从 2018 版本开始,Unity 加入了 .NET 4.x Equivalent 脚本运行时的支持,这意味着你可以使用较新版本的 MySql.Data 驱动。如果你新建项目后不动设置,默认还是 .NET 3.5 Equivalent,那 MySql.Data 8.0 版本会在运行时直接抛一堆 TypeLoadException,很多新手在这个坑里卡一整天。

所以第一步必须去 Player Settings 里把 Scripting Runtime Version 切成 .NET 4.x Equivalent。如果项目确实古老,没法切,那就只能退而求其次用老驱动 MySql.Data 6.9.x。我的建议是,新项目直接用 Unity 2018.4 LTS + .NET 4.x + MySql.Data 8.0.x 这套组合,社区里验证过的人最多,遇到问题也最好查。

2. 环境准备:Unity 工程和 MySQL 两边都要动

2.1 Unity 工程侧配置:不切运行时会翻车

先打开 Unity 2018,创建或打开项目后,依次点击 Edit → Project Settings → Player → Other Settings,找到 Configuration 区域。这里有两个关键下拉框:Scripting Runtime Version 和 Api Compatibility Level。

Scripting Runtime Version 必须选 .NET 4.x Equivalent。这一步是大多数教程不会强调的,但恰恰是成败的关键。选完之后 Unity 会提示需要重启编辑器,照着做就行。Api Compatibility Level 同样要选 .NET 4.x,不要选 .NET Standard 2.0,因为部分 MySql.Data 版本对 Standard 2.0 的支持并不完整,在打包阶段容易出幺蛾子。

为什么这两个配置这么重要?因为 MySql.Data 8.0 官方要求最低 .NET Framework 4.5.2,Unity 2018 的 .NET 3.5 Equivalent 运行时压根不满足这个条件。如果你不切换,就算 DLL 导入成功,编译能过,运行时也会在实例化连接对象时立刻报错。我见过最典型的报错是 TypeLoadException: Could not load type 'System.Threading.Tasks.Task' from assembly 'mscorlib',就是这个原因。

2.2 MySQL 服务端配置:账号、权限、监听地址,一步都不能少

MySQL 服务端安装本身不复杂,社区版下载安装,选 Server only,设置好 root 密码即可。但要注意安装完成后默认的 bind-address 是 127.0.0.1,也就是说 MySQL 只监听本机回环地址,Unity 客户端在另一台机器或局域网内是永远连不上的。

修改方法:找到 MySQL 配置文件 my.ini(Windows)或 my.cnf(Linux),在 [mysqld] 段落下添加或修改这行:

bind-address = 0.0.0.0

保存后重启 MySQL 服务。这行配置的意思是让 MySQL 监听所有网络接口,而不是只监听本地。改完之后,还需要确认 Windows 防火墙是否放行了 3306 端口,如果没放行,外部连的时候会直接超时。如果用的是云服务器,还需要在安全组规则里放行 3306。

接下来是账号和权限。千万不要直接用 root 账号给 Unity 用,风险太大。正确姿势是创建专用账号,只给必要的库和必要的权限。我用到的 SQL 如下:

CREATE DATABASE IF NOT EXISTS game_db DEFAULT CHARACTER SET utf8mb4; CREATE USER 'unity_user'@'%' IDENTIFIED BY '这里换成你自己的强密码'; GRANT SELECT, INSERT, UPDATE, DELETE ON game_db.* TO 'unity_user'@'%'; FLUSH PRIVILEGES;

这里的 '%' 表示允许来自任意主机的连接,如果只在局域网用,更稳妥的方式是限制网段,比如 'unity_user'@'192.168.1.%'。权限方面只给了 SELECT、INSERT、UPDATE、DELETE,没给 DROP、CREATE 这类危险权限,这个习惯要从一开始就养成。

配置完成后,建议先用 MySQL Workbench 或 Navicat 验证一下:用 unity_user 连接数据库,试试能否查询。这一步能提前把服务端配置问题排除掉,免得后面写完了 Unity 代码,才发现根本不是代码的问题。

2.3 DLL 导入细节:几个版本坑一次说清

驱动文件是连接的关键,通常用 MySql.Data.dll。获取方式有两种:Visual Studio 里通过 NuGet 安装 MySql.Data 包,或者直接到官网下载 .NET Connector。我推荐在 Visual Studio 里建一个空项目,NuGet 安装 MySql.Data 8.0.x,然后从 packages 目录里把 net45 或 netstandard2.0 版本的 MySql.Data.dll 复制出来。

复制出来后,放到 Unity 项目的 Assets/Plugins 文件夹下。放在这个目录下的 DLL 会被 Unity 当作原生插件处理,打包时会自动包含,脚本也可以直接 using。但这里有几个细节要注意。

第一,MySql.Data 8.0 不是单文件依赖,它可能依赖 System.Buffers、System.Memory、Google.Protobuf 等,如果只复制主 DLL,运行时可能报找不到程序集。稳妥的做法是打开 NuGet 包目录,把 net45 目录下所有 dll 一起复制到 Plugins。如果服务端是 MySQL 5.6 或 5.7,我反而推荐用老一点的 MySql.Data 6.9.12,这个版本依赖极少,对 Unity 2018 兼容性也更好。

第二,如果项目里已经存在其他版本的 MySql.Data,或者有第三方插件自带了这个 DLL,一定要清理掉,否则会报程序集冲突,错误信息类似 Assembly 'MySql.Data' has different version than expected。我在项目里就遇到过和某个广告 SDK 冲突的情况,最后是删除重复 DLL 才解决。

第三,判断 DLL 是否导入成功,可以在 Unity 的 Project 窗口选中 DLL,看 Inspector 底部会显示平台兼容情况;或者写一行代码测试:Debug.Log(typeof(MySql.Data.MySqlClient.MySqlConnection).FullName)。如果能打印出完整类型名,说明驱动已经可用。

3. 核心代码实现:从查询到写入

3.1 连接字符串详解:每个参数都得知道干嘛的

连接字符串是 Unity 和 MySQL 之间唯一的"通信地址",格式如下:

string connectionString = "Server=192.168.1.100;Port=3306;Database=game_db;Uid=unity_user;Pwd=your_password;CharSet=utf8mb4;SslMode=none;Connection Timeout=5;";

逐个参数说下:

参数含义示例
ServerMySQL 服务器 IP 或主机名192.168.1.100
PortMySQL 端口,默认 33063306
Database要连接的数据库名game_db
Uid登录用户名unity_user
Pwd登录密码你自己设的密码
CharSet字符集,影响中文存取utf8mb4
SslModeSSL 模式,局域网可设 nonenone
Connection Timeout连接超时秒数5

SslMode 这里特别说一句,MySQL 8.0 服务端默认开启 SSL 支持,部分客户端驱动在 SSL 握手上有些玄学问题,直接连局域网时经常白白消耗时间。我实测下来,内网环境设 SslMode=none 能显著缩短握手时间,并且不影响功能。如果后续要公网上用,那另说,局域网内用 none 是没问题的。

Connection Timeout 我也建议显式设置。Unity 客户端最怕卡死,如果不设超时,一旦服务器不可达,数据库操作会阻塞很久,配合后面讲的异步处理,这个参数能确保快速失败。

3.2 查询方法:从 MySQL 拉数据到 Unity UI

接下来是重头戏,写一个简单的数据库访问类。我习惯把连接字符串写在私有字段里,后续所有数据库操作都走这个类的方法。最基础的查询代码如下:

using MySql.Data.MySqlClient; using System; using System.Collections.Generic; using UnityEngine; public class MySqlHelper { private string connectionString = "Server=192.168.1.100;Port=3306;Database=game_db;Uid=unity_user;Pwd=your_password;CharSet=utf8mb4;SslMode=none;Connection Timeout=5;"; public List<PlayerData> GetTop10Players() { List<PlayerData> result = new List<PlayerData>(); string sql = "SELECT id, nickname, score FROM player ORDER BY score DESC LIMIT 10;"; using (MySqlConnection conn = new MySqlConnection(connectionString)) { conn.Open(); using (MySqlCommand cmd = new MySqlCommand(sql, conn)) { using (MySqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { PlayerData data = new PlayerData(); data.id = reader.GetInt32("id"); data.nickname = reader.GetString("nickname"); data.score = reader.GetInt32("score"); result.Add(data); } } } } return result; } } public class PlayerData { public int id; public string nickname; public int score; }

这里有几个细节。using 语句一定要用,MySqlConnection 和 MySqlDataReader 都是非托管资源,不释放会导致连接池耗尽,后面会专门讲这个坑。读取字段时用 GetInt32("id") 这种按列名读取的方式比 reader[0] 可读性更强,也不容易因为 SELECT 语句顺序变化而出错。

如果只想快速看结果,可以做个 UI 绑定测试。在场景里创建一个 Text 组件,查询后把结果拼接成字符串赋给 Text.text。看到玩家数据在游戏界面里显示出来的那一刻,这条链路就算通了,后面所有复杂功能都是在这个基础上扩展。

3.3 写入方法:参数化 SQL 是底线,别用字符串拼接

写入数据的核心除了正确,还要安全。很多人在教学 Demo 里写这样的 SQL:

string sql = "INSERT INTO player (nickname, score) VALUES ('" + nickname + "', " + score + ")";

这种写法必须严令禁止。一方面存在 SQL 注入风险,用户输入特殊字符就能改变 SQL 语义;另一方面,如果 nickname 里带了单引号,这条 SQL 直接语法错误。正确做法是用参数化查询:

public void InsertPlayer(string nickname, int score) { string sql = "INSERT INTO player (nickname, score) VALUES (@nickname, @score);"; using (MySqlConnection conn = new MySqlConnection(connectionString)) { conn.Open(); using (MySqlCommand cmd = new MySqlCommand(sql, conn)) { cmd.Parameters.AddWithValue("@nickname", nickname); cmd.Parameters.AddWithValue("@score", score); cmd.ExecuteNonQuery(); } } }

AddWithValue 这个方法会自动处理类型转换和转义,单引号、反斜杠都不会破坏 SQL 结构。更新操作同理,UPDATE 语句里也全部走参数化。

如果你要循环插入大量数据,比如批量导入配置表,逐条执行会非常慢。简单优化方案是用事务包裹,或者拼接批量 SQL。我常用的是事务方式:

using (MySqlConnection conn = new MySqlConnection(connectionString)) { conn.Open(); MySqlTransaction transaction = conn.BeginTransaction(); try { foreach (var item in playerList) { string sql = "INSERT INTO player (nickname, score) VALUES (@nickname, @score);"; using (MySqlCommand cmd = new MySqlCommand(sql, conn, transaction)) { cmd.Parameters.AddWithValue("@nickname", item.nickname); cmd.Parameters.AddWithValue("@score", item.score); cmd.ExecuteNonQuery(); } } transaction.Commit(); } catch { transaction.Rollback(); throw; } }

事务的好处是一旦中间某条失败,之前所有插入都回滚,不会留下半截脏数据。

3.4 用协程或异步避免卡死:主线程不能阻塞

前面所有代码都是同步执行,这在编辑器里跑没问题,在真机上跑就会暴露问题:数据库操作是网络 IO,耗时可能是几百毫秒,如果 Unity 主线程被阻塞,游戏画面会直接卡住。尤其是移动端,用户点一下按钮,界面转圈好几秒,体验非常差。

Unity 的主线程是渲染线程,同时负责处理 MonoBehaviour 生命周期方法,你不应该把耗时的网络操作直接丢在主线程。解决办法是用 Task.Run 把数据库操作放到线程池,再用协程轮询完成状态,回到主线程更新 UI。

public class RankPanel : MonoBehaviour { public Text rankText; public void OnShowRankButtonClick() { StartCoroutine(LoadRankData()); } private IEnumerator LoadRankData() { MySqlHelper helper = new MySqlHelper(); List<PlayerData> result = null; string error = null; bool done = false; Task.Run(() => { try { result = helper.GetTop10Players(); } catch (Exception e) { error = e.Message; } finally { done = true; } }); yield return new WaitUntil(() => done); if (error != null) { rankText.text = "加载失败: " + error; } else if (result != null) { StringBuilder sb = new StringBuilder(); foreach (var player in result) { sb.AppendLine(player.nickname + " : " + player.score); } rankText.text = sb.ToString(); } } }

这里 WaitUntil 会每帧检查 done 标志位,一旦子线程跑完,协程继续执行,此时已经回到了主线程,可以安全地操作 UI。

Unity 2018 在切到 .NET 4.x 后也支持 async/await 写法,但如果你对线程上下文不熟悉,我建议先用协程方案,思路更直白,更适合 Unity 项目。等协程跑顺了,再尝试把数据库服务封装成异步接口也不迟。

4. 打包发布前必须知道的坑

4.1 IL2CPP 下的兼容性问题:提前验证再上

Unity 2018 打包 Android 或者 iOS 时,脚本后端可以选择 Mono 或 IL2CPP。真机和主流应用商店对包体大小和性能有要求,IL2CPP 更常见,但它也是直连 MySQL 时最容易翻车的环节。

IL2CPP 的工作方式是把 C# 代码转换成 C++,再编译成平台原生代码。这个过程对反射和动态代码生成支持有限,而 MySql.Data 的老版本驱动里恰好用了不少反射和动态类型判断逻辑。打包后运行到数据库相关代码时,可能会直接抛异常,常见报错有 ExecutionEngineException 或者 NotSupportedException。

我的建议是:如果项目只是开发阶段验证,打包到 Windows 用 Mono 就够了;如果确定要发 Android 且必须用 IL2CPP,开工前先建一个只包含数据库查询的最小工程,用 IL2CPP 打包到真机跑一次,确认驱动兼容性。实测下来,MySql.Data 8.0 在大多数 Android 设备上能跑,但偶尔也会碰到机型相关的问题,根本原因还是 IL2CPP 对某些 API 不友好,这种问题往往无处可查,最后只能换方案。

如果直连在目标平台实在跑不通,别死磕,退回到 HTTP 中间层方案。Unity 这边用 UnityWebRequest 请求后端接口,数据库操作全部在后端完成,打包到任何平台都没问题。

4.2 移动端和 WebGL 的差异性:不是所有平台都能直连

Android 和 iOS 上直连 MySQL,除了 IL2CPP 问题,还要注意网络权限。Android 需要在 AndroidManifest.xml 里声明 INTERNET 权限,Unity 一般默认会带,但如果你改了 Manifest 文件,一定要手动检查。iOS 上如果走的是 HTTP 而非 HTTPS,还要在 Info.plist 里处理 App Transport Security 设置。

WebGL 平台则基本不要指望直连。浏览器运行环境是沙箱机制,JavaScript 没有能力直接建立原始 TCP 连接,MySql.Data 压根无法在浏览器里工作。之前有朋友做 WebGL 版本的游戏,直连失败后卡了很久,最后用了 WebSocket 代理服务中转。这个思路可行,但复杂度远高于直接改 HTTP 中间层方案。

热搜词里有个 "unity 发布 webgl 使用 idbfs 写入失败",这跟数据库其实没关系,是浏览器文件系统的问题,但它和直连 MySQL 失败一样,都说明 WebGL 平台有很多边界限制。我的经验是:新的项目如果准备发 WebGL,从一开始就不要往数据库直连方向设计,直接把数据访问层设计成 HTTP API 形态,省得后面推倒重来。

4.3 数据安全与防护:客户端写死密码等于裸奔

Unity 客户端直连数据库,最大的问题不在技术,而在安全。打包出来的游戏容易被反编译,C# 代码配合 dnSpy 或 ILSpy 这类工具直接可以看到字符串常量,你写在连接字符串里的数据库密码相当于明文暴露。

之前我做过一个离线的项目,密码就直接写在代码里,后来被要求给客户做个内部演示版,我特意把连接字符串抽到了外部配置文件,虽然也谈不上绝对安全,但至少比硬编码强。对于生产环境,更彻底的方案是客户端完全不接触数据库,Unity 只调用后端接口,数据库密码只存在于服务端环境变量里。

除了密码问题,日常开发还要注意几点:数据库账号权限要最小化,不要给 Unity 账号 DROP 或 ALTER 权限;所有输入必须参数化;查询结果要校验类型,DataReader 里某字段为 NULL 时强行 GetString 会抛异常,用 IsDBNull 判断一下更稳。这些习惯不是洁癖,是帮你省掉线上事故的保障。

5. 常见问题排查:我踩过的雷都在这

5.1 连接失败的逐层排查清单

连接数据库失败是最常见的问题,报错千奇百怪,但排查路径是固定的。我从近到远整理了一个排查清单,遇到问题按顺序走一遍,大部分都能解决。

现象可能原因排查方法
Unable to connect to any of the specified MySQL hostsMySQL 没监听外部地址、防火墙拦截、IP 不通先 ping 服务器;再用 telnet IP 3306 测端口;检查 bind-address 和防火墙
Access denied for user 'unity_user'@'...'账号不存在、密码错误、host 不允许在 MySQL 里用 SQL 查看账号;确认 host 范围和密码
Unknown database 'game_db'数据库没创建或名字写错Workbench 里确认库名,注意大小写
Connection Timeout网络不通、端口被挡、连接字符串配置错误先用 Workbench 在同一网络环境下测试连接
Authentication method not supportedMySQL 8.0 默认认证插件与驱动不兼容详见下一节

最经典的现象是本机 MySQL Workbench 能连上,Unity 却连不上。这种情况九成是 bind-address 没改或防火墙没放行。用 netstat -an 查看 3306 端口监听地址,如果显示 127.0.0.1:3306 而不是 0.0.0.0:3306,那就是配置没生效。

5.2 MySQL 8.0 认证插件不兼容:一行 SQL 搞定

如果你用的是 MySQL 8.0 服务端,而驱动是 MySql.Data 6.9.x 或更老的版本,连接时会报这样的错误:Authentication method 'caching_sha2_password' not supported by any of the available plugins。

原因很简单:MySQL 8.0 默认的认证插件是 caching_sha2_password,而老版驱动只认识 mysql_native_password。解决办法是给 Unity 用户改回旧认证方式:

ALTER USER 'unity_user'@'%' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES;

执行完这行再连接就通了。如果是新项目,直接用 MySql.Data 8.0 版本驱动,天然支持新认证插件,就不需要改。

5.3 中文乱码:字符集不一致的锅

Unity 读出来的中文显示成问号或者乱码,原因通常是字符集没统一。MySQL 默认字符集可能是 latin1,而 Unity 的字符串是 UTF-8,两者转换出了问题。

我的做法是统一三处字符集:数据库、表、连接字符串。建库时指定 utf8mb4:

CREATE DATABASE IF NOT EXISTS game_db DEFAULT CHARACTER SET utf8mb4;

连接字符串里加上 CharSet=utf8mb4。如果用的老驱动不支持 utf8mb4,就改成 CharSet=utf8。注意,如果存储时就已经是乱码,光改连接字符串救不回来,得先清理数据,再从源头保证字符集。

排查乱码问题有个技巧:先用 Workbench 直接查询表里数据。如果 Workbench 显示正常,说明库里数据没问题,是 Unity 读取时解码错误,重点查连接字符串;如果 Workbench 显示也乱,说明写入时就错了,得检查写入方和表结构。

5.4 连接资源耗尽:你忘了释放连接

做 Unity 客户端最容易忽略的问题就是连接释放。早前我做测试时,反复打开关闭界面,某次突然报 Too many connections,一看 MySQL 最大连接数 151,全被占满了。

原因就是连接对象没释放。MySqlConnection 是托管对象,但它内部持有底层 socket,靠垃圾回收不可靠,必须手动 release。正确做法是每个连接都用 using 包裹,如下面这样:

using (MySqlConnection conn = new MySqlConnection(connectionString)) { conn.Open(); // 执行操作 }

using 语句会在作用域结束时自动调用 Dispose,连接会归还连接池,而不是真正关闭,后续复用成本很低。如果你用了异步操作,还要注意每次查询都 new MySqlConnection,不要复用一个已关闭的旧连接,容易踩到连接状态异常。

连接池参数也可以在连接字符串里调,比如加 Pooling=true;Connection Lifetime=30,让池里的连接合理回收。总之,内存泄漏可以靠工具查,连接泄漏就只能靠代码习惯防。

最后再分享点个人经验

这套直连方案,我前前后后用了半年多,后来项目规模变大,稳定性和安全性要求上来,最终我把数据库操作收拢到了后端服务,Unity 客户端只通过 HTTP 接口拿数据。但回头看,直连 MySQL 这段经历非常值钱,它让我彻底搞懂了连接字符串、SQL 执行、数据流传输的完整链路,后续用中间层方案时,很多后端接口设计都是在直连代码基础上改出来的。

如果你也想自己动手跑一遍,我的建议是:不要一上来就设计复杂的数据库结构,先建一张 player 表,字段就 id、nickname、score,把 SELECT 和 INSERT 跑通,再逐步扩展到更新、事务、异步。这个最小闭环走通了,后面所有扩展都只是加 SQL 语句而已。Unity 2018 连接 MySQL 不是个能用花哨技巧绕过去的问题,它是客户端工程师和数据库打交道的第一道门槛,跨过去之后,你会发现整个外部系统接入的思路都通了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询