UnityとPhotonでローカルネットワーク通信をする
経緯
最近筆者がオフライン環境でPhoton通信をしようとしたところ、最初うまくできずに困っていたが、解決策を見つけた。
インターネットにつながっていないオフラインな環境下でのPhoton通信についての記事をあまり見かけなかったのでここいらでまとめておく。
そもそもインターネットのない環境でPhoton通信できるのか
結論から言うとPhoton Server起動時にインターネット接続が必要。
一度起動すればオフラインにしても大丈夫。
どうやらサーバーを起動する際に、持っているライセンスが有効かどうかをインターネット経由で確認している模様。
今回使用しているもの
Unity 2021.1.0f1
PUN 2 v2.30.0
Photon OnPremise Server v4.0.28.2962
ネットワーク構成
PC2台を用意して、ルーターにそれぞれ有線と無線で接続。
有線接続のPC1にはサーバーとアプリを、無線接続のPC2にはアプリのみを入れて通信をしようとした。
問題1.サーバーが起動しなかった
前述したとおり、Photon Server v4はサーバー起動時にインターネット経由でライセンスの有効性を確認する。
そのためオフライン環境で動作させるためには、サーバー起動時のみオンラインにしてライセンス認証を通す必要がある。(オフラインとは)
オンライン認証のいらない特殊なライセンスは公式HPには存在しないけど、サポート連絡したら何かあるかも。
問題2.サーバーは起動したがアプリから接続できない
これはバージョンの相性などがあると思うが、筆者の環境ではシリアル化プロトコルのバージョンを変更することで解決できた。
現状のクライアント側のPhotonではプロトコルバージョン1.8をデフォルトで使用するようになっているが、今のところPhoton Server SDKはこれに対応しておらず、クライアントから接続を試みてもはじいてしまうらしい。
Unityの場合、サーバーに接続する前にクライアント側(Unityアプリ側)で以下のスクリプトでシリアル化プロトコルバージョンを変えることで接続できるようになる。
//シリアル化プロトコルのバージョンを1.6に変更する PhotonNetwork.NetworkingClient.LoadBalancingPeer.SerializationProtocolType = ExitGames.Client.Photon.SerializationProtocol.GpBinaryV16;
この問題は2021/8/28現在、V4.0.29.11263でも既知な模様
https://doc.photonengine.com/ja-jp/realtime/current/getting-started/onpremises-or-saas
Androidビルドで"Gradle Error:Colliding Attributes"と出てビルドできない問題
はじめに
先日、Oculus Questのファームウェアアップデートがあり、Oculus GOアプリへの対応準備がされた模様。
1/3: Go emulation is coming to Quest later this year, by way of a compatibility layer that makes Quest report as a Go and emulate the Go controller for old apps.
— John Carmack (@ID_AA_Carmack) July 23, 2019
しかしこれにより、既存のQuestのアプリで片手しか認識されないという不具合がしばしば確認された。
幸い、OVRManagerの設定をいじることで解決できるらしく、私もこれの対応をしていたのだが、ちょっと見慣れないエラーにぶち当たって数時間頭を悩ませたので、解決策をここで記述しておく。
動作環境
Windows10
Unity 2018.3.6f1
Oculus Integration 1.39
Android Studio 3.4.1
確認した問題
Questの新ファームウェアに対応するため、以下の操作を実施
OVR Manager
のTargetDevices
をQuestに変更- メニューバーの
Oculus > Tools > Remove AndroidManifest.xml
で既存のAndroidManifestを削除 - メニューバーの
Oculus > Tools > Create store-compatibleAndroidManifest.xml
で新しくAndoroidManifestを作成
本来であればこれで問題ないはずだったのだが、ビルド最終段階になってこんなエラーが出た。
...英語がいっぱいだ...
このGradle Error:Colliding Attribute
というのは、AndroidManifestの統合時のエラーらしい
正直これに関しての知識は今回調べた程度で詳しくないので、その手の方々から怒られないためにもあまり下手なことは書かないようにするが、
どうやら追加されたアセットなどでインポートされたライブラリファイル内のManifest
ファイルにandroid:allowBackup="true"
が記述されている状態で、
Assets>Plugins>Android>AndroidManifest.xml内のApplication
タグ内にあるandroid:allowBackup
がfalse
となっていると統合できないらしい。
そもそもAndroidManifest.xmlって?
Androidアプリの基本情報(パッケージ名やバージョン、APIレベルなど)が記述されている。
アプリ内には一つしか入れることができないため、ビルド前にアセットなどで複数のManifestファイルがある場合は、ビルド時に自動で一つに統合されるらしい。
今回はこの統合時にエラーが起きてしまったらしい。
解決策
メインとなるAndroidManifest
内に追記することでこの問題を回避できる。
Manifest
タグ内にxmlns:tools="http://schemas.android.com/tools"
を追記
Application
タグ内にtools:replace = "android:allowBackup"
を追記
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> //←ここと <application android:allowBackup="false" tools:replace = "android:allowBackup"> //←ここ </manifest>
とりあえずこれで私は解決した。
FBXでUnityにアニメーションインポートしたらうまく動かない時の対処
先日少しハマっていたので書いてみる
僕は普段人型モデルのアニメーションを作る時は3ds maxで作成してから、FBXに書き出してUnityにインポートしている。
しかし、先日それをしたところ、なぜかうまく動かないという事態に陥った。
具体的には、インポートしたアニメーションをループさせた時に、切り替わりの瞬間にガタつきが目立った。
「うまくキー打ちできてなかったのかな」と思い3ds max側で確認したところ、問題なく動作した。
明らかにUnity側でアニメーションに不具合が出ているようだった。
色々と検証してみた結果、どうやらボーンの制御方法の違いによる誤差だったようだ。
Unityではボーンの回転制御にQuarternion(四元数)とオイラー角制御の二種類がある。
Quarternion
QuarternionはUnityでのオブジェクトのTransformコンポーネントに入っている回転制御で使われている方式で、四元数とも呼ばれてる。
正直数学的知見はあまり持ち合わせていないのでここで詳しく言及はしないが、内部処理的に計算しやすいらしい。
オイラー角
もう片方のオイラー角制御は、角度を測る時に使う度数法で計算する方法。
人間としては30度、60度、180度と、こちらのほうが計算しやすい。
Unityの場合、オイラー角制御のアニメーションはインポート時にQuarternionで制御できるように自動で変換される設定がデフォルトでオンになっているらしい。
まあUnity君がQuarternionの計算のほうがいいのならそっちのほうが計算リソース的にもよろしいのだが、この変換作業が罠だった...。
どうやらオイラー角→Quarternionの変換には若干の誤差が生じるらしく、このせいでアニメーションが微妙にずれてしまったようだ。
なんで誤差が生じてしまう変換の設定がデフォルトでオンになっているのか...。
解決方法としては、FBXを選択したときのInspectorで、Animationタブにある"Resample Curves"のチェックを外す。
これでアニメーション制御をQuarternionに自動変換せず、オイラー角のまま計算する。
デメリットとしては、少しだけアニメーション再生の計算が重くなるらしい。
PC向け開発ならそこまで気にする必要もないが、モバイルなら気にしたほうがいいかもしれない。
おとなしくアニメーションをQuarternionで作成するのが吉。
Unityの角度の求め方とベクトル
ちょっと勘違いしてたので、忘れないようにここにも記載
少しややこしくて頭の中で混線してたけども、最近何となくわかってきた
Vector3.angle
を使う
Vector3.angle(Vector3 from, Vector3 to)
これで角度が求まる
しかしここで重要なのが、Vector3が点(座標)ではないと言うこと(ここでつまづいてた)
Vector3って座標を計算する時にめっちゃ使ってたので、Vector3 = 座標っていう認識があった(僕だけじゃないと思いたい...)
あくまでVector3.angleは2つのベクトルの角度差を求める。
なので、点(座標として使っている状態)では使えない。
ちなみに、ワールド座標との角度を計算したいときには
Vector3.up //ワールドの上ベクトル(y軸方向)
Vector3.right //ワールドの→ベクトル(x軸方向)
VEctor3.forward //ワールドの前ベクトル(z軸方向)
等、いちいち(0,1,0)
とかで指定しなくても大丈夫。便利。
Unityの公式リファレンスに全部乗ってる
docs.unity3d.com
二点のベクトルを求める
二つの座標間のベクトルを求める時は、終点(ベクトルの先)座標から始点(ベクトルの元)を引けばいい
Vector3 ベクトル = Vector3 終点 - Vector3 始点
プレイヤーから目標物までの方向をとりたいときとかによく使う。
ウェイポイントを配列で順番に管理して、近づいたら次のウェイポイントまでのベクトルを再計算とかすると、簡単にナビゲーションシステムができる。
OVRInputのコントローラ入力まとめ
いろんなサイトやブログにいくらでも載っているけど、欲しいものがまとまってるのは少ないのでここに貯めていく。
ちなみに公式のサイトはこちら→Oculus OVRInput
どうやらOVRInputはInput Mappingという入力の種類が複数あるらしく、それによってコードが変わってくるらしい(正直よくわからん)
ので、ここでは「Raw Mapping」の記法を載せていく。
トリガーの入力
- 右手のトリガーボタンは
OVRInput.RawButton.RIndexTrigger
で取れる(戻り値はboolean)
"Index"って人差し指って意味らしい。
RIndexTrigger
をLIndexTrigger
にすると左手の方をとれる。
この前にGetDown
やらGetUp
とつけることで「押した時」や「離した時」の値が返ってくる。
OVRInput.GetDown(OVRInput.RawButton.RIndexTrigger) ///右手トリガー押した時 OVRInput.Get(OVRInput.RawButton.RIndexTrigger)///右手トリガー押している間 OVRInput.GetUp(OVRInput.RawButton.RIndexTrigger) ///右手トリガー離した時
IndexTrigger
をHandTrigger
にすると、中指らへんにあるハンドトリガー(?)の値が取れる。
OVRInput.GetDown(OVRInput.RawButton.RHandTrigger) ///右手ハンドトリガー押した時 OVRInput.Get(OVRInput.RawButton.RHandTrigger)///右手ハンドトリガー押している間 OVRInput.GetUp(OVRInput.RawButton.RHandTrigger) ///右手ハンドトリガー離した時
- トリガーは0~1までのアナログ値も取れる(戻り値はfloat)
float analog = OVRInput.Get(OVRInput.RawAxis1D.RIndexTrigger)///右手トリガーの押し具合(-1~1)
ボタンの入力
- A,B,X,Yボタンは
OVRInput.RawButton."ボタン"
で取れる(戻り値はboolean)
ABボタンは右コントローラ。XYボタンは左コントローラについてる。
OVRInput.GetDown(OVRInput.RawButton.A)///Aボタンを押した時 OVRInput.GetDown(OVRInput.RawButton.B)///Bボタンを押した時
- こいつらも
GetDown
やGetUp
を使うと押している状態をそれぞれ取れる。
OVRInput.Get(OVRInput.RawButton.X)///Xボタンを押している間 OVRInput.GetUp(OVRInput.RawButton.Y)///Yボタンを離した時
スティック操作
Oculus Touchには親指の位置に立派なスティックがある。
当然傾けたりもできるが、押し込むことでボタンのような入力もできる。
- 上下左右の動きの入力は
OVRInput.RawButton.RThumbstick"方向"
で取れる。
左のスティックならLThumbStick
にする。
OVRInput.Get(OVRInput.RawButton.RThumbstickUp)///右手スティック上 OVRInput.Get(OVRInput.RawButton.RThumbstickDown)///右手スティック下 OVRInput.Get(OVRInput.RawButton.LThumbstickRight)///左手スティック右 OVRInput.Get(OVRInput.RawButton.LThumbstickLeft)///左手スティック左
- スティックの位置を二次元で取ることもできる。(戻り値はVector2)
Vector2 pos = OVRInput.Get(OVRInput.RawAxis2D.LThumbstick)///定位置を原点としたx(-1~1),y(-1~1)の数値
Up
やRight
等方向を示す部分を削ると、スティックを押しボタンとした入力が取れる。
OVRInput.GetDown(OVRInput.RawButton.RHandTrigger);///右手スティック押した OVRInput.Get(OVRInput.RawButton.RHandTrigger);///右手スティック押している OVRInput.GetUp(OVRInput.RawButton.LHandTrigger);///左手スティック離した
残りはそのうち追加していく