20210729のUnityに関する記事は7件です。

PythonでETロボコンシミュレータ(大会用)を動かしてみた。

ETロボコンシミュレータ(大会用)について ETロボコンは参加者向けに環境構築のためのリポジトリを公開しており、ここからETロボコンシミュレータ(大会用)をインストールすることができます。実は、参加者でない方でもサンプルコースだけが動くETロボコンシミュレータを体験することができます。作りかけのWEB版ETロボコンシミュレータと異なり、大会に必要な機能をすべて備えていますので、こちらのシミュレータをもっと活用できればいいのになぁ、と思う方もいらっしゃると思います。 今時C言語でロボコン? などと言われるのですが、以前はJavaで参加するチームもいましたし、ETロボコン2021では実はmrubyが公式に使えます。やる気になればどうにでもなる大会なのです。mrubyは実機でも動作することを意識し、TOPPERS、Athrillの環境をベースにしているため、これはこれでとても興味深い環境だと思っています。 MiniScriptとか知らんし。 WEB版ETロボコンシミュレータではMiniScriptを採用しましたが、これを勉強したところで役に立たないよね?という意見も多いかと思います。まぁ、私もそうかなと思いつつも、今のところこれ以上いい案が思いついていないだけなのです。sessionStorageとWebAssemblyを使えば何でもできそうな気がしなくはないですが、まだモチベーションが上がっていません。 実はETロボコンシミュレータ(大会用)はPythonでも動かせる。 ここからが本題です。ETロボコンシミュレータは組込みCPUシミュレータAthrillとUDPで通信しています。ここの通信フォーマットを理解しているならば、Athrillでなくとも制御ができてしまいます。(もちろん、今大会では利用できませんけど!) ということで理解している私がPythonクライアントを作りました。ETロボコンシミュレータを起動し適当なディレクトリで $ git clone https://github.com/YoshitakaAtarashi/ETroboSimController $ cd ETroboSimController $ python test_Motor.py とすると、 というような感じで走行体を動かすことができます。前提環境はPythonだけなのでインストール地獄にはならないはずです。Pythonは3.7.6以上で確認しています。ETroboSimServerがIO処理でブロックされないようasyncioを使っているため、古いPythonだと動かない可能性が高いです。 test_Motor.pyの解説 まず、EV3 C++ APIに似せたetrobosim.ev3api(as ev3)と、Athrillの代わりにETロボコンシミュレータを制御する etrobosim(as ets)をインポートします。 次に、各モータの初期化をします。この辺はWEB版ETロボコンシミュレータと違って、大会環境に似せようという過去の私の努力が見て取れます。 ets.Controllerで左コースか右コースかを選択できます。Python実装を見るとわかりますが、ここでUDPポートを切り替えています。controller.addHandlersは作成したデバイス(motor*)をここで追加することで、controllerの管理下に置くことができます。あとはお決まりのPWM値の設定です。 Ctrl+Cで停止できるように、KeyboardInterruptをキャッチしています。(これがなかなかうまく実装できなかった) import etrobosim.ev3api as ev3 import etrobosim as ets import time motorR=ev3.Motor(ev3.ePortM.PORT_B,True,ev3.MotorType.LARGE_MOTOR) motorL=ev3.Motor(ev3.ePortM.PORT_C,True,ev3.MotorType.LARGE_MOTOR) motorARM=ev3.Motor(ev3.ePortM.PORT_A,True,ev3.MotorType.MEDIUM_MOTOR) motorTAIL=ev3.Motor(ev3.ePortM.PORT_D,True,ev3.MotorType.LARGE_MOTOR) motorR.reset() motorL.reset() # 1秒毎に直進と回転を切り替える。 walker=[(50,50),(50,0)] wid=0 try: controller=ets.Controller(ets.Course.LEFT) controller.addHandlers([motorR,motorL,motorARM,motorTAIL]) controller.start(debug=False) while controller.isAlive(): motorR.setPWM(walker[wid][0]) motorL.setPWM(walker[wid][1]) wid=(wid+1)%len(walker) print("MotorR={},MotorL={}".format(motorR.getCount(),motorL.getCount())) time.sleep(1) controller.exit_process() except KeyboardInterrupt: controller.exit_process() raise test_LineTrace.pyによるライントレース もちろんライントレースもできます。colorSensorを追加している点と、pidControlを周期的に動かすためにcontroller.runCyclic(pidControl)を実行している点が異なります。組込みOSと違って精度よく周期実行させることはできませんが、まぁまぁいい感じに動くのでぜひ試してみてください。 import etrobosim.ev3api as ev3 import etrobosim as ets # ColorSensorのReflectを使ってP制御でライントレースする。 def calcPID(r, target=20, power=70,P=1.8): p=r-target left=power-P*p right=power+P*p return (int(left),int(right)) def pidControl(initARM_count=-50,initTAIL_count=0): left,right=calcPID(colorSensor.getBrightness()) motorL.setPWM(left) motorR.setPWM(right) motorARM.setPWM(initARM_count-motorARM.getCount()) motorTAIL.setPWM(initTAIL_count-motorTAIL.getCount()) #print("MotorR={},MotorL={},MotorARM={},Color={}".format(motorR.getCount(),motorL.getCount(),motorARM.getCount(),colorSensor.getBrightness())) motorR=ev3.Motor(ev3.ePortM.PORT_B,True,ev3.MotorType.LARGE_MOTOR) motorL=ev3.Motor(ev3.ePortM.PORT_C,True,ev3.MotorType.LARGE_MOTOR) motorARM=ev3.Motor(ev3.ePortM.PORT_A,True,ev3.MotorType.MEDIUM_MOTOR) motorTAIL=ev3.Motor(ev3.ePortM.PORT_D,True,ev3.MotorType.LARGE_MOTOR) motorR.reset() motorL.reset() colorSensor=ev3.ColorSensor(ev3.ePortS.PORT_2) try: controller=ets.Controller(ets.Course.LEFT) controller.addHandlers([motorR,motorL,motorARM,motorTAIL,colorSensor]) controller.start(debug=True) controller.runCyclic(pidControl) controller.exit_process() except KeyboardInterrupt: controller.exit_process() pass 最後に UDPの通信仕様だけ知ってればいいんだからPythonで簡単に作れると思いきや、同期周りで苦労して、組込みOS的な機能がやはり欲しくなりました。asyncioは今回初めて使ったのですが、ちゃんと動くようになるまではイライラしっぱなしでした。非同期処理ってホント難しいですね。 これの応用ですが、大会で採用するとしたら機械学習とか強化学習とかに使えるんじゃないですかね。学習データをPythonで取っておいて、大会用プログラムはAthrill対応の言語(C、C++、mrubyなど)とするのも面白そうです。来年はさすがにシミュレータ大会オンリーじゃなくなっててほしいですけど。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】iOS,Androidでuuidを取得する方法

