他人のシーン編集を見える化するぜ!

  • 5
    いいね
  • 0
    コメント

プロローグ

「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のコード

sample.py
#!/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はここを参考にしました

https://freedom-man.com/blog/python-falcon/

jsonのファイル記載と、読み込み系はここから参考に(Martin Thomaさんのコメント)

http://stackoverflow.com/questions/12309269/how-do-i-write-json-data-to-a-file-in-python

起動

gunicorn sample:api

sampleはファイル名sample.pyから、
apiは中にある変数名。

Unity側

UniRXはAsseetStoreからとってきて下さい。
別にUniRXを使わずにWWWを生で書いてもOKです。
こちらを書きます。Editorフォルダに入れなくてもいいですが、入れた方がいいかも?

MyAssetModificationProcessor.cs
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への投稿コードはこちらを参考にしました。

http://belhb.hateblo.jp/entry/2016/05/02/025713

ソースについて

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://naichilab.blogspot.jp/2014/08/unityunirxrest-apipost.html

実行

シーンを編集して保存してみて下さい。

http://127.0.0.1:8000/quote
にアクセス

Screen Shot 2016-12-23 at 9.02.22 PM.png

こんな感じのログが見えます。

最後に

  • 今回のはほんの小さなサンプルです。
  • 実際はデータベースに入れましょう。
  • quoteのURLもきちんとしましょうね。
  • ファイル名も今回sample.pyとかだったので名前きちんとしましょう。
  • 今回はローカルで行いましが実際はサーバを立てて置きましょう。
  • だれかキチンと作ったらシェアしてね。それか僕が作ったらAssetStoreに置くかも?
  • Prefabも対応するのもいいかも?
この投稿は Unity 2 Advent Calendar 201614日目の記事です。