14.10 在Unity中连接数据库
应用程序收发数据通常有很多中方法,但其中最快、最常用的方法是连接数据库。虽然此处使用了“直接连接”这个说法,但实际上需要ODBC(Open Database connectivity,开放数据库互联)或者JDBC等中间件才能连接数据库。
如前所述,在Unity中也可以通过ODBC连接数据库,但实际开发中会根据平台不同而有所区别。如图14-116所示,也可通过Web服务器的服务器脚本(Server Side Script)连接到数据库并收发数据。各位熟知的PHP、ASP、JSP等都是服务器端脚本,实际开发中可用JSON、XML、CSV等格式收发数据。
14.10.1 JSON
JSON(JavaScript Object Notation)是收发数据的一种格式,主要用于轻量级数据。其结构虽然借鉴了Java的语法,但它独立于计算机编程语言或平台。另外,大部分服务器端脚本都支持解析或构建JSON格式的消息体。
JSON支持的数据类型有整数、字符串、布尔型、数组等。
下列示例就是保存游戏玩家信息的JSON消息体。
JSON格式消息以大括号({})表示开始和结束,然后在大括号内写入要识别的键名和键值。
其中的键值可根据JSON支持的数据类型决定。
JSON中,数组的开始和结束均有中括号([])表示。上述示例中,“拥有技能”键保存的键值为数组,包含"掩蔽","透明模式","穿甲弹"这3个字符串类型的值。
JSON支持嵌套子JSON结构体。
{
"姓名":"狙击手",
"性别":"男",
"职业":"狙击手",
"能力值":{
"等级":37,
"生命力":480,
"活力":270,
"法力":1200
},
"拥有技能":["掩蔽","透明模式","穿甲弹"],
"分数":2750000,
}
提示:关于JSON的详细内容请参考下列网址http://www.json.org/json-zh.html
14.10.2 SimpleJSON
如果想在Unity中使用JSON,需要可以分拆和构建JSON消息的JSON解析器。Unity中可以使用的JSON解析器有SimpleJSON、LitJSON、JSONObject、JsonFX等,本书使用的是SimpleJSON解析器。
SimpleJSON的使用方法较为简单,不需要复杂的C#高级语法。可在下列网站下载SimpleJOSN。
http://wiki.unity3d.com/index.php/SimpleJSON
下载的SimpleJSON.zip文件包括JSON Test文件夹、plugins文件、SimpleJSON.unitypackkage文件夹,只需plugins文件夹中的SimpleJSON.cs文件导入项目视图的Plugins文件夹即可。
制作新的场景并简单测试JSON。先将本节开头的JOSN示例重命名为user_info.json并保存,放入项目视图的Resources文件夹。
{
"姓名":"狙击手",
"性别":"男",
"职业":"狙击手",
"能力值":
{
"等级":37
,"生命力":480
,"活力":270
,"法力":1200
},
"拥有技能":["掩蔽","透明模式","穿甲弹"],
"分数":2750000,
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SimpleJSON;
public class LoadUserInfo : MonoBehaviour
{
public TextAsset jsonData = null;
public string strJsonData = null;
// Use this for initialization
void Start()
{
//加载Resources文件夹中的JSON文件
jsonData = Resources.Load
//将Json文件转化为字符串类型的文件
strJsonData = jsonData.text;
// Debug.Log(strJsonData);
//解析JSON文件
var N = JSON.Parse(strJsonData);
//获取key为姓名的键值
string user_name = N["姓名"].ToString();
//从能力值中获取key为等级的键值
int level = N["能力值"]["等级"].AsInt;
Debug.Log(user_name);
Debug.Log(level.ToString());
for (int i = 0; i < N["拥有技能"].Count; i++)
{
Debug.Log(N["拥有技能"][i].ToString());
}
}
将脚本添加到场景后执行,控制台视图中会出现如图14-118所示的日志信息。
14-118 解析JSON文件的结果
14.10.3 将分数信息保存到数据库
本节将之前坦克爆炸时的累积的分数保存到数据库,然后将其封装为JSON格式的文件并显示到画面。为了便于测试数据保存的内容,各位可以使用我的服务器以使用MySQL数据库。我为每个测试数据库都分配了一个序列号,这样可以避免各自的数据相互干扰。复制并打开下列网址,输入简单的信息再点击“生成账号”按钮,就会得到一个序列号。请复制此序列号。
http://www.unity3d.com/Tankwar/UserRegister.html
14-119 为了使用我的MySQL数据库而分配序列号
注意
考虑到运营服务器的成本,我会定期初始化保存的数据,所以此数据库只用于学习。另外,如果数据保存及查询时产生过多的流量,则测试账号的页面会暂时终止。
如脚本14-45所示编写脚本,将其命名为DataMgr,用于数据库中保存和查询数据。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DataMgr : MonoBehaviour
{
public static DataMgr instance = null;
//为了使用MySQL数据库而分配的序列号
private const string seqNo = "3201411232";
//保存分数的PHP地址
private string urlSave = "http://www.Unity3dStudy.com/Tankwar/save_score.php";
void Awkae()
{
instance = this;
}
public IEnumerator SaveScore(string user_name, int killCount)
{
WWWForm form = new WWWForm();
//设置要传递的参数
form.AddField("user_name", user_name);
form.AddField("kill_count", killCount);
form.AddField("seq_no", seqNo);
//调用URL
var www = new WWW(urlSave, form);
//等待完成
yield return www;
if (string.IsNullOrEmpty(www.error))
{
Debug.Log(www.text);
}
else
{
Debug.Log("Error" + www.error);
}
}
}
使用seqNo变量保存从图14-119额页面中获取的序列号,避免与其他读者的数据弄混。
//为了使用MySQL数据库而分配的序列号
private const string seqNo = "3201411232";
使用save_score.php将分数保存到数据库,php页面的源码如脚本14-46所示
//连接数据库
$connect = mysql_connect("localhost","用户名","密码");
//查看是否连接成功
if($connect==0)
{
?>SORRY SOCRE SERVER CONNECTION ERROR
}else
{
}
//设置要使用的数据库
mysql_select_db("数据库名称",$connect);
//以POST方式传递的参数保存到变量
$user_name = $_POST["user_name"];
$kill_count = $_POST["kill_count"];
$seq_no = $_POST["seq_no"];
//生成SQL查询语句
$sql = "INSERT INTO tb_score(user_name,kill_count,seq_no)";
$sql.="\n values("'.$user_name.'",".$kill_count.",".$seq_no.")";
$sql.="\n ON DUPLICATE KEY UPDATE kill_count = kill_count +1";
//执行SQL查询语句
$result = mysql_query($sql,$connect);
//返回查询语句的执行结果
if($result)
{
echo("YOUR SCORE SAVED.");
}else
{
echo("eroor");
}
//断开与数据库的连接
mysql_close($connect);
?>
DataMgr脚本制作完成后,将其添加到场景中的GameManager游戏对象。
如脚本14-47所示,将DataMgr脚本中定义的“向数据库保存数据”的函数添加到TankDamage脚本的IncKillCount函数。
void InKillCount()
{
++killCount;
if (texttKillCount != null)
texttKillCount.text = killCount.ToString();
Debug.Log(killCount);
if(DataMgr.instance!=null)
StartCoroutine(DataMgr.instance.SaveScore(gameObject.name, 1));
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.I))
InKillCount();
}
重新构建2个游戏可执行文件并运行,在不同游戏窗口中互相攻击,可以在控制台视图中看到诸如YOUR SCORE SAVED.的日志和执行的SQL查询语句,说明数据已经正常保存到数据库(此处使用键盘的I键为分数增加的模拟操作)。
14.10.4 从数据库获取排名信息
下面从数据获取保存的排名信息。从数据中查询数据,构建为JSON格式的消息并返回,然后在Unity中解析JSON消息以获取数据。我制作的PHP脚本源码如脚14-48所示。
//连接数据库
$connect = mysql_connect("localhost","用户名","密码");
//查看是否连接成功
if($connect==0)
{
?>SORRY SOCRE SERVER CONNECTION ERROR
}else
{
}
//设置要使用的数据库
mysql_select_db("数据库名称",$connect);
//以POST方式传递的参数保存到变量
$seq_no = $_POST["seq_no"];
//生成SQL查询语句
$sql = "SELECT ";
$sql.="\n @rownum:=@rownum+1 as ranking";
$sql.="\n ,A.user_name as user_name";
$sql.="\n ,A.kill_count as kill_count";
$sql.="\n FROM tb_score as A, (SELECT @rownum := 0) R ";
$sql.="\n where A.seq_no ='".$seq_no."'";
$sql.="\n ODDER BY kill_count DESC";
//执行SQL查询语句
$result = mysql_query($sql,$connect);
//保存JSON的数组
$rows = array();
$return = array();
//从开始到最终记录始终输出查询结果值
while($row = mysql_fetch_arrya($result))
{
$rows["ranking"] = $row["ranking"];//获取排名
$rows["user_name"] = $row["user_name"];//获取用户名
$rows["kill_count"] = $row["kill_count"];//获取分数
//向数组添加记录值以生成JSON数据
array_push($return ,$rows);
}
header("Content-type:application/json");
//生成JSON文件
echo json_encode($return);
//断开与数据库的连接
mysql_close($connect);
?>
向DataMgr脚本添加入脚本14-49所示代码,解析JSON格式的消息并获取、显示排名信息数据。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SimpleJSON;
public class DataMgr : MonoBehaviour
{
public static DataMgr instance = null;
//为了使用MySQL数据库而分配的序列号
private const string seqNo = "3201411232";
//保存分数的PHP地址
private string urlSave = "http://www.Unity3dStudy.com/Tankwar/save_score.php";
//可以获取排名信息的PHP地址
private string urlScoreList = "http://www.Unity3dStudy.com/Tankwar/get_score_list.php";
void Awake()
{
instance = this;
}
public IEnumerator SaveScore(string user_name, int killCount)
{
WWWForm form = new WWWForm();
//设置要传递的参数
form.AddField("user_name", user_name);
form.AddField("kill_count", killCount);
form.AddField("seq_no", seqNo);
//调用URL
var www = new WWW(urlSave, form);
//等待完成
yield return www;
if (string.IsNullOrEmpty(www.error))
{
Debug.Log(www.text);
}
else
{
Debug.Log("Error" + www.error);
}
StartCoroutine(this.GetScoreList());
}
public IEnumerator GetScoreList()
{
WWWForm form = new WWWForm();
//设置要传递的参数
form.AddField("seq_no", seqNo);
//调用URL
var www = new WWW(urlScoreList, form);
//等待完成
yield return www;
if (string.IsNullOrEmpty(www.error))
{
Debug.Log(www.text);
//调用显示分数的函数
DispScoreList(www.text);
}
else
{
Debug.Log("Error" + www.error);
}
}
//解析JSON文件并显示分数的函数
void DispScoreList(string strJsonData)
{
//解析JSON文件
var N = JSON.Parse(strJsonData);
//根据JSON对象数组大小循环
for (int i = 0; i < N.Count; i++)
{
int ranking = N[i]["ranking"].AsInt;
string userName = N[i]["user_name"].ToString();
int killCount = N[i]["kill_count"].AsInt;
//将结果值显示到控制台视图
Debug.Log(ranking.ToString() + userName + killCount.ToString());
}
}
}
本示例中,排名信息以JSON格式传递,脚本需要使用SimpleJSON,所以在DataMgr脚本的开始部分添加了using SimpleJSON以声明相应的命名空间。
将分数保存到数据库后,脚本访问了get_score_list.php以获取排名信息。Get_score_list.php中生成的JSON格式的消息如下所示。
[
{"ranking":"1","user_name":"Zack","kill_count":"17"},
{"ranking":"2","user_name":"Penzer",kill_count":"9"}
]
运行游戏,操作坦克互相攻击,可在控制台视图中看到从数据库获取的结果值,如图所示。
本博文参考:Unity5权威讲解,【韩】李在贤 著 孔雪玲 译,ISBN 978-7-115-43636-8