Unityを使った3Dゲームの作り方(かめくめ)ゲーム開発エンジンUnityを使った3Dゲームの制作に参考になる情報を公開しています。2020.11.08今回はUnityのML-Agentsを使って主人公を追いかける敵キャラクターを強化学習を使って学習させていこうと思います。ML-Agentsに関してはこれまで、UnityでML-Agentsを使った機械学習の環境作りをするUnityでML-Agentsを使った機械学習をする為にML-AgentsコンポーネントのインストールとAnacondaのインストール等の環境作りをしていきます。gametukurikata.com2020.10.19で環境の作成をし、UnityのML-Agentsのサンプルを動かしてみるUnityのML-Agentsのツールキットのサンプルを動かして機械学習をさせてみようと思います。gametukurikata.com2020.10.23でUnityのサンプルを使って実際に学習したゲームオブジェクトを動かす事をしました。今回は自分で一からML-Agentsを使ったプロジェクトを作成してみようという感じです。操作キャラクターである主人公と主人公を追いかける敵キャラクターを作成し、敵キャラクターに強化学習を行って主人公キャラクターを追いかけるようにML-Agentsの設定やスクリプトの作成をしていきます。操作キャラクターである主人公にはCharacterControllerコンポーネントの取り付け等、通常のキャラクターを操作する機能を取り付けるという今までと同じように作成します。操作出来る主人公キャラクターの作成については既に他の記事にありますので詳細については省略します。敵キャラクターにもCharacterControllerコンポーネントを取り付けコライダと移動機能を使います。敵キャラクターの移動は従来のスクリプトを使ったものではなく、ML-Agentsを使って移動させるようにします。敵キャラクターに取り付けるコンポーネントや作成するスクリプトの詳細については後で細かく見ていきます。ゲームの舞台を作成するまずは主人公キャラクターと敵キャラクターが移動をするゲームの舞台を作成します。今回はゲームの舞台を狭くし、強化学習が早く終わるようにします。Emptyを選択し、名前をEnvironmentとします。EnvironmentのインスペクタのTransformの歯車からResetを選択し、PositionのXYZが全て0、RotationのXYZが全て0、ScaleのXYZが全て1となるようにします。このEnvironmentを一つのゲームの舞台の元とします。これは複数の学習の舞台を同時に動かし、学習を早める為です。その為に主人公や敵、障害物等をEnvironmentゲームオブジェクトの子要素に配置し、Environmentゲームオブジェクトを複製するだけでいいようにします。ObjectのPlaneを作成し、名前をFloorとします。FloorのインスペクタでTransformのScaleのXYZを全て3とします。ObjectのCubeをいくつか作成し名前をWallとして、Floorを囲むように配置したり、Floor上に障害物として置きます。全てのWallゲームオブジェクトにはWallタグを作成し、設定しておきます。ここで設定したWallタグはすぐには使いませんが、後でRayPerceptionSensor3Dコンポーネントを使った例の時に使います。それぞれのWallのTransformのScale等を変更し配置してください。ここまでのヒエラルキーは以下のようになります。実際に出来たゲームの舞台は以下のようになります。同じにする必要はありませんが、キャラクターがFloorから落ちないようにします。これでゲームの舞台が出来ました。主人公キャラクターモデルをEnvironmentの子要素に配置します。名前をPlayerと変更します。PlayerのインスペクタでタグをPlayerに設定します。操作出来る主人公キャラクターにCharacterControllerコンポーネントを取り付け、コライダのサイズを調整します。AnimatorControllerを作成し、アニメーションパラメータにFloat型のSpeedを作成します。Idel状態とWalk状態を作成し、Idle→WalkはSpeedが0.1より大きい時Walk→IdleはSpeedが0.1より小さい時に遷移するように状態と遷移を作成します。新しくMainCharacterというスクリプトを作成し、主人公キャラクターに取り付けます。Time.deltaTime);}}これで主人公キャラクターの作成が出来ました。細かい部分は説明していませんが、ここら辺はUnityのアニメーションの切り替えシステムとスクリプトUnityのアニメーションの切り替えシステムであるAnimatorControllerの設定とスクリプトからアニメーションを制御していきます。敵キャラクターのモデルをEnvironmentの子要素に配置します。わたくしの場合はゾンビのキャラクターを使用したので敵キャラクターの名前をZombieAgentとしました。主人公キャラクターと同様にCharacterControllerコンポーネントの取り付けとAnimatorに作成したAnimatorControllerを設定します。敵のキャラクターのAnimatorControllerは主人公キャラクターに設定したものと同じように状態と遷移を作成しアニメーションの設定をします。敵の強化学習と行動のスクリプトを作成する敵の強化学習と行動のスクリプトを新しくZombieAgentとして作成します。この時点ではまだ敵キャラクターに取り付けないでください。強化学習を行うゲームオブジェクトにはMonoBehaviourクラスを継承してクラスを作成するのではなくAgentクラスを継承して作成します。AgentクラスのいくつかのメソッドをZombieAgentクラスでオーバーライドして定義し、そこに処理を書いていきます。以下のようなメソッドがあります。InitializeOnEpisodeBeginCollectObservationsOnActionReceivedHeuristicInitializeメソッドはエージェントの初期化時に呼ばれます。OnEpisodeBeginはエピソード開始時に呼ばれます。CollectObservationsはエージェントが観察するデータの収集時に呼ばれます。OnActionReceivedは観察によって収集した情報を決定した後にアクションを起こす時に呼ばれます。この時の行動によって報酬を与えます。Heuristicは学習状態でない時にテストプレイヤーがキャラクターを操作したりするときに使用します(他には模倣学習時に使います)。学習を開始してから観察データの収集→行動を起こすまでが1ステップでAgentのMaxStepで指定したステップ数を繰り返し、MaxStepを超えたらエピソードが終了します。スクリプト内でEndEpisodeメソッドを呼び出した場合もエピソードが終了します。このステップ毎に報酬を与える事で敵に主人公キャラクターを追いかけるという事を学習させていきます。この後スクリプトを作成していきますが、長いので少しずつ追加していきます。クラスの定義とフィールドまずはクラスの定義とフィールド宣言部分を作成します。GetComponent<Animator>();}}Unity.MLAgentsとUnity.MLAgents.Sensorsをusingディレクティブで指定します。ZombieAgentクラスはAgentクラスから継承して作成します。targetは追いかける対象(主人公キャラクターであるPlayer)をインスペクタで設定します。walkSpeedは歩く速さです。velocityは移動速度です。Startメソッドでは自身のコンポーネントを取得しています。次にエピソード開始時の処理を行うAgentクラスのOnEpisodeBeginメソッドをオーバーライドします。{Reset();}Resetメソッドを呼び出しているだけです。Resetメソッドは後で作成します。次にデータの観察と収集を行うAgentクラスのCollectObservationsメソッドをオーバーライドします。transform.localPosition).normalized;sensor.AddObservation(direction);}今回の場合は敵キャラクターが主人公キャラクターを追いかけるので観察するデータとして自身(敵)のローカル位置と相手(主人公)のローカル位置、相手の方向を観察データに追加します。観察データに追加する場合は必ずではないですが、なるべく正規化したデータを追加した方が学習が早くなるようです。今回の場合はゲームの舞台であるFloorの縦横はそれぞれ15m(FloorのScaleでなく実際の長さ)なので位置は15で割り、相手の方向は.normalizedで正規化します。追加するデータは必ずいつも同じ順番で追加する必要があるようです。ここで追加している観察データが学習に必要なものでないとまったく学習しなかったり思い通りに学習してくれない状態になります。学習させようと思っている意図と関連したデータを追加する必要があります。ここで追加したデータはtarget.localPositionとtransform.localPositionがVector3型なのでXYZの3つのデータが二つ、主人公の方向のデータもVector3なので合計9つの観察データを追加していることになります。Sizeを設定する時に必要になります。プロパティやフィールドのデータはCollectObservationsメソッド内でsensor.AddObservationを使用して観察データを追加せずともプロパティやフィールドにObservableアトリビュートを付ける事で勝手にデータを収集してくれるようです。Handlingを変更する必要があるかもしれません。速さフィールドを観察データに加える場合は以下のようにします。Unity.MLAgents.Sensors.Reflection;を指定しておきます。Sizeの数に加える必要はないようです。CollectObservationsメソッドで観察データを追加せず、RayPerceptionSensor3DやRayPerceptionSensor2D等を使っても観察データの収集が出来ます。RayPerceptionSensor3Dコンポーネントは後で使ってみます。Sizeの数に加える必要はないようです。Sizeに設定するということです。ObservableアトリビュートやRayPerceptionSensor系コンポーネントを使った場合は数については考慮しなくていいという感じです。次に観察したデータを元に起こす行動を起こすメソッドであるAgentクラスのOnActionReceivedメソッドをオーバーライドして作成します。Size分のデータが渡ってきます。AgentクラスのAddRewardメソッドで報酬を与える事が出来ますが、敵には主人公に早く追いついてほしいので、毎ステップマイナス報酬を与える事にします。次にvectorActionに渡ってきたデータを元に入力データinputを作成します。キャラクターの移動処理に関しては通常の処理です。キャラクターが向きを変える時は現在の回転から入力データinputの方向の回転の方向に徐々に回転させています。Quaternion.LookRotationで入力の方向の回転を計算しています。キャラクターの向きの処理に関しては他の記事に記載しているので説明は省略させて頂きます。回転スピードに6というマジックナンバーを使用していますが、これはプロパティやフィールドで定義して使った方がいいと思います。(-_-)自身(敵)と相手(主人公)との距離が1.8mより近くなった場合に1の報酬を与えます。それ以外に敵キャラクターがなんらかの理由でFloorから転落し、TransformのYの位置が-5より下になったら-0.1の報酬を与えます。与える報酬に関して今回与えている報酬は1、-0.1、等ですが、報酬は-1~1の間ぐらいで与えると良いようです。UnityのサンプルのPushBlockでは報酬を5とか与えているので、必ずしも-1~1の間というわけではないですが、大体この間で与えるといいのかもしれません。次にデータのリセットを行うResetメソッドを作成します。true;}Resetメソッドはエピソード開始時に呼ぶので敵キャラクターや主人公キャラクターの位置を変更する処理を記述しています。位置を変更する時は一旦CharacterControllerコンポーネントを無効化してから位置を変え再度有効にしています。これはCharacterControllerが有効なまま位置を変更しても反映されないことがあるからです。UnityのCharacterControllerコンポーネントの落とし穴?UnityのCharacterControllerコンポーネントを使ってキャラクターの衝突判定や移動機能を使うと非常に便利ですが、使い方を間違えると原因不明の不具合に悩まされるかもしれません。次は自分で操作して機能を確認する時の為のAgentクラスのHeuristicメソッドをオーバーライドして作成します。Input.GetAxis("Vertical");}actionOutに入力データをそのまま入れます。OnlyまたはDefaultで学習状態でない時に自分で操作して敵キャラクターを移動出来、機能の確認をすることが出来ます。これでスクリプトの作成が終わりました。ZombieAgentスクリプトの取り付けと設定ZombieAgentスクリプトが出来たのでZombieAgentゲームオブジェクトに取り付けます。Parametersコンポーネントも取り付けられます。これはAgentクラスにRequireComponentアトリビュートでBehaviorParametersスクリプト(コンポーネント)が指定してあるからです。Parametersコンポーネントの設定項目について見ていきます。Nameは動作の識別子でここで指定した名前と同じ名前の学習エージェントは同じデータを共有します。今回はZombieAgentという名前を設定しました。Observationは観察するデータに関しての設定です。SizeはCollectObservationsメソッドで追加した観察データの数を設定します。今回は9つのデータを追加するので9を設定しました。Vectorsは観察データを過去の何ステップ分を保持しておくかのデータです。今回は過去の3ステップ分を保持しておくことにします。Actionは行動のVectorデータに関しての設定です。TypeのDiscreteは離散データで-1、0、1等の整数データでContinuousは浮動小数点データです。Sizeで設定します。TypeをDiscreteにした例は記事の最後に記します。SizeはXとZ方向の入力データの2にします。Modelには実際に学習させたデータを設定しますが、まだ学習していないので現時点では設定しません。DeviceはCPUとGPUどちらを使って学習するかです。今回はCPUにします。Typeは動作のタイプでInferenceは学習データを使って推論でエージェントを動かします。HeuristicはHeuristicメソッドで設定したとおり入力してエージェントを動かします。Defaultだと学習中はInferenceにし、学習をしていない時にプレイボタンを押したらHeuristicにします。Idはセルフプレイ時のチームの定義に使用するようです(使ってないのでわからない)。Allは全てを調べるになります。Observableアトリビュートを取り付けたプロパティやフィールドの扱いについてです。今回はObservableアトリビュートを付けたプロパティやフィールドの観察は行わないのでIgnoreにしています。次にZombieAgentのインスペクタの設定をします。最大ステップ数を5000にします。最大ステップ数を超えるとエピソードが終了し新しいエピソードが開始されOnEpisodeBeginメソッドが呼ばれます。Targetには追いかける対象であるPlayerゲームオブジェクトを設定します。Requesterを取り付けます。Requesterコンポーネントは決定をリクエストします。Periodは決定の周期です。値が大きいほど決定の頻度が多くなります。Decisionsにチェックを入れると決定の間にアクションを取ります。観察→決定→アクション→報酬というステップを繰り返します。Requesterコンポーネントを取り付けていない場合はスクリプトでAgent.RequestDicisionメソッドを呼び出して決定をする必要があります。これで機能が出来上がりました。機能を実行してみるとりあえず機能が出来たのでUnityエディターのプレイボタンを押して実行し、敵を動かして主人公に接近し近づくと位置がリセットされるのを確認してください。このまま実行するとPlayerゲームオブジェクトが動くので一旦PlayerのMainCharacterコンポーネントのチェックを外し入力で動かないようにしておきます。実行してみると以下のように主人公に近づくとエピソードが終了し、新しい位置にセットされ新しいエピソードが開始されるのを確認出来ました。これは作った機能を確認するだけ学習はしていません。確認が終了したらPlayerのMainCharacterコンポーネントを有効化しておいてください。学習環境を複製する機能は出来たので後は敵キャラクターに学習させるだけですが、ひとつの環境だけで学習をさせると時間がかかるので、Environmentを複製して10個にします。Environmentを選択し、Ctrl+Dキーを押すとEnvironmentゲームオブジェクトとその子要素が複製されます。複製されたEnvironment(1)を横に移動させます。次は元のEnvironmentとEnvironment(1)をCtrlキーを押しながら両方選択し、Ctrl+Dキーを押して複製し、そのまま矢印を操作し移動させるという事を繰り返して複製したり移動させたりをします。ヒエラルキーは以下のようになりました。以下のように10個の環境が出来ました。学習設定ファイルを作成する学習設定ファイルを一から作成するのは大変なので、UnityのサンプルのPushBlockの設定ファイルをコピーして使います。Unityツールチップをデスクトップにファイル展開しているとすればC:\Users\ユーザー名\Desktop\ml-agents-release_8\ml-agents-release_8\config\ppoにあるPushBlock.yamlをコピーしデスクトップに貼り付けます。名前をZombieAgent.yamlに変更し、中身も少し変更します。Nameと一致させます)。max_stepsを5.0e6とし10個の学習ステージで全部で5000000回(実際にこんなにステップを実行する必要はないが・・)のステップを行うようにします。summary_freqを5000としコマンドプロンプトに表示される経過を5000ステップ毎に行う事にします。これは現在の平均報酬を定期的に確認したい為です。細かい設定については以下を参照してください。intelli...github.com別の記事に書こうと思いましたが思いのほか専門的な感じなのでやめました・・・・(^_^;)学習を開始する機能が出来たので学習をさせてみましょう。学習をさせるやり方は前回の記事でやったのでおさらいにサラッと書いておきます。Anacondaのコマンドプロンプトでデスクトップに移動します。コマンドプロンプトは以下のようになっているはずです。C:\Users\ユーザー名\Desktop>以下のようにコマンドを打ちます。ML-Agentの環境名ML-Agentの環境名は環境を作った時に付けた名前です。わたくしの記事ではmlagentsという名前で作成しました。ここまででコマンドプロンプトは以下のようになっています。C:\Users\ユーザー名\Desktop>以下のようにコマンドプロンプトにコマンドを打ち学習を開始させます。ZombieAgent_01コマンドプロンプトにUnityEditorのプレイボタンを押すとトレーニングが開始されるというのが表示されたらUnityエディターのプレイボタンを押します。以下のように敵が主人公を追いかけるように学習していきます。学習時はUnityのTimeScaleが20になります。Scaleを1にすることで通常のスピードで敵が学習するのを確認出来ます。TimeScaleを1にすると当然学習スピードが落ちますので特に変更する必要はありません。Unityエディターのプレイボタンを再度押すか、コマンドプロンプトでCtrl+Cキーを押すとと学習が終了します。今回は以下のように5分ほど学習させました。最初は報酬が得られていませんが、だんだん1に近づいています。今回の場合はどんだけがんばっても1ステップで得られる報酬の最大が1なので平均報酬が1に近づくほど安定して主人公に到達出来ているという事になります。学習結果を使って敵の動きを確認する学習結果を使って敵の動きを確認してみます。学習結果は以下のフォルダにZombieAgent.nnという名前で作成されます。C:\Users\ユーザー名\Desktop\results\ZombieAgent_01ZombieAgent.nnファイルをUnityのAssetsフォルダにドラッグ&ドロップします。学習の為に複製したEnvironmentは最初のひとつを除きインスペクタの名前の横のチェックを外し非アクティブにします。ParametersのModelにドラッグ&ドロップして設定します。Unityエディターのプレイボタンを押して敵が主人公を追いかけるかどうかを確認します。以下のように主人公を移動させても敵が追いかけてきます。RayPerceptionSensor3Dを使って観測するエージェントに取り付けたスクリプトのCollectObservationsメソッド内で自分で観測するデータを追加する他に、RayPerceptionSensor3Dコンポーネントを追加することでも指定したタグを持つゲームオブジェクト(コライダ)をレイで探しそのデータを観測することが出来ます。試しに敵にRayPerceptionSensor3Dコンポーネントを取り付けて観測出来るようにしてみます。Emptyを選択し空のゲームオブジェクトを作成して、名前をRaySensorとします。Nameはこのセンサーの名前です。Tagsはセンサーが感知するタグを設定します。今回はWall(壁)とPlayer(主人公)を設定しました。Directionは方向に対するレイの数を設定します。Degreesはレイの角度(全てのレイの範囲角度)を設定します。Radiusはレイが感知した部分で判定する範囲である球の半径を設定します。Lengthはレイの長さを設定します。Maskはどのレイヤーを判断するかです。Raycastsは過去の観測データを何個分保持するかです。Offsetはレイのスタート位置の垂直方向のオフセット値です。Offsetはレイのエンド位置の垂直方向のオフセット値です。Colorはレイがヒットした場合の色を設定します。Colorはレイがヒットしていない場合の色を設定します。RaySensorゲームオブジェクトを敵キャラクターからレイを出す場所に移動させると以下のようにシーンビューでレイのギズモが表示されWallタグを設定した壁やPlayerタグを設定した主人公にヒットしているのがわかります。Sizeに追加する必要はありません。RayPerceptionSensor3Dコンポーネントを使うとレイがぶつかった部分のデータの観測も出来るので色々使えそうですね。現時点でわたくしにはどう使っていいやらわかりませんが・・・・(^_^;)Unityのサンプルを見てどう使っているか確認してみてください。TypeをDiscreteにした場合の設定やスクリプトについても見ておきます。Sizeを3とします。これは前後の入力は入力していない場合、前に入力した場合、後ろに入力した場合の3パターン左右の入力は入力していない場合、右に入力した場合、左に入力した場合の3パターンを用意します。次にZombieAgentスクリプトのOnReceiveActionメソッドとHeuristicメソッドを変更します。Heuristicメソッドです。Sizeを2としたので、それぞれの移動に個別にアクションを設定します。右の矢印キーを押した場合はdiscreteActionOut[1]に1、左の矢印キーの場合は2を入れます。これに対応してOnActionReceivedメソッドも書き換えます。以下はContinuousと同じ処理なので省略}引数はActionBuffers型のactionsの受け取りに変更します。actions.DiscreteActions[0]が前後の動き、actions.DiscreteeActions[1]を左右の動きに対応し、前後と左右でそれぞれ入力値を計算出来るようにしています。入力はtransform.forwardを使ってしまうとキャラクターを回転した時にtransform.forwardが常に変わってしまうのでVector3.forward等のワールド空間の前後を使っています。終わりにこの記事を書くまでに環境の作成からUnity側でのコンポーネントの設定やスクリプトの作成、学習をさせる方法等、様々な事柄を理解する必要があり時間がかかりました。観測データに意味のないデータを入れたり報酬をうまく設定せずにトレーニングを開始し、一向に学習をしてくれない状態になるということも多々あります。(^_^;)いきなり細かい設定の学習をさせるより小さい機能で試して少しずつ複雑な学習をさせるように作っていく方がいいかもしれませんね。使いこなすまでには全然いかないです。((+_+))Unityツールキットのサンプルでは色々な事をやっているのでそちらもどうなっているのか見てみるのも面白いかもしれません。次回があるとしたら模倣学習についても記事に出来たらいいなぁと思います。UnityのML-Agentsで自分で動かしたキャラクターの模倣学習をさせてみるUnityのML-Agentsで自分で動かしたキャラクターのデモを保存し、そのデモを使って模倣学習を行いキャラクターの動きを作ってみます。機械学習UnityのML-Agentsのサンプルを動かしてみるUnityのML-Agentsのツールキットのサンプルを動かして機械学習をさせてみようと思います。機械学習UnityのML-AgentsのAPIリファレンスを自動生成するUnityのML-AgentsのAPIリファレンスを自動生成してスクリプトの作成に役立てたいと思います。機械学習UnityでML-Agentsを使った機械学習の環境作りをするUnityでML-Agentsを使った機械学習をする為にML-AgentsコンポーネントのインストールとAnacondaのインストール等の環境作りをしていきます。UnityのML-Agentsのサンプルを動かしてみるUnityのML-AgentsのAPIリファレンスを自動生成するホーム機械学習スポンサーリンクサイト内検索が出来ますプロフィール管理人のかめくめちゃんです。(-_-)メールはこちらにお願いします(記事に関する質問は送らないでください、返信は期待しないでください(._.))。kamekumechan@gmail.comいずれはSIRENやバイオハザードみたいなアクションゲームを作りたいと思っています。ゲームシナリオが書けないので面白いゲームはなかなか作れません・・・・。(^_^;)名前の由来はカメが好きだからです。プロフィールになってないな・・・・(-.-)Unityをこれから始める人へこれからUnityを使ってみようという方はUnityを使った3Dゲームの作り方(かめくめ)の歩き方Unityを使った3Dゲームの作り方(かめくめ)で、はじめてUnityの学習をする時の当ブログの記事を読む順番を書きました。機能を積み上げていく形になるので便利かも?gametukurikata.com2019.06.21の記事に記載の順番で見て頂くとUnityの基本的な使い方がわかり、簡単なアクションゲームや、TPSゲームの作成までが出来ます。自作タイピングゲームタイピングゲームを作りました。WebGL形式なのでPCのブラウザで遊べます。以下からプレイ出来ます。FinalTypingUnityを使って作成したローマ字入力のタイピングゲームです。gametukurikata.com2022.01.02お知らせ2023/01/24個人的にUnity2022.2.3f1をインストールしました。2022/10/14UnityのIKを使って足の位置と角度を地面に合わせる機能Unityのゲームでキャラクターの両足が別の高さの地面の上にある時にそれぞれの足をそれぞれの地面につけるようにする機能を作成しますgametukurikata.com2016.04.11機能を少し修正しました。2020/09/19コメント欄は閉鎖しました。今までコメントを投稿してくださった方はありがとうございました。(^^)/2018/08/07わたくしに出来る事は既に記事にしてあるので、ブログ右上の検索窓を活用してください。(._.)またブログの最初の方の機能を積み上げていく記事に関しての疑問点は当ブログ記事のスクリプトの説明と寄せられた質問への回答Unityを使った3Dゲームの作り方(かめくめ)の機能積み上げ形式の記事のスクリプトの説明と寄せられた記事の機能についての質問への回答を掲載しています。gametukurikata.com2019.08.01Unityを使い始めたばかりの人がつまづきやすい点を解消するUnityを使い始めたばかりの人が結構な確率で陥る不具合や疑問点を解消していきます。基本的にはブログに寄せられた質問への回答という感じです。gametukurikata.com2018.12.04も参照してみてください。小・中学生によるデジタルコンテンツ開発の全国大会ですuycc.unity3d.jp2022年大会の先行エントリーが開始されているようです。当ブログはハイスピードプランです。WordPressの編集画面の動作が早くなった。当ブログはハイスピードプランです。WordPressの編集画面の動作が早くなった。記事の内容に質問がある方へUnityを使った3Dゲームの作り方(かめくめ)の歩き方UnityのJavaScriptとC#を読み替えてどちらでも対応出来るようにするホーム検索トップサイドバータイトルとURLをコピーしました。