- 投稿日:2019-02-03T22:44:26+09:00
Unity+Firebase RealTimeDatabase Editor上で環境によっては突然接続できなくなる
経緯
コワーキングスペース的な所でドヤリングしながら、Unity+FirebaseRealtimeDatabaseでゲーム作ろうとしたら
「[Error] WebSocket: ws_0 - could not connect」 「[Error] WebSocket: ws_0 - WebSocketException during handshake Firebase.Database.Internal.TubeSock.WebSocketException: unknown host: ****.firebaseio.com ---> System.Net.Sockets.SocketException: No route to host」みたいに怒られてなんで?となったので記録しときます
結論
Realtime Database connection is flakey on desktop
https://github.com/firebase/quickstart-unity/issues/106#issuecomment-426835393desktop上(Unity Editor)だとネット環境によっては上手く接続できない事があるみたいです。
対策は以下
*利用している環境を変える(違うwifiやネット環境を利用する)
*「Firebase/Plugins/FirebaseDatabase.dll」をEditor上で有効にする(但し動作が不安定)再現環境
MacBook Pro (15-inch, 2017)
OS:10.13.6(17G5019) High Sierra
Unity: 2018.3.0f2
Scripting Runtime Version: .NET 4.x Equivalentまずそもそも読み書き権限つけ忘れてないか確認
RealtimeDatabaseはルールを設定して読み書きの権限を設定できます。
うっかり{ "rules": { ".read": false, ".write": "auth != null && auth.isAdmin == true" } }なんてしてたら当然読み込みできません。(自分はうっかりしてたけど・・・)
ちなみにその場合はこういう警告がEditorのコンソールに出力されます。[Warn] SyncTree: Listen at /******* failed: DatabaseError: Permission denied UnityEngine.Debug:LogWarning(Object)権限ちゃんとついてるのに何故かエラーになるんだけど・・・
で、うっかり権限を修正した後に冒頭のエラーに出くわして、なんでえええええっとなってたわけでした。
ネットの海を泳いで、結論のページにたどり着きました。実際にやって見た所
1.ポケットwifiを持っていたので、コワーキングスペースのwifiからそちらを利用するように切り替えるとちゃんと応答返ってきてデータ取れました。
→お家に帰った後に有線、無線両方の環境で再度確認した所両方データ取れました。
ピンポイントでハズレ引いたみたい・・・2.コワーキングスペースのwifiに戻して、「Firebase/Plugins/FirebaseDatabase.dll」をEditor上で有効にしたら同様にデータ取れました
→Import時に特に設定を変更していない場合「Assets/Firebase/Plugins/FirebaseDatabase.dll」に対象のファイルがいます。但しissueに記載の通り動作は不安定で、時々UnityEditor毎落ちます
However, if you have to use it, you should expect the instability. This is why it is disabled for Editor by default.
↓↓↓Google 翻訳
しかし、あなたがそれを使わなければならないなら、あなたは不安定さを期待するべきです。エディターではデフォルトで無効になっているのはこのためです。感想
もし、制作する側の立場の場合、こういう環境に依存するようなやつが一番めんどくさいだろうなとは思います。
そもそもFirestore使えと言われたら、ハイ、スイマセンって感じですが・・・参考
https://stackoverflow.com/questions/50651865/unityfirebase-databaseerror-websocket-ws-0-could-not-connect
https://github.com/firebase/quickstart-unity/issues/106#issuecomment-426835393
- 投稿日:2019-02-03T18:03:27+09:00
Unityアセット AnySync を使ってネットワークを介したゲームオブジェクトの位置のシンクロを簡単に
はじめに
ネットワークゲームでは、ネットワーク上の各マシン上で共有されているゲームオブジェクトの位置が常にシンクロ(同期)していないと困りますが、それをきっちり作るのに意外と苦労しています、、、。最初はシンクロしているようでも、時間がたつと段々ズレてしまったりして。
そこで便利なのがAnySyncというUnity用アセットです(残念ながら有料。15ドル)
https://assetstore.unity.com/packages/tools/network/anysync-114580
これは、ネットワーク上のマシン間でゲームオブジェクトのPosition, Rotationなどを共有するためのアセットです。UNET、Photonなど幅広い種類のライブラリと一緒に使うことができます。ただ、使用の際には少しコーディングが必要なので、その方法について解説します。なお、今回はLAN内P2P(peer to peer)に限定してお話するのでご注意ください。
準備
UnityのネットワークライブラリはUNET(2018.4LTSを最後に提供終了予定)が
最も身近ですが、Izmさんのこちらの記事を参考にした結果、UNETと近い感覚で使えて動作が安定している Mirror という無料アセットを使うことにしました。Unityプロジェクトを作ったら、MirrorとAnySyncをダウンロード、インポートしてください。
シンクロさせるゲームオブジェクトの設定
今回は、2つのPCで一方にHost,もう一方にClientを実行し、Host側でスペースキーを押すとBallというプレハブがインスタンス化され、それをHost側で動かすとClient側でも動くようにします。
まず、メニューからGameObject/3D Object/Sphereを選んでゲームオブジェクトを作り、Ballという名前を付けてください。
次に、BallにNetwork Identityコンポーネントをアタッチします。Server Only,Local Player Authorityのチェックは不要です。更に、Sync Bufferもアタッチします。これには、シンクロさせたい位置情報などが保存されます。
それでは、シンクロに関する処理のためのスクリプトBallSync.csを準備しましょう。AnySyncのサンプルプロジェクトに付属のものを基にしています。準備できたらBallにアタッチしてください。
BallSync.csusing UnityEngine; using Mirror; public class BallSync : NetworkBehaviour { private const float MinimumSendInterval = 0.05f; private float _timeSinceLastSync; private Vector3 _lastSentPosition; private bool _idle; private SyncBuffer BallSyncBuffer; private void Awake() { BallSyncBuffer = GetComponent<SyncBuffer>(); } private void Update() { if (hasAuthority) { _timeSinceLastSync += Time.deltaTime; if (_timeSinceLastSync >= MinimumSendInterval) { if (_lastSentPosition != transform.position) _idle = false; if (!_idle) { CmdSync(_timeSinceLastSync, transform.position); if (_lastSentPosition == transform.position) _idle = true; _lastSentPosition = transform.position; _timeSinceLastSync = 0f; } } } else { if (BallSyncBuffer.HasKeyframes) { BallSyncBuffer.UpdatePlayback(Time.deltaTime); transform.position = BallSyncBuffer.Position; } } } [Command] private void CmdSync(float interpolationTime, Vector2 position) { BallSyncBuffer.AddKeyframe(interpolationTime, position); RpcSync(interpolationTime, position); } [ClientRpc] private void RpcSync(float interpolationTime, Vector2 position) { if (isLocalPlayer || isServer) return; BallSyncBuffer.AddKeyframe(interpolationTime, position); } }この中で、[Command]アトリビュートのついているCmdSyncはホスト側で、[ClientRpc]アトリビュートのついているRpcSyncはクライアント側で実行されます。
Update関数内はhasAuthorityによって分岐しています。hasAuthorityがTrue、つまりAuthorityを持っている場合(今回はホスト側)はCmdSyncを実行して現在の位置情報をSync Bufferに送ります。更にその中でRpcSyncを実行して、クライアント側のSyncBufferにも位置情報を送ります。これでホスト、クライアント両方で同じ位置情報がバッファーに保存されます。hasAuthorityがFalseの場合(今回はクライアント側)はSync Bufferに保存されているデータを読み取ってクライアント側のBallの位置として代入しています。
これでBallの準備はできたので、Ballをプロジェクトウィンドウにドラッグして、プレハブ化してください。ヒエラルキー内のBallは消しましょう。
他のゲームオブジェクトの設定
次に、BallSpawnerという空のゲームオブジェクトを作ってください。Network Identityコンポーネントをアタッチして、Server Onlyにチェックをいれます。これにより、BallSpawnerはホスト側だけで動作するようになります。
次に、スクリプト BallSpawner.csを作ってBallSpawnerオブジェクトにアタッチしてください。
BallSpawner.csusing UnityEngine; using Mirror; public class BallSpawner : NetworkBehaviour { public GameObject Ball; void Update () { if (Input.GetKeyDown(KeyCode.Space)) { Fire(); } } void Fire() { var go = Instantiate(Ball); NetworkServer.Spawn(go); } }インスペクタ上で変数BallにBallプレハブをアサインするのを忘れずに。
これで、スペースキーを押すとホスト側でBallがインスタンス化されて、さらに、NetworkServer.Spawn(go)によりクライアント側でもBallオブジェクトが生成されます。
最後に、NetworkManagerという空のゲームオブジェクトを作ってください。そちらに、Network Managerコンポーネント、Network Manager HUDコンポーネントをアタッチしてください。さらに、Network Managerコンポーネントの中のSpawn Info中のRegistered Spawnable PrefabsにBallプレハブをアサインしてください。
完成
これで完成です。スタンドアロンとしてビルドして、エディターと一緒に実行してみましょう。
この画面になったら、Editor側ではLAN Host, ビルドしたアプリ側ではLAN Clientをクリックしてください。次に、スペースバーを押せばBallが出てきて、シーンビューか何かでBallを移動させれば、Client(スタンドアロンアプリ)側でも同じように移動するはずです。
- 投稿日:2019-02-03T11:23:41+09:00
LeapMotion+Unityでグー・チョキ・パーを認識する
はじめに
VRMの流行の兆しが見えたので推しの子とじゃんけんが出来るソフトでも作っておこうかな、と思ったのでLeapMotionでじゃんけんの手を検知する仕組みを作りました。知見共有。
今日のおもちゃ:LeapMotionでじゃんけんの手を認識する pic.twitter.com/rLF0GXOh9F
— 避雷 (@lucknknock) 2019年2月3日
制作
新しい方のLeapMotionSDKとUnityの知見は少な目な気がします…もしかしてもうLeapMotion界隈下火だったりします…?
LeapMotionSDKの準備
まずLeapMotion公式からUnity向けSDKをダウンロードしておきます。中にあったUnityPackageをProjectにインポートします。
Unity側の準備
Assets/LeapMotion/Core/Examples/Capsule Hands(Desktop)からLeapMotionControllerとHandModelsをコピペして自分のシーンに貼り付けます。コレさえあればとりあえず動くはずなので、試しに実行してみて手がトラッキングできればとりあえず成功です。
手の形を認識する
HandRSP.csusing System; using System.Collections; using System.Collections.Generic; using UnityEngine; using Leap; using System.Linq; public class HandRSP : MonoBehaviour { private Controller controller; private Finger[] fingers; private bool[] isGripFingers; public enum RSP { Rock, Scissors, Paper }; public RSP rsp; // Use this for initialization void Start () { controller = new Controller(); fingers = new Finger[5]; isGripFingers = new bool[5]; } // Update is called once per frame void Update () { Frame frame = controller.Frame(); if(frame.Hands.Count != 0) { List<Hand> hand = frame.Hands; fingers = hand[0].Fingers.ToArray(); isGripFingers = Array.ConvertAll(fingers, new Converter<Finger, bool>(i => i.IsExtended)); Debug.Log(isGripFingers[0]+","+ isGripFingers[1] + "," + isGripFingers[2] + "," + isGripFingers[3] + "," + isGripFingers[4]); int extendedFingerCount = isGripFingers.Count(n => n == true); if(extendedFingerCount == 0) { rsp = RSP.Rock; } else if(extendedFingerCount < 4) { rsp = RSP.Scissors; } else { rsp = RSP.Paper; } } } }コレをシーン内の好きなオブジェクトにアタッチすればrspにグーチョキパーの認識結果が表示されます。
解説
usingステートメント
LeapMotionの機能を利用するには
using Leap;のステートメントを利用します。Controller
LeapMotionの制御はControllerで行い、これがメインのインターフェースとなります。
Frame
Controller.Frameには手の挙動の情報が含まれています。Hand
Hand型には手の位置、角度や各指の情報が存在しています。
Controller.Handsで認識されている全ての手についてのHandを返します。認識範囲内に一つも手がなければ当然要素数0になるので例外処理をしっかりしましょう。Finger
各手についている指の情報を保有する型です。位置、指し示す向き、握っているか否かなどの情報を取得することができます。
Hand.Fingersですべての指のFingerを取得することができます。また、各Fingerに対してFinger.IsExtendで指の開きを検知することができます。指が開いているときTrue、指が曲がっているときがFalseです。上記の実装例ではisGripFingers = Array.ConvertAll(fingers, new Converter<Finger, bool>(i => i.IsExtended));で全ての指の曲がり方のブール値を取得したのち、int extendedFingerCount = isGripFingers.Count(n => n == true);(LinQを利用しています)で曲がっている指の本数を取得しています。RSP
曲がった指の本数を基準にグーチョキパーのいずれの手が出されているか判断します。RSPは手の種類をまとめたEnumで、今回は曲がった指の本数が5本でグー、4~2本でチョキ、1本以下でパーとしています。
問題点
取り敢えず雑に認識させることができましたが、チョキとグーの認識が結構厳しく、ちゃんと手を下に向けないとうまく認識してくれなかったりします。これについては部屋が明るいのとか僕の手がオタク特有のまっしろハンドなのとかいろいろ原因がありそうです。
解決のアイデア
まぁ別にガチじゃんけんするわけでもないしコンピュータ側の出す手に合わせてユーザーを勝たせる側に強めの認識補正をかけてあげればいいんじゃないでしょうか。勝てる手を出したのに認識ミスで負けるってのはプレイフィール最悪なのでそれだけは避ける方向で行くといいと思います。
最後に
ちょうどVroidHubSDKも頂けたのでこれを利用して好みのモデルとじゃんけんが出来るシステムを組んでいきたいです。やるぞ~~
- 投稿日:2019-02-03T09:05:07+09:00
はじめての非同期処理 call backは,送信機と受信機のイメージで.(OcuGo.Games)
非同期処理とは。
このボタンが押されたら、とか。
敵が3体倒されたら、とか。
一般論でいうと、この人から電話がきたら、とか。
自分のタイミングではなく、外部からのトリガーで処理を実行したい際に「非同期処理」を使います。
送り手と受け手がいる.
受け手: 条件満たしたのね.連絡ありがと! 次の処理流そっと.
callbackは System.Action型
C#では、System.Action型の Callback という関数が用意されています.
以下では、「スペースキーが3回押されたら」という条件をトリガーに、次の処理を流すという設定でコードを紹介します。送り手側
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CountSpace : MonoBehaviour { System.Action Callback; int spacenum = 0; // Update is called once per frame void Update() { //スペースキーを押すとspacenumをカウントアップする. if (Input.GetKeyDown(KeyCode.Space)){ this.spacenum++; } //スペースキーが3回押されたら if (spacenum == 3) { this.Callback(); //送信機を使用して連絡をする. } } //送信機の設置 public void SetCallback(System.Action Callback) { this.Callback = Callback; // }受け手側
public CountSpace A; //CountSpaceという classの Aという変数 // Use this for initialization void Start () { //電話が来たらカッコ内の処理を流す. A.SetCallback(連絡を受け取ってから流したい処理); }ここでは仮に変数名をAとしましたが,なんでも構いません.
イメージ的にはAさんからかかってきたらということですが,そのAさんが誰であるか限定する必要がないためです。
- 投稿日:2019-02-03T00:14:51+09:00
CVVTuberExample の VRMCVVTuber を動かす
概要
Unity アセットの CVVTuberExample の中に同梱されている、
VRMCVVTuberExample を動かすのに少し修正が必要だったのでメモCVVTuberExample とは
OpenCV と Dlib を使って簡単に VTuber システムを作るもの。
その中でも VRMCVVTuberExample は、VRMモデルを利用したもの。利用したもの
OpenCV for Unity(有料)
Dlib FaceLandmark Detector(有料)
CVVTuberExample
UniVRM(今回は現時点で最新の0.49を利用)
VRMモデル(今回は AliciaSolid.vrm を利用、お好きなキャラクターでお試しください VRoid Hub オススメ)
Webカメラ Logicool C270セットアップ
- Asset Store から CVVTuberExample をインポート
- CVVTuberExample/CVVTuber/Addons/VRMCVVTuber.unitypackage をダブルクリックしてインポート
- CVVTuberExample/CVVTuber/Addons/VRMCVVTuber/Examples/VRMCVVTuberExample.unity をダブルクリックしシーンを読み込み
- Asset Store から OpenCV for Unity をインポート
- Asset Store から Dlib FaceLandmark Detector をインポート
- インポートした OpenCVForUnity, DlibFaceLandmarkDetector の中にある StreamingAssets フォルダを、Assets 配下に移動する
- UniVRM をインポート
VRM モデルを読み込む
Assets 配下の適当なところに、ダウンロードしてきた vrm ファイルを D&D
出来上がった prefab を Hierarchy に D&Dその後、Hierarchy の VRMCVVTuberExample 内の VRMControllManager を選択し
Inspector に表示される VRM Loader (Script) 内の Meta に読み込んだモデルを選択するコードの修正
今のままだと、エラーが出たままで実行できないので、修正します。
原因はおそらく UniVRM の仕様が昔から変わってるからだと思います。VRMCVVTuberControllManager.cs を修正する
67行目あたり
VRMCVVTuberControllManager.cs((VRMHeadRotationController)item).target = vrmLoader.lookAtHead.Head.Transform;↓
VRMCVVTuberControllManager.cs((VRMHeadRotationController)item).target = vrmLoader.lookAtHead.Head;VRMLoader.cs を修正する
56行目あたり
VRMLoader.csvar context = new VRMImporterContext (path); context.ParseVrm (bytes);↓
VRMLoader.csvar context = new VRMImporterContext (); context.ParseGlb(bytes);これで実行できるようになります。
おまけ
VRMLoader.cs の修正部分は、ランタイムロードを使う部分なので、
そのまま動かす場合は関係ないですが、実行できるようにするために必要です。ランタイムロードを確認する場合は、StreamingAssets 配下に vrm ファイルを配置します。
また、Hierarchy の VRMCVVTuberExample 内の VRMControllManager を選択し
VRMCVV Tuber Controll Manager (Script) の Use Runtime Loader にチェックをいれて
VRM Loader (Script) 内の VRM File Name StreamingAssets 配下からの vrm ファイルのパスを入力してください。
StreamingAssets/Model/AliciaSolid.vrm と配置していたら Model/AliciaSolid.vrm と入力する。
これで VRM モデルのランタイムロードができます。
この場合は、モデルの prefab を Hierarchy においておく必要がありません。













