Unity結合Flask實現排行榜功能
業餘做的小遊戲,排行榜本來是用PlayerPrefs
儲存在本地,現在想將資料放在伺服器上。因為功能很簡單,就選擇了小巧玲瓏的Flask來實現。
閒話少敘。首先考慮URL的設計。排行榜無非是一堆分數score
的集合,按照REST的思想,不妨將URL設為/scores
。用GET
獲得排行榜資料,用POST
新增一條新紀錄到排行榜。此外,按照慣例,排行榜的資料不需要更新和刪除。
Flask自身不支援REST,但我們可以通過route
和method
自己實現。下面建立一個原型版本的rank_server.py
。命名沿襲了Rails的習慣:
from flask import Flask app = Flask(__name__) @app.route('/scores', methods=['GET']) def index(): return 'index' @app.route('/scores', methods=['POST']) def create(): return 'create' if __name__ == '__main__': app.run(debug=True)
執行python rank_server.py
來啟動自帶的伺服器。下面我們安裝cURL
來測試應用。
brew install curl
測試GET
:
`curl -i -X GET 127.0.0.1:5000/scores`
測試POST
:
`curl -i -X POST 127.0.0.1:5000/scores`
-i
引數可以展示響應的頭部資訊,便於debug。-X
引數指定請求的方法method。
可以看到測試成功。
下面我們建立儲存資料的表。本地測試我們使用sqlite,之後部署使用mysql。
建表檔案create_rank.sql
內容如下:
DROP TABLE IF EXISTS rank; CREATE TABLE rank( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(255) NOT NULL, score INTEGER NOT NULL );
Mac自帶sqlite。執行下面語句匯入sql檔案:
sqlite3 rank.db < create_rank.sql
然後隨便插入幾條測試資料。如:
INSERT INTO rank (name, score) VALUES ('A', 100); INSERT INTO rank (name, score) VALUES ('B', 200); INSERT INTO rank (name, score) VALUES ('C', 300);
針對資料庫,我們在rank_server.py
中加入下面一段程式碼,用於在請求前後處理資料庫連線。
import sqlite3 DATABASE = 'rank.db' @app.before_request def before_request(): g.db = sqlite3.connect(DATABASE) @app.teardown_request def teardown_request(exception): if hasattr(g, 'db'): g.db.close()
我們規定伺服器和客戶端使用JSON
傳輸資料。
GET
請求返回的JSON
格式如下:
{ "data": [ { "id": 0, "name": "A", "score": 100 }, { "id": 1, "name": "B", "score": 200 } ] }
這裡的id
其實是自增主鍵,可以不必保留,但為了後面處理方便就一起保留了。
POST
提交的JSON
格式如下:
{ "id": 0, "name": "C", "score": 300 }
現在我們可以著手實現index
方法了:
def index(): cur = g.db.execute('select id, name, score from rank order by score desc;') result = cur.fetchmany(100) data = [] for row in result: data.append({'id': row[0], 'name': row[1], 'score': row[2]}) return jsonify({'data': data})
(其中jsonify
和g
在flask
模組內。後面不再對匯入進行說明,預設都是從flask
匯入。)
在查詢時對資料做了排序,並且只返回了前100條記錄。可以用curl
再測試一下。測試無誤再實現create
方法:
def create(): status = {'status': 'OK'} if not request.json or not 'name' in request.json or not 'score' in request.json: status['status'] = 'bad request' try: g.db.execute('insert into rank (name, score) values (?, ?)', [request.json['name'], request.json['score']]) g.db.commit() except: status['status'] = 'database error' return jsonify(status)
我們的POST
請求都是JSON
型別的,所以要從request.json
獲得,而不是args
或者form
。此外,返回了一個status
變數,便於檢視出錯原因。
再用curl
測試一下POST
。這次,我們要向POST
請求中加入資料:
curl -i -X POST -H "Content-Type: application/json" -d '{"id": 0, "name": "xyz", "score": "800"}' 127.0.0.1:5000/scores
-H
引數用於指定頭部資訊,-d
引數可以攜帶資料,這裡就是一條符合我們提交格式的JSON
資料。
現在伺服器端就(暫時)實現完了。下面該寫C#程式碼啦。
我們需要設計一個和伺服器互動、並返回資料給UI層的類。
首先,這個類應該是單例的,要繼承MonoBehaviour
(因為和伺服器互動要利用Coroutine
);而且最好獨立於場景之外。關於Unity中實現單例類的集中方式,請看我的另一篇文章。單例的程式碼如下:
private static SaveLoad _instance = null; public static SaveLoad Instance { get { if (_instance == null) { GameObject go = new GameObject("SaveLoadGameObject"); DontDestroyOnLoad(go); _instance = go.AddComponent<SaveLoad>(); } return _instance; } }
還需要定義一些常量:
const int recordsPerPage = 5; const string URL = "127.0.0.1:5000/scores";
定義一個資料結構:
public struct Data { public int id; public string name; public int score; }
在動手之前,還要了解兩個東西:WWW
類和LitJson
庫。WWW
類是Unity自帶的處理HTTP請求的類;LitJson
是一個C#處理JSON
的開源庫。要使用LitJson
,先從官網下載dll檔案,然後匯入Asset。
SaveLoad
類的功能就像名字一樣,包括儲存Save
和載入Load
。
public void Save(Data data) { var jsonString = JsonMapper.ToJson(data); var headers = new Dictionary<string, string> (); headers.Add ("Content-Type", "application/json"); var scores = new WWW (URL, new System.Text.UTF8Encoding ().GetBytes (jsonString), headers); StartCoroutine (WaitForPost (scores)); } IEnumerator WaitForPost(WWW www){ yield return www; Debug.Log (www.text); }
這裡建立WWW
例項,指定了URL、header和提交資料。第一行的JsonMapper
可以在物件和JSON
之間進行轉換,前提是物件中的屬性和JSON
中的鍵要保持一致。
public void Load() { var scores = new WWW (URL); StartCoroutine(WaitForGet(scores)); } IEnumerator WaitForGet(WWW www){ yield return www; if (www.error == null && www.isDone) { var dataList = JsonMapper.ToObject<DataList>(www.text); data = dataList.data; }else{ Debug.Log ("Failed to connect to server!"); Debug.Log (www.error); } }
Load
方法中是將前面index
方法返回的JSON
文字轉換成物件,這裡為了實現轉換,新建一個DataList
類,其中的屬性是List<Data>
。
到這裡,客戶端的讀取和儲存資料就實現了。其餘的邏輯,比如和UI的互動,在這裡就不寫了。感興趣的可以看我的小遊戲的完整程式碼。GitHub傳送門
最後談談部署的事情。如果要部署到SAE有幾點要注意:
- 程式碼要進行一定的修改以適應
MySQLdb
。 - 要注意中文的編碼。如用
unicode
方法轉換名字屬性,以及檔案頭部的:
# -*- coding:utf8 -*- #encoding = utf-8
最後說說比較坑的Unity跨域訪問的限制。在我成功部署後,curl
測試沒有問題了。結果Unity報了錯:
SecurityException: No valid crossdomain policy available to allow access
經過一番搜尋,原來要在伺服器的根目錄增加一個crossdomain.xml
檔案。檔案內容大致如下:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <site-control permitted-cross-domain-policies="master-only"/> <allow-access-from domain="*"/> <allow-http-request-headers-from domain="*" headers="*"/> </cross-domain-policy>
但是SAE好像不支援上傳檔案到根目錄。只能用Flask仿冒一下了:
@app.route('/crossdomain.xml') def fake(): xml = """上面的那堆內容""" return xml, 200, {'Content-Type': 'text/xml; charset=ascii'}
OK,大功告成!
相關文章
- Unity——計時器功能實現Unity
- Flask後端開發(二) - 功能實現和專案總結Flask後端
- Vue中結合clipboard實現複製功能Vue
- [Unity] Dreamteck Splines實現沿路徑移動功能Unity
- Flask-Login 讓實現登入功能變簡單Flask
- 使用Redis的有序集合實現排行榜功能Redis
- Celery #4 結合Flask和apscheduler使用Flask
- Redis 實用小技巧——如何實現一個排行榜功能Redis
- laravel cdn 結合七牛雲實現檔案自動同步功能Laravel
- flask框架如何實現修改密碼和免密登入功能Flask框架密碼
- Express 結合 Webpack 實現HMRwiExpressWeb
- 關於Unity 如何與Blazor Server結合UnityBlazorServer
- SpringBoot應用篇之藉助Redis實現排行榜功能Spring BootRedis
- springboot結合Redis實現工具類Spring BootRedis
- .Net Core結合AspNetCoreRateLimit實現限流NetCoreMIT
- 【SpringBoot】結合Redis實現快取Spring BootRedis快取
- 【Django】組合條件的搜尋功能實現Django
- spring boot 結合Redis 實現工具類Spring BootRedis
- React Native 結合友盟實現分享React Native
- Vue結合uniapp實現水平公告欄VueAPP
- 基於 flask 結合 Redis 的簡單聊天室FlaskRedis
- Vue結合Django-Rest-Frameword結合實現登入認證(二)VueDjangoREST
- Vue結合Django-Rest-Frameword結合實現登入認證(一)VueDjangoREST
- LRU快取-實現雜湊連結串列結合快取
- 用連結串列實現佇列的功能佇列
- 雙向連結串列的功能實現(初版
- Aspose.Words實現郵件合併功能和列印
- unity實現場景跳轉Unity
- 用Unity實現彈反效果Unity
- Flask SocketIO 實現動態繪圖Flask繪圖
- Unity 華為快遊戲JS橋接 實現寫日誌等功能Unity遊戲JS橋接
- 利用Vue3的axios+Python的flask實現前後端互動功能VueiOSPythonFlask後端
- 使用Flask-Dropzone在Flask程式中實現檔案上傳Flask
- Laravel 結合 Redis 實現 PHP 定時器LaravelRedisPHP定時器
- SpringCloud Fegin結合Ribbon實現負載均衡SpringGCCloud負載
- Windows+Pycharm+Flask+Vue+Element-Plus 前後端分離實現分寫查詢功能WindowsPyCharmFlaskVue後端
- 在Unity中實現手部跟蹤Unity
- 在Unity實現遊戲命令模式Unity遊戲模式
- 【unity】 Loom實現多執行緒UnityOOM執行緒