冬乃Tech Blog

主にUnityでのC#コーディングで得た知見をまとめていく

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にはアプリのみを入れて通信をしようとした。 f:id:fuyunomikan1223:20210828014201p:plain

問題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アプリへの対応準備がされた模様。

しかしこれにより、既存のQuestのアプリで片手しか認識されないという不具合がしばしば確認された。
幸い、OVRManagerの設定をいじることで解決できるらしく、私もこれの対応をしていたのだが、ちょっと見慣れないエラーにぶち当たって数時間頭を悩ませたので、解決策をここで記述しておく。

動作環境

Windows10
Unity 2018.3.6f1
Oculus Integration 1.39
Android Studio 3.4.1

確認した問題

Questの新ファームウェアに対応するため、以下の操作を実施

  • OVR ManagerTargetDevicesをQuestに変更
  • メニューバーのOculus > Tools > Remove AndroidManifest.xml で既存のAndroidManifestを削除
  • メニューバーのOculus > Tools > Create store-compatibleAndroidManifest.xml で新しくAndoroidManifestを作成
    本来であればこれで問題ないはずだったのだが、ビルド最終段階になってこんなエラーが出た。

f:id:fuyunomikan1223:20190810151115p:plain:left


...英語がいっぱいだ...

このGradle Error:Colliding Attributeというのは、AndroidManifestの統合時のエラーらしい

docs.unity3d.com

正直これに関しての知識は今回調べた程度で詳しくないので、その手の方々から怒られないためにもあまり下手なことは書かないようにするが、
どうやら追加されたアセットなどでインポートされたライブラリファイル内のManifestファイルにandroid:allowBackup="true"が記述されている状態で、
Assets>Plugins>Android>AndroidManifest.xml内のApplicationタグ内にあるandroid:allowBackupfalseとなっていると統合できないらしい。

そもそもAndroidManifest.xmlって?

Androidアプリの基本情報(パッケージ名やバージョン、APIレベルなど)が記述されている。
アプリ内には一つしか入れることができないため、ビルド前にアセットなどで複数のManifestファイルがある場合は、ビルド時に自動で一つに統合されるらしい。

developer.android.com

今回はこの統合時にエラーが起きてしまったらしい。

解決策

メインとなる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"のチェックを外す。

f:id:fuyunomikan1223:20190720214812p:plain
UnityでFBXを選択したときのInspectorウィンドウ
これでアニメーション制御を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"って人差し指って意味らしい。
    RIndexTriggerLIndexTriggerにすると左手の方をとれる。
    この前にGetDownやらGetUpとつけることで「押した時」や「離した時」の値が返ってくる。
OVRInput.GetDown(OVRInput.RawButton.RIndexTrigger) ///右手トリガー押した時
OVRInput.Get(OVRInput.RawButton.RIndexTrigger)///右手トリガー押している間
OVRInput.GetUp(OVRInput.RawButton.RIndexTrigger) ///右手トリガー離した時
  • IndexTriggerHandTriggerにすると、中指らへんにあるハンドトリガー(?)の値が取れる。
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ボタンを押した時
  • こいつらもGetDownGetUpを使うと押している状態をそれぞれ取れる。
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)の数値
  • UpRight等方向を示す部分を削ると、スティックを押しボタンとした入力が取れる。
OVRInput.GetDown(OVRInput.RawButton.RHandTrigger);///右手スティック押した
OVRInput.Get(OVRInput.RawButton.RHandTrigger);///右手スティック押している
OVRInput.GetUp(OVRInput.RawButton.LHandTrigger);///左手スティック離した

残りはそのうち追加していく