プロローグ
「Advent Calendar2016Unity 2 Advent Calendar 2016」の記事だよ。
だれかが書こうとしてたけど、12月12日はとっくに過ぎてたんで上書きますw
もとの方はUniRxで書こうとしてたっぽいから、僕もUniRxも使うことにしますね。
前書き
チームでUnity使っていて、シーン編集をするときにいちいちチャット部屋で共有とか、他の人に確認するの手間かと思います。
それを見える化する方法を考えました。
今回使うもの
- Unity 5.5.0x1-CollabOreview
- UniRx 5.5.0 (Unityとほぼ同じだw)
- Python 3.5
- gunicorn 19.6.0
- Falcon 1.1.0
※今回DBは面倒なのでプレーンテキストにしますw
サーバ準備
API作るのにFalconが楽そうだったのでそうします。
PHP使いたい人はPHP使って下さい。
バーチャル環境
virtualenv-3.4 falcon_apps
source bin/activate
Falconとgunicorn入れるよ
pip install falcon gunicorn
Pythonのコード
#!/usr/bin/env python
# -*- coding:utf-8 *-
import falcon
import json
FNAME = 'text.json'
class QuoteResource:
def on_get(self, req, resp):
str = self.read()
# resp.body = json.dumps(str)
resp.body = str
def on_post(self, req, resp):
resp.status = falcon.HTTP_200
resp.content_type = 'text/plain'
resp.body = 'post!!'
# リクエストボディを読み込んで JSON を解析
body = req.stream.read().decode('utf-8')
print(body)
self.write(body)
# 本当はDBから書く
def write(self, data):
# 追記なのでa
with open(FNAME, 'a') as outfile:
json.dump(data, outfile, indent=4, sort_keys=True, separators=(',', ':'))
# これがないと改行がない
outfile.write('\n')
# 本当はDBから読む
def read(self):
content = ''
with open(FNAME) as fp:
for line in fp:
data = json.loads(line)
dic = json.loads(data)
msg = '{} が {} の {} を編集しました ({})'.format(dic['user'], dic['screenPath'] , dic['sceneName'], dic['time'])
print(msg)
content += msg + '\n'
return content
api = falcon.API()
api.add_route('/quote', QuoteResource())
Falconはここを参考にしました
jsonのファイル記載と、読み込み系はここから参考に(Martin Thomaさんのコメント)
起動
gunicorn sample:api
sampleはファイル名sample.py
から、
apiは中にある変数名。
Unity側
UniRXはAsseetStoreからとってきて下さい。
別にUniRXを使わずにWWW
を生で書いてもOKです。
こちらを書きます。Editor
フォルダに入れなくてもいいですが、入れた方がいいかも?
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UniRx;
public class MyAssetModificationProcessor : AssetModificationProcessor
{
[Serializable]
public class CustomModel
{
public string user;
public string screenPath;
public string sceneName;
public string time;
}
public static string[] OnWillSaveAssets(string[] paths)
{
// Get the name of the scene to save.
string user = "shinriyo";
string scenePath = string.Empty;
string sceneName = string.Empty;
foreach (string path in paths)
{
if (path.Contains (".unity")) {
scenePath = Path.GetDirectoryName (path);
sceneName = Path.GetFileNameWithoutExtension (path);
string msg= string.Format ("{0}が{1}パスの{2}ファイルを編集中です.",
user, scenePath, sceneName
);
Debug.Log (msg);
// Postリクエスト
// form要素設定
var obj = JsonUtility.ToJson(new CustomModel {
user = user,
screenPath = scenePath,
sceneName = sceneName,
time = DateTime.Now.ToString ()
});
WWWForm form = new WWWForm();
form.AddField("data", obj);
// ObservableWWW.Post ("http://127.0.0.1:8000/quote", form)
byte[] data = System.Text.Encoding.UTF8.GetBytes (obj);
ObservableWWW.Post ("http://127.0.0.1:8000/quote", data)
// タイムアウト時間設定.
.Timeout(TimeSpan.FromSeconds(30))
.Subscribe(result =>
{
try
{
// JSONをパース.
Debug.Log("ok");
}
catch (System.Exception e)
{
Debug.Log("JSONのパースに失敗しました.");
}
},
err =>
{
if (err.GetType() == typeof(System.TimeoutException))
{
Debug.Log("タイムアウトしました.");
}
else
{
Debug.Log("通信に失敗しました.");
}
});
}
if (sceneName.Length == 0)
{
return paths;
}
}
return paths;
}
}
これを記載して配置しておくだけで、スクリプトが勝手にPOSTしてくれます。。
Webへの投稿コードはこちらを参考にしました。
ソースについて
CustomModel
とかわざわざ作るの面倒なので、無名クラス(new {hoge="val1", bar="val2"}
)とかでできればよいのに!
Form
でやるとdata=
パラメータがくっついてしまう。
WWWForm form = new WWWForm();
form.AddField("data", obj);
ObservableWWW.Post ("http://127.0.0.1:8000/quote", form)
substringとかで一生懸命data=
を消したりとかやろうとしてしてしまいましたが、
今回のソースコードのようにbyte[]
にしました。
この時ここを参考
実行
シーンを編集して保存してみて下さい。
http://127.0.0.1:8000/quote
にアクセス
こんな感じのログが見えます。
最後に
- 今回のはほんの小さなサンプルです。
- 実際はデータベースに入れましょう。
- quoteのURLもきちんとしましょうね。
- ファイル名も今回sample.pyとかだったので名前きちんとしましょう。
- 今回はローカルで行いましが実際はサーバを立てて置きましょう。
- だれかキチンと作ったらシェアしてね。それか僕が作ったらAssetStoreに置くかも?
- Prefabも対応するのもいいかも?