Android SystemInfo.deviceUniqueIdentifier iOS SystemInfo.deviceUniqueIdentifier ではOSのアップデート時に値が変わるそうなので一工夫が必要です。 前提条件 iOSのキーチェーンを取り扱うアセットを使います。 手順 1.キーチェーンからuuidを取得する。 2.uuidがなければ「Guid.NewGuid()」でuuidを発行する。 3.発行したuuidをキーチェーンに保存する。 これでアプリを端末から削除しても変わらないuuidが使えます。 サンプルソース using UnityEngine; using FSG.iOSKeychain; using System; public class TestManager : MonoBehaviour { public static readonly string KEY_ID = "CustomID"; void Start() { string id = string.Empty; #if UNITY_IOS id = Keychain.GetValue(KEY_ID); if (string.IsNullOrWhiteSpace(id)) { id = Guid.NewGuid().ToString(); Keychain.SetValue(KEY_ID, id); } #elif UNITY_ANDROID id = SystemInfo.deviceUniqueIdentifier; #endif Debug.Log($"id : {id}"); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityでM5stackのIMUdataをBLE通信で受信。IMU情報を画面に表示 & csvファイルに保存するiOSアプリを作成する。

概要紹介 簡単に機能を列挙します。 ① M5stack core からIMU情報をBLE通信で送信する。 ② Unity側で IMU情報をBLE通信で受信する。 ③ 受信したIMU情報を画面上に表示する。 ④ 受信したIMU情報をcsvにて保存する。(内部フォルダ) アプリ化までの設定手順・紹介もいたします。 ※IMUの通信内容・保存内容はあまり整理できていないので、正常な値は未調整です。 環境 ● macOS Big Sur ver11.2.2 - Unity ver : 2019.2.8f1 - Platform : iOS - Unity Api Compatibility Level : .NET 4.x - Unity asset: Bluetooth LE for iOS, tvOS and Android - xcode 12.0 ● iphone X - iOS 14.5 ● M5stark Gray - codeは、Arduino 今回のM5stack側のコード説明は省きます。 ※ xcode, iOS, macOSは常に最新版で合わせています。iOSへのアプリをxcodeで生成するには、Apple developperの登録が必要です。無料でもビルドできますが、ビルド回数が週か月かで制限があります。有料会員だと制限はありません。( 年会費 12,000円くらい) github ・Unityコード ・Arudiunoコード 参考元 Aruduino側設定 今回は、省きます。M5stackの基本的な使い方の説明は多々あると思いますので、そちらをご参照ください。 Unity側設定 ・ファイル → Build Setting → Platform「iOS」 ・Player Settings… → Target minimum iOS Version 「14.5」に設定する。  ※ verを合わせておく。xcodeでも設定可です。  また Configuration の Api Compatibility Level : .NET 4.x に設定する。 ・Unity上で、プログラムの開始ボタンを押した後、cubeの色が赤色になれば、BLE通信の接続ができておりIMUの情報が表示されている状態となります。 xcode用コード生成 ・Build setting の 「Build」します。問題なければ、「Build sucsessful」 が表示され  xcodeファイル「Unity -iPhone.xcodeproj」が生成されます。 xcode用Build設定 ・生成された「Unity -iPhone.xcodeproj」を開きます。「Unity-iPhone」「Product」「info.plist」のcodeを右クリックして 「Open As」「Soruce Code」で開きます。 ・App上で、Bluetoothを使う許可表示をするために、下記のcodeを<dict>に追加します。 <key> NSBluetoothAlwaysUsageDescription</key> <string> What to use bluetooth </string> ・「Unity-iPhone」をダブルクリックして「PROJECT」の「Unity-iPhone」「info」で   iOS Deployment Target を最新版に設定します。現在だと「14.5」 ・「Identity and Type 」の 「Project format」 のxcodeを最新版に設定します。   xcode 12.0 - compatible ・「Unity-iPhone」をダブルクリックして  「TARGETS 」「Unity-iPhone」「Signing & Capabilities」を設定します。  「Automatically manage signing」をチェック  「Team」の選択で、Apple developperの登録したアカウントを選択します。  「 Bundle Identifier」 でエラー(既に使われている的な?)が出ていたら   登録名を変更します。 ・「TARGETS 」「Unity-iPhone Tests」 でも同様に設定をいたします。   「Signing & Capabilities」を設定します。 ・「Unity-iPhone」をダブルクリックして  「TARGETS 」「Unity-iPhone」「Signing & Capabilities」を設定します。  「Automatically manage signing」をチェック  「Team」の選択で、Apple developper 登録したアカウントを選択します。 ・もし csvファイル保存設定がある場合には  「TARGETS」「Unity-iPhone」「info」のKeyの欄で+を押して  「Application supports iTunes file Sharing」  「Supports opening documents in place」   を追加してYESに設定します。 iOSアプリ生成 ・上記の設定が終わったら、Appを実装したいiPhoneとMacを接続して、「Any iOS Device」の欄をクリックして、自分のiPhoneを選択します。「▶️」を押して、Build します。成功したら、iPhone上で自動でAppが起動します。 ・起動したら、Iphoneとの接続を一回切って、iphone側のAppも一度終了します。( なぜかいつも動かない ) ・AppleDevelopperが無料の場合はiphone上で許可設定が必要です。 ・AppleDevelopperが無料の場合は、iphone上で、「設定」「一般」「プロファイルとデバイス管理」で〇〇を信頼をクリックする。 ・Appを立ち上げて、最初にBluetoothの許可がどうたらこうたら表示されるので「はい」で、Appが起動開始する。 Qiita, GitHubの初登録、初投稿なのでわかりにくい部分とかあるかと思いますがまた適宜更新します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityでM5stackのIMUdataをBLE通信で送信。IMU情報を画面に表示 & csvファイルに保存するiOSアプリを作成する。

概要紹介 簡単に機能を列挙します。 ① M5stack core からIMU情報をBLE通信で送信する。 ② Unity側で IMU情報をBLE通信で受信する。 ③ 受信したIMU情報を画面上に表示する。 ④ 受信したIMU情報をcsvにて保存する。(内部フォルダ) アプリ化までの設定手順・紹介もいたします。 ※IMUの通信内容・保存内容はあまり整理できていないので、正常な値は未調整です。 環境 ● macOS Big Sur ver11.2.2 - Unity ver : 2019.2.8f1 - Platform : iOS - Unity Api Compatibility Level : .NET 4.x - Unity asset: Bluetooth LE for iOS, tvOS and Android - xcode 12.0 ● iphone X - iOS 14.5 ● M5stark Gray - codeは、Arduino 今回のM5stack側のコード説明は省きます。 ※ xcode, iOS, macOSは常に最新版で合わせています。iOSへのアプリをxcodeで生成するには、Apple developperの登録が必要です。無料でもビルドできますが、ビルド回数が週か月かで制限があります。有料会員だと制限はありません。( 年会費 12,000円くらい) github ・Unityコード ・Arudiunoコード 参考元 Aruduino側設定 今回は、省きます。M5stackの基本的な使い方の説明は多々あると思いますので、そちらをご参照ください。 Unity側設定 ・ファイル → Build Setting → Platform「iOS」 ・Player Settings… → Target minimum iOS Version 「14.5」に設定する。  ※ verを合わせておく。xcodeでも設定可です。  また Configuration の Api Compatibility Level : .NET 4.x に設定する。 ・Unity上で、プログラムの開始ボタンを押した後、cubeの色が赤色になれば、BLE通信の接続ができておりIMUの情報が表示されている状態となります。 xcode用コード生成 ・Build setting の 「Build」します。問題なければ、「Build sucsessful」 が表示され  xcodeファイル「Unity -iPhone.xcodeproj」が生成されます。 xcode用Build設定 ・生成された「Unity -iPhone.xcodeproj」を開きます。「Unity-iPhone」「Product」「info.plist」のcodeを右クリックして 「Open As」「Soruce Code」で開きます。 ・App上で、Bluetoothを使う許可表示をするために、下記のcodeを<dict>に追加します。 <key> NSBluetoothAlwaysUsageDescription</key> <string> What to use bluetooth </string> ・「Unity-iPhone」をダブルクリックして「PROJECT」の「Unity-iPhone」「info」で   iOS Deployment Target を最新版に設定します。現在だと「14.5」 ・「Identity and Type 」の 「Project format」 のxcodeを最新版に設定します。   xcode 12.0 - compatible ・「Unity-iPhone」をダブルクリックして  「TARGETS 」「Unity-iPhone」「Signing & Capabilities」を設定します。  「Automatically manage signing」をチェック  「Team」の選択で、Apple developperの登録したアカウントを選択します。  「 Bundle Identifier」 でエラー(既に使われている的な?)が出ていたら   登録名を変更します。 ・「TARGETS 」「Unity-iPhone Tests」 でも同様に設定をいたします。   「Signing & Capabilities」を設定します。 ・「Unity-iPhone」をダブルクリックして  「TARGETS 」「Unity-iPhone」「Signing & Capabilities」を設定します。  「Automatically manage signing」をチェック  「Team」の選択で、Apple developper 登録したアカウントを選択します。 ・もし csvファイル保存設定がある場合には  「TARGETS」「Unity-iPhone」「info」のKeyの欄で+を押して  「Application supports iTunes file Sharing」  「Supports opening documents in place」   を追加してYESに設定します。 iOSアプリ生成 ・上記の設定が終わったら、Appを実装したいiPhoneとMacを接続して、「Any iOS Device」の欄をクリックして、自分のiPhoneを選択します。「▶️」を押して、Build します。成功したら、iPhone上で自動でAppが起動します。 ・起動したら、Iphoneとの接続を一回切って、iphone側のAppも一度終了します。( なぜかいつも動かない ) ・AppleDevelopperが無料の場合はiphone上で許可設定が必要です。 ・AppleDevelopperが無料の場合は、iphone上で、「設定」「一般」「プロファイルとデバイス管理」で〇〇を信頼をクリックする。 ・Appを立ち上げて、最初にBluetoothの許可がどうたらこうたら表示されるので「はい」で、Appが起動開始する。 ※ macとiphoneの接続は純正品でないエラーが発生しますので、注意してください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】動的生成されるGameObjectにInjectする方法

概要 疎結合なクラス設計で開発する際に非常に便利なZenject/Extenjectですが、 扱いの難しさ故に、ちょっとしたことでハマってしまいます。 今回は、動的に生成されたGameObjectがInjectされない問題とその解決策を記載していきます。 Prefabを動的生成する 以下のようなPrefabがあります(以下「プレイヤーPrefab」と表記)。 プレイヤーPrefabにはPlayer.csがアタッチされています。 Player.cs // ゲームステータスを管理するマネージャ [Inject] GameManager manager; void Start() { // ゲームステータスがGOなら処理を行う if(manager.GetGameState == "GO"){ // 好きな処理を書く } } そしてScene内には位置情報だけのPlayerSpawnerオブジェクトがあり、PlayerSpawner.csがアタッチされています。 プレイヤーPrefabはHierarchyには設置せず、PlayerSpawner.csによって動的生成される仕様とします。 PlayerSpawner.cs [SerializeField] GameObject playerPrefab; void Start() { // プレイヤーPrefabを動的生成する Instantiate(playerPrefab, new Vector3(0, 0, 0), Quaternion.identity); } 以上の条件で実行してみると、Player.csにNullReferenceExceptionのエラーが出ます。 エラーの原因はアタッチ先のプレイヤーPrefabが、PlayerSpawnerから動的生成されたことによりInjectされず、managerがnullになってしまうからですね。 解決策 とても簡単で、ZenAutoInjectorをプレイヤーPrefabにアタッチすればOKです。 DI Containerを直接使用し、InstantiatePrefabを行うことでも解決できますが、無駄にコードを増やさずに済むのでZenAutoInjectorを使用するのがおすすめです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Strix】PhotonからStrix Cloudに移行してみた ~RPC & MessageRelay編~

今記事は以下の記事のRPC & MessageRelay編です。 まだメインの記事を読んでない方はそちらから読んでいただけると幸いです。 Strix CloudにおけるRPCとMessageRelayの利用までを解説していきたいと思います。 独自の型をStrixに登録する struct ShootRequest { public Vector3 dir; public float speed; } 例としてこのstructを登録したいとします。 public class MyNetwork: MonoBehaviour { public void Awake() { ObjectFactory.Instance.Register(typeof(ShootRequest)); } } この型を初期化時に登録することで利用することができます。 型の中身がStrixが対応している型であれば、PUN2のようにbyte列に詰め込む作業が必要ありません。 これによって独自の方でも送信することができるようになりました。 RPC [StrixRpc] void RPCMethod() { } StrixBehaviourを継承したクラスにStrixRpc属性を付けることでRPCとして使えるようになります。 UIDの取得方法 UID uid = strixReplicator.ownerUid; メッセージの送信にはUIDという、サーバー側でオブジェクトやルームメンバーを管理するためのIDが必要です。 StrixReplicatorのインスタンスから取得することができます。 RPCの送信方法 1. 特定のルームメンバーでRPCを呼び出す void Rpc(UID to, //送信したいプレイヤーのUID string rpcName, //関数名 RpcSuccessEventHandler successHandler, //成功時 FailureEventHandler failureHandler, //失敗時 params object[] args) //送信するオブジェクト 2. このメンバーでRPCを呼び出す void Rpc(string rpcName, RpcSuccessEventHandler successHandler, FailureEventHandler failureHandler, params object[] args) 3. ルームの現在の所有者に対してのみRPCを呼び出す void RpcToRoomOwner(string rpcName, RpcSuccessEventHandler successHandler, FailureEventHandler failureHandler, params object[] args) 4. 現在のメンバーを除くすべてのメンバーでRPCを呼び出す void RpcToOtherMembers(string rpcName, RpcSuccessEventHandler successHandler, FailureEventHandler failureHandler, params object[] args) 5. 自分を含むすべてのルームメンバーでRPCを呼び出す void RpcToAll(string rpcName, RpcSuccessEventHandler successHandler, FailureEventHandler failureHandler, params object[] args) MessageRelay PUN2のRaiseEventと同等の機能になります。 ネットワークオブジェクトでなくとも送信でき、リフレクションを使わず、パフォーマンス的にも優しいので 個人的にはこっちのほうが好きです。 RPCのように、ネットワークオブジェクトにメッセージが送られるわけではないので注意してください。 指定したUIDのルームにメッセージを送るだけの機能になっています。 リレーメッセージの送信 void SendRoomRelay(object messageToRelay, //送りたい情報 RoomRelayEventHandler handler, //成功時 FailureEventHandler failureHandler, //失敗時 RequestConfig config = null) 自身以外のルームにメッセージを送信します。 void SendRoomDirectRelay(UID to, //送信先のUID object messageToRelay, RoomDirectRelayEventHandler handler, FailureEventHandler failureHandler, RequestConfig config = null) 指定したメンバーのUIDのルームにメッセージを送信します。 リレーメッセージの受信 リレーメッセージの通知イベントを登録することで利用できるようになります。 StrixNetwork.instance.roomSession.roomClient.RoomRelayNotified += RelayNotified; StrixNetwork.instance.roomSession.roomClient.RoomDirectRelayNotified += DirectRelayNotified; 独自にラップしてみる 独自にクラスを作ってラップしているので、サンプルを踏まえて説明します。 public class MyRelay{ static readonly StrixNetwork Instance = StrixNetwork.instance; static Dictionary<RelayType, Action<long, RelayData>> relayActions = new Dictionary<RelayType, Action<long, RelayData>>(); //有効化 public void Enable() { StrixNetwork.instance.roomSession.roomClient.RoomRelayNotified += RelayNotified; StrixNetwork.instance.roomSession.roomClient.RoomDirectRelayNotified += DirectRelayNotified; } //無効化 public void Disable() { StrixNetwork.instance.roomSession.roomClient.RoomRelayNotified -= RelayNotified; StrixNetwork.instance.roomSession.roomClient.RoomDirectRelayNotified -= DirectRelayNotified; } //イベントの種類、Primaryキーとカスタムデータのコールバックを登録 public static void RegisterEvent(RelayType type, Action<long, RelayData> action) { if (!relayActions.TryGetValue(type, out _)) { relayActions.Add(type, action); return; } Debug.LogError($"{type}のActionは既に存在しています!"); } void DirectRelayNotified(NotificationEventArgs<RoomDirectRelayNotification> data) { try { RelayData customData = (RelayData) data.Data.GetMessageToRelay(); HandleRelay(data.Data.GetFromUid(), customData); } catch (Exception e) { Debug.LogException(e); throw; } } void RelayNotified(NotificationEventArgs<RoomRelayNotification> data) { try { RelayData customData = (RelayData) data.Data.GetMessageToRelay(); HandleRelay(data.Data.GetFromUid(), customData); } catch (Exception e) { Debug.LogException(e); throw; } } //受信したメッセージを実行 void HandleRelay(UID fromUid, RelayData customData) { if (relayActions.TryGetValue(customData.GetRelayType(), out Action<long, RelayData> action)) { action.Invoke(Instance.roomMembers[fromUid].GetPrimaryKey(), customData); return; } Debug.LogWarning($"{customData.GetRelayType()}のActionは存在しません!"); } //全てのルームにメッセージを送信 public static void SendRelay(RelayType type, object customData) { RelayData data = RelayData.Create(type, customData); Instance.SendRoomRelay(data, null, args => { Debug.LogWarning($"Relayの送信に失敗しました!\n{args.cause.Message}"); }); } //指定のルームにメッセージを送信 public static void SendDirectRelay(UID uid, RelayType type, object customData) { RelayData data = RelayData.Create(type, customData); Instance.SendRoomDirectRelay(uid, data, null, args => { Debug.LogWarning($"Relayの送信に失敗しました!\n{args.cause.Message}"); }); } //データ送信用の構造体 public struct RelayData { public static RelayData Create(RelayType type, object customData) { return new RelayData() { dataType = type, customData = customData }; } RelayType dataType; object customData; public RelayType GetRelayType() => dataType; public T Cast<T>() { try { return (T) customData; } catch (Exception e) { Debug.LogException(e); throw; } } } } public enum RelayType : byte { Shoot, Damage, } DirectRelayNotified(), RelayNotified()で受信したデータを受け取っています。 また、引数NotificationEventArgsでは、 data.Data.GetFromUid() : 送信元のUID data.Data.GetMessageToRelay() : メッセージのobject を取得することができます。 それを、HandleRelay()内で辞書内のデリゲートを実行しているといった形になっています。 StrixNetwork.instance.roomMembersには、UIDとCustomizableMatchRoomMemberのペアが入っています。 そこでUIDを検索して、CustomizableMatchRoomMember.GetPrimaryKey()でPrimaryKeyを取得しているというわけです。 しかし、UIDは普通にinterfaceなので、辞書で毎回検索して引っ張り出して来るのはあんまりパフォーマンス的には良くないです、、笑 もしかしたらカスタムデータにPrimaryキーを仕込んで置いたほうが早いかも? long型だから8バイトも通信容量食うのはかなり痛いけど、、 使い方 MyRelay.RegisterEvent(RelayType.Shoot, (from, data) => { //受信したカスタムデータをキャスト(ここでは架空の構造体) ShootRequest r = data.Cast<ShootRequest>(); if (playerControllers.TryGetValue(from, out PlayerController player)) { player.OnShootRequest(r); } }); RegisterEvent()にメッセージのタイプと受信した際のデリゲートを渡すことで、登録できます。 PlayerControllerなどプレイヤーに紐づくクラスを適当に作っておいて、それをPrimaryKeyと紐付けた辞書を作っておきます。 そしてメッセージリレーを受信した際にそれを検索して、実行するという仕組みになっています。 実際にこのまま使うのは難しいので、お好みで改良してください。 ルームサーバーへの接続(マッチメイキング)は次の記事で解説します! 【Strix】 PhotonからStrix Cloudに移行してみた ~コールバック編~ ※工事中 参考リンク
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity ML-Agentsで機械学習

UnityでNPCの動きの作り方を学ばなければならないことになったので、流行りの機械学習と組み合わせてNPCの動きを学習できるML-Agentsを利用してみます。 人と協力するAgentを目指しますが、どこまでいけるのやら。 今回はまず、配布リポジトリにある環境を動かせるようになることを目指します。自作環境などは作らないのでご注意ください。 インストール準備 GitHubのML-Agentsトップページにはたくさんのバージョンがありますが、今回は最新の安定版であるrelease18をインストールします。 Unityのインストール(2019.4以降) Python3(ver3.6もしくは3.7が推奨されている)およびpip3のインストール Gitコマンドをターミナルで使えるようにしておく(任意) これらはできているものとして省略します(ごめんちゃい、ここも詳しく書いてると記述量があばば)。 多分調べればすぐ出てきます。 pip3はPython3環境でML-Agentの学習を行うためのパッケージをインストールするために使用します。 GitコマンドはML-Agent Toolkit Repositoryをcloneするために使います。 今回はAnacondaで作った仮想環境上でインストールします。 pyenvでもできるんかな。まあとりあえず、仮想環境上にインストールすることをお勧めします。 そんな安定したパッケージじゃないはずなので。 筆者の環境は Windows10 Pro 20H2 Anaconda3 2021.5 Python3.7.10 pip3 21.1.3 Unity Editor 2020.3.12f です。参考までに。 インストール まずターミナルでML-Agent Toolkit Repositoryをcloneします。 このリポジトリがなくても学習できるようですが、環境を作り学習せねばならないので(僕みたいな)初心者はcloneして、設定・学習済みのものを使用することを推奨します。 git clone --branch release_18_branch https://github.com/Unity-Technologies/ml-agents.git 次にML-AgentsのPythonパッケージをインストールします。 僕はWindows環境だったのでPyTorchを別に先行してインストールしました。 pip3 install torch~=1.7.1 -f https://download.pytorch.org/whl/torch_stable.html pip3 install mlagents==0.27.0 これで一通り必要なもののインストールは終わりです。実際に試してみましょう! Unity Editorで試してみる まずは特に設定をいじらずに、学習済みのモデルを確認してみましょう。 Unity Hubを開いて、リストに追加を選択してください。 次に、先ほどcloneしたリポジトリ(ml-agents)の中にあるProjectフォルダを選択してUnity Hub上にプロジェクトを追加してください。 最初はUnityのバージョンが違う警告が出ると思いますが、Unity 2020.3.12fでも起動できました。 不安な方はバージョンを合わせてUnity Editorをインストールしてください。 Unity HubからProjectを開き、ProjectウィンドウからAssets > ML-Agents > Exampleに移動するといろんなゲームの例があります。 今回はWallJump > Scenes > WallJumpでWallJumpのSceneファイルを開いてみましょう。 いつも通り、タブ下のPlay(▶)ボタンで動きを確認できます。 箱のAgentが壁を飛び越える動きをしているシーンを確認できればオッケーです。 かわいい 学習 さて、ML-Agentsの本筋である学習をしてみましょう。 PyTorchをインストールしたのでお察しかと思いますが、PyTorchによって学習を行います。 ただし、Pythonでコードを書くのではなくyamlファイルでハイパラを編集するだけでよさそうです。 ML-Agentsパッケージ内にスクリプトファイルがあるんでしょう。 あんまり僕のPCに負荷をかけたくないのでこのWallJumpだけ学習します。 cloneしたリポジトリ(ml-agents) > config > ppo > WallJump.yamlを以下のように編集します。 強化学習アルゴリズムはSACも使えて、GAILも可能(?)らしいですが、オーソドックスにPPOでやってみましょう。 max_stepsの回数をめちゃめちゃ減らしただけです。 WallJump.yaml behaviors: BigWallJump: trainer_type: ppo hyperparameters: batch_size: 128 buffer_size: 2048 learning_rate: 0.0003 beta: 0.005 epsilon: 0.2 lambd: 0.95 num_epoch: 3 learning_rate_schedule: linear network_settings: normalize: false hidden_units: 256 num_layers: 2 vis_encode_type: simple reward_signals: extrinsic: gamma: 0.99 strength: 1.0 keep_checkpoints: 5 max_steps: 1000 time_horizon: 128 summary_freq: 20000 SmallWallJump: trainer_type: ppo hyperparameters: batch_size: 128 buffer_size: 2048 learning_rate: 0.0003 beta: 0.005 epsilon: 0.2 lambd: 0.95 num_epoch: 3 learning_rate_schedule: linear network_settings: normalize: false hidden_units: 256 num_layers: 2 vis_encode_type: simple reward_signals: extrinsic: gamma: 0.99 strength: 1.0 keep_checkpoints: 5 max_steps: 1000 time_horizon: 128 summary_freq: 20000 ターミナルでml-agentsフォルダに移動して、以下のコマンドを入力してください。 mlagents-learn config/ppo/WallJump.yaml --run-id=firstwj やってることは、mlagents-learnで学習開始。次の引数でyamlファイルの相対パス指定。 --run-id=hogeで学習履歴をhogeというidで保存、です。 このコマンドを打つと、ターミナルがこんな感じになります。かっこいいな。 Unity EditorのPlayボタン(▶)を押せやい!って出るので素直に押します。 しばらくPlayボタンを押さないまま放置すると勝手に止まります。その時はさっきの学習開始コマンドに--resumeをつけて入力し再開してください。 僕のPCなら5秒ぐらいで終わりました。早すぎた。 学習ファイルの適用 さて、出来上がった学習ファイルは ml-agents > results > firstwj(さっき入力したrun-id) にある.onnxファイルです。 この.onnxファイルを ml-agents > Project > Assets > Examples > WallJump > TFModels に突っ込みます。元のファイルを消したくなければしっかり避けておいてね。 そして、Unity Editor上でシーンに対応したAgentを探します。 今回であれば、Projectウィンドウ上で Project > Assets > ML-Agents > Examples > WallJump > Prefab まで移動してWallJumpArea.prefabを選択すると HierarchyウィンドウにAgentが出現します。 こいつを選択すると、Inspectorウィンドウに赤線で囲ったような、ニューラルネットワークを登録する場所が現れます。 さっき移動した.onnxファイルをProjectウィンドウで見つけ、ドラッグアンドドロップして赤線で囲った部分に登録します。 今回は2つ.onnxファイルがありますがSmall WallとBig Wallを対応させておけばとりあえず問題ないと思います。 No Wall Brainはどちらの.onnxファイルを登録してもオッケーです。 この状態でUnity Editor上でPlayボタン(▶)を押せば、学習したネットワークを用いて行動します。 こんな感じ。さっきと比べてまったくタスクをこなせていませんね。でも飛び跳ねてるのかわいい。 まとめ ML-Agentsのインストール 学習 学習ファイルの適用 をしました。もっといろいろできそうですね。 でもなんというか、この技術を使ってもプレイヤーと協力するAgentはなかなか作れなさそうな気がする。 自分で環境作ったりしてみますかぁ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む