【Unity 导出 WebGL 通过Linux宝塔的 Nginx 连接数据库】使用 UnityWebRequest 与 WebAPI {php服务} 执行mysql命令
- 前言
- 方案一,MySql.Data.MySqlClient
- 前置准备
- 数据库部分
- 代码部分
- 结论
- 方案二:创建中间php服务,通过UnityWebRequest调用,来执行mysql命令
- php 编写
- php 部署
- C# 代码编写
- A Native Collection has not been disposed, resulting in a memory leak.
- 其他我还遇到的一些问题
前言
- 做项目,需要
Unity
导出的WebGL
项目能进行与数据库交互,这里指能实现insert
命令即成功
项目已经成功部署在 腾讯云服务器下,通过Linux
宝塔的Nginx
部署。 - 于是通过
chatGPT
紧急学习了其他相关技术,来实现与数据库交互的功能。
方案一,MySql.Data.MySqlClient
前置准备
- 第一种方案比较简单,在
C#
代码直接导入
using System.Data;
using MySql.Data.MySqlClient;
第一个头文件在比较新的Unity Editor版本可自动直接导入,否则需要到具体的编辑器的路径处查找
编辑器的路径可以在UnityHub直接打开
- 头文件,需要在
Plugins
文件夹放置如下DLL
文件
MySql.Data.dll
I18N.CJK.dll
I18N.dll
I18N.West.dll
其中 MySql.Data.dll
在 这里下载,这里选择第三个版本下载才成功。
后面的四个文件一般在这些目录中:
- 然后,上述dll放入该文件夹下。常识性操作。
数据库部分
- 在
Linux
宝塔的该位置添加数据库
注意编码的区别
utf8mb4
在utf8
的基础上支持most bytes 4
,即那些emoji的编码
utf8
支持全国各种字符,包括中文
gbk
更加支持中文编码,其中gbk2312
只支持中文
注意我们这里选择utf8
否则后续会需要调整编码,比较麻烦。 - 记住这里的数据库名,用户名和密码。注意该账号和
root
账号密码之间是不同的。
- 然后在
Navicat
处,连接该数据库,并创建自己需要的表格。
可能第一次创建时连接失败或者其他别的什么问题,需要下面修改一下:
修改访问权限:
数据库定时备份:
放行端口:默认数据库使用3306
号端口。需要在Linux
宝塔以及腾讯云服务器处放行。
- 端口号在这里查看。
代码部分
- 代码部分很简单。引入必要的头文件后,在某个类中声明一个
MySqlConnection
连接变量,然后调用ConnectSQL()
代码,给定数据库名,端口号,用户名和密码然后连接。
QuerySet()
代码调用,执行sqlString
命令
using System.Data;using MySql.Data.MySqlClient;public static string dbName = "XXdb";public static string tableName = "XXT";public static string host = "IP";public static string uname = "aniDB";public static string psw = "your password";public static string port = "your port";private static MySqlConnection con;public void ConnectSQL() // 直接复制粘贴使用即可{try{string mySqlString = string.Format("Database={0};Data Source={1};User Id={2};Password={3};port={4}", dbName, host, uname, psw, port);con = new MySqlConnection(mySqlString);con.Open();}catch (Exception e){throw new Exception("服务器连接失败,请重新检查MySql服务是否打开。" + e.Message.ToString());}}public void CloseSql() // 直接复制粘贴使用即可{if (con != null){con.Close();con.Dispose();con = null;}}public void RawQuery(int stage, int val) // 为一个具体的插入例子{DateTime dateTime = DateTime.Now;string sqlstr = string.Format("INSERT INTO {0} VALUES('{1}',{2},'{3}','{4}','{5}',{6},'{7}') ", tableName, id, stage, user_name, college_name, tel, val, dateTime);QuerySet(sqlstr);}private DataSet QuerySet(string sqlString) // 直接复制粘贴使用即可{if (con.State == ConnectionState.Open){DataSet ds = new DataSet();try{MySqlDataAdapter mySqlAdapter = new MySqlDataAdapter(sqlString, con);mySqlAdapter.Fill(ds);}catch (Exception e){throw new Exception("SQL:" + sqlString + "/n" + e.Message.ToString());}finally{}return ds;}return null;}
结论
- 本地调用成功,数据库内数据成功插入
WebGL
项目中,数据库内数据插入失败
原因:unity在webgl的平台下无法支持直连MySql
方案二:创建中间php服务,通过UnityWebRequest调用,来执行mysql命令
- 哈哈,好绕,但是是
chatGPT
在我最无助的时候提供的一个看似可行的方案。
但我之前也没学过这俩玩意儿啊
没事,一步一步来
php 编写
- 通过
chatGPT
的引导,拥有了这么一份index.php
文件
<?php$servername = "your IP";
$username = "your user name";
$password = "your password";
$dbname = "your db name";// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);// 检查连接是否成功
if ($conn->connect_error) {die("连接失败: " . $conn->connect_error);
}// 从HTTP请求中读取JSON数据
$json_data = file_get_contents('php://input');// 将JSON数据解码为PHP数组
$data = json_decode($json_data, true);// 插入数据到数据库表格
$sql = "INSERT INTO aniT VALUES ('".$data['id']."', '".$data['stage']."','".$data['college']."','".$data['tel']."','".$data['value']."','".$data['time']."')";
if ($conn->query($sql) === TRUE) {echo "插入成功";
} else {echo "Error: " . $sql . "<br>" . $conn->error;
}// 关闭连接
$conn->close();
?>
- 上述代码通过
httpRequest
传入json数据,然后转成$data
php数组,然后组合成$sql
mysql语句(一个字符串),最后执行。
- 第一次测试,可能对于大量代码测试比较麻烦,建议第一次使用下述代码进行测试
<?php
phpinfo();
?>
php 部署
- 部署真是头疼。
首先确保你的Linux
宝塔下载了该PHP
,这个5.X
的版本其实也能用。
- 打开配置文件,我们目前只需要关注 FPM配置文件
查看listen = XXXX
,注意这里监听的不是端口而是一个sock
,复制该sock
- 打开
Nginx
配置文件,选择配置修改,往下翻到server
这一块
前面我们了解了location
对于html
的基本配置,也让我们成功打开了WebGL
项目
这里我们需要部署php
文件,又大相径庭了…
- 之前没有配置过的话,需要新加入这一段
fastcgi_pass
后面填写unix:
再加刚刚的监听sock
其他的照抄,不然我就是打不开…
location ~ \.php$ {fastcgi_pass unix:/tmp/php-cgi-56.sock;fastcgi_index index.php;fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;include fastcgi_params;}
- 然后我把
index.php
传到了 root 目录,即/www/wwwroot
目录下。
通过ip/index.php
可以尝试访问是否成功。
我遇到了如下的问题。
情况1:502 BadGate 估计是端口号没有放行,没有权限访问。
情况2:404 :超链接 direct 失败,一般是你文件放错目录,或目录配置错误,或二级目录输入错了
情况3:file not found 文件放错目录,或目录配置错误
情况4:403 forbidden 貌似是权限问题
(注:该php部署后打开就是类似一个网页,虽然它被叫做WebAPI,提供了其他的服务)
(但它貌似不是一个完整的WebAPI项目)
- 一般多次尝试,对配置文件了解一下,就可以访问成功了。
C# 代码编写
- 到这里我们再返回
Unity
,在脚本中编写一些代码
我们需要使用UnityWebRequest
来访问上述的php
服务。
首先需要导入新的头文件using UnityEngine.Networking;
代码部分如下:
public void RawQuery(int stage, int val) // 一个测试函数{StartCoroutine(Insert(url, id, stage, college_name, tel, val, DateTime.Now));}// 具体的插入myqsl命令的函数IEnumerator Insert(string url, string id, int stage, string college, string tel, int value, DateTime time){// 创建JSON数据string json = "{\"id\":\"" + id + "\",\"stage\":" + stage + ",\"college\":\"" + college + "\",\"tel\":\"" + tel + "\",\"value\":" + value + ",\"time\":\"" + time.ToString("yyyy-MM-dd HH:mm:ss") + "\"}";// Debug.Log(json);UnityWebRequest request = UnityWebRequest.Post(url, json);// 创建HTTP请求byte[] bodyRaw = Encoding.UTF8.GetBytes(json);request.uploadHandler = new UploadHandlerRaw(bodyRaw);request.downloadHandler = new DownloadHandlerBuffer();request.SetRequestHeader("Content-Type", "application/json");// 发送HTTP请求yield return request.SendWebRequest();if (request.result == UnityWebRequest.Result.Success){Debug.Log("Request successful!");}else{Debug.Log("Request failed!");}request.Dispose();}
- 那么问题又来了
疑惑一:WebGL
不是不支持IEnumerator
吗?这里为什么可以用?
(你小子,肝反复横跳是吧!)
好吧,经过我的思考,我得出了如下结论:
虽然WebGL
不支持IEnumerator
(大众风评,以及chatGPT的回复)
虽然WebGL
支持UnityWebRequest
(这点在unity官方文档中也有提出)
但是UnityWebRequest
支持IEnumerator
(WTF?)
好吧,代码和我有一个能跑就行。 - 疑惑二:那个
string json
的怪异格式
就是单纯变成了一个字符串存储的json,unity把该字符串json传到php中再后续的解析。
注意:对于varchar格式的数据,需要给引号,这也是常识。格式不对,后面你会遇到爆炸的问题(这里按下不表)。
A Native Collection has not been disposed, resulting in a memory leak.
- 疑惑三:
request.Dispose();
一开始chatGPT
没有让我写这句。
不加的话,你就会在运行阶段遇到(一直遇到)可爱的:
A Native Collection has not been disposed, resulting in a memory leak. ……
- 后面没有更多报错信息,需要
packManager
导入一个新的包com.entity
啥的,但是导入后确实可以看到了,发现就是UnityWebRequest.Post(url, json)
进去后报错了。
但是之后运行阶段又出了
RuntimeError: memory access out of bounds - gatsby development extremely slow
查看解决措施,貌似就是删掉没有用的dll
等文件,就可以解决。所以我又把这个包给删了… - 额,那么
A Native Collection has not been disposed, resulting in a memory leak. ……
的产生原因是什么呢?
有人提出,各种各样的问题都会导致这个bug。
情况一:UnityWebRequest
使用后没有Dispose
掉
情况二:mysql
语句语法错误(也就是我遇到的…) - 或者有博客推荐如下格式:
using(UnityWebRequest request = XXX){XXXX;
}
他们说这样退出的时候会自动使用 Dispose
方法,可能也行
其他我还遇到的一些问题
- 我发现
Unity
导出的WebGL
,其中InputField
,移动端点击后居然没有输入的键盘…
离谱,说好的WebGL
支持各种设备呢,结果只是名义上支持…
有说用虚拟键盘的,就是自己敲个代码之类的,居然还有人设置需要花100R
来下载…
我这里没办法,只能取消汉字输入,手写了一个点触数字输入的虚拟键盘了,寄。 - 即
WebGL
不支持移动端的横屏切换,输入键盘,基本的IEnumerator
,一些浏览器打不开,一些浏览器对音频播放不支持……唯一的优势也就是打开连接就能下载游玩吧。 - 我这里只需要
insert
命令。如果需要select
命令,就是需要POST
+GET
的,应该是差不多原理的,具体的也可以问问百度和ChatGPT
,使用C#中的dataSet
类型存储GET来的数据。