【レースゲーム制作】Unity強化学習でレースAIを作ってみる

こんにちは!今回は前からやりたかったレースゲーム作成第1回目です。

前回の強化学習の勉強でなんとなくUnityでの強化学習の方法は学びました。

今回はこれを活かして2Dのレースゲームを制作する方法について勉強しましたので紹介したいと思います。

あしぺん
あしぺん

ぼくがレースするよ!

準備(コースとキャラクターの作成)

プロジェクトの作成

今回はUnity2Dで制作しました。2DはUnityの新規作成から選択できます。

Unityの新規作成の画面

初期の画面はこんな感じです。

Unity2Dの開始画面

3Dとくらべて平面になっていますね!

2Dを使う場合は2D用のコンポーネントがあり、3Dのときと微妙に使い勝手違うものがあるので注意が必要です。

Unity2Dの軸方向

Unity3Dの場合位置(x, y, z)はxが横方向,zが縦方向,yが高さ方向だったと思います。

しかし2Dの場合は(x, y, z)でxが左右方向,yが上下方向,zが画面に対して奥行方向になります。

位置を取得する場合等ベクトルを操作する場合には注意が必要です。

その他スクリプトなどのこまかい使い方などは公式HPで調べられます。

コースを作る

レースゲームにはコースが必要ですので、まずは適当にコースを作りました。

できたコースはこんな感じ、

作成したコースとキャラクター

高さ20×幅40のロの字の枠を4つの長方形(wall(1), wall(2), wall(3), wall(4))で作成し、真ん中に仕切り(wall)を追加しました。

動かすキャラクターはあしぺん君にお願いしました。

イメージは今いる位置がスタート地点で反時計回りに回る至ってシンプルなコースです。

絵の表示方法

上記の画像ではもう絵が追加されていますが、絵の追加の方法を説明します。

まずはHierarchyCreateから2DObjectの中のSpriteを選択してオブジェクトを追加します。

すると何もないオブジェクトが作成されます。

また、これとは別に絵を準備します。今回はあしぺんを準備しました。

これは私がペイントで作成していたものです。これをAssetSpritesフォルダを作成してその中に追加します。

そしてこのSpriteを先に追加していたNew Spriteに追加するだけです。

そうするとオブジェクトに絵が追加されて画面に表示されます。

オブジェクトに絵が追加された様子

簡単ですね! 壁も同じようにして水色にしました。

あしぺん
あしぺん

水色はどうなんですかね~

もし絵を追加してもシーンには表示されるけど下のゲーム画面には表示されない!という場合はTransformPositionZの位置が-10などになっている可能性が考えられます。PositionZ0にすれば表示される場合がありますので、試してみてください。私はこれに数十分悩みました。

物理判定の追加

コースに使ったwallにはそれぞれキャラクターがぶつかったときに判定を追加するために「Box Colider 2D」のコンポーネントを追加しました。

これであしぺん君が壁にぶつかったときに弾かれます。

同様にあしぺん君にも「Box Colider 2D」を追加します。またあしぺん君は動きますので、物理演算を行います。そこで「Rigidbody 2D」を追加します。今回は上から見たレースゲームですので、Gravity Scale0にしておきます。

Rigidbody 2Dの追加Rigidbodyではないので注意

キーでキャラクターを操作しよう

これで見た目はできました!

あとはキャラクターを操作する仕組みを作ります。今回はキー入力で動かします。つまり「キーボードの上を押すと画面上方に移動する」ということです。

移動のさせかたは色々あるみたいです。こちらのサイトを参考にしました。

今回はレースゲームですのでより自然な方法で動かしたいです。そこで、キー入力中は加速度を増加させる方法にしました。

作成したスクリプトは以下のとおりです。

Input.GetAxis入力された軸に補正をかけて-1から1までの数値で返すものですので、これで入力の値を取得します。ここで、Horizontal Vertical wasd矢印キーを表します。

その後入力した値を好みの値に補正してオブジェクトの物理演算に使っています。

AddForceベクトルの方向に継続的に加速度を加えられるものです。

できたものがこちらです。

おおおおおおおおおおおおおー!

徐々に加速されて、壁にぶつかると跳ね返っていることがわかります。

なんとなくそれっぽいものができました。

AIを作ってみよう

さてここからが本番です。上記のままですとただのソロプレイであまり面白みがありません。

せっかくなのでAIを追加してみたいと思います。

作り方は前回勉強したML-Agentsを使い強化学習で作成することにしました。

基本的な使い方は前回と同じです。

なので、今回は前回のものを流用してコースを一周するAIに挑戦してみました。

Tergetを用意して移動させる

まずは前回と同様にTergetを準備してそこにたどり着くと報酬を与えるといった方法で強化学習をしてみたいと思います。

約200万回学習した結果がこちら

コースの上側にある青い四角が今回のTergetです。

・・・・・?全くですね。

ちなみに下の壁に近づくと強制的にリセットされるようにしているので、スタート地点に戻されます。

くそ~これだと一緒にプレイしても面白くない!

ブロックに分けて報酬を与える

報酬の与え方を見直す必要がありそうです。そこで、下図のようにコースを6つに分割しました。

そしてそれぞれに番号を与えてキャラクターが存在する場所に応じて領域の番号を付与します。

そして図の矢印の方向に領域を移動するときに報酬を与えることにしました。

Tergetは無視することにしました。

また、番号5,0,1にいるときは右側に移動しているときに継続的に報酬を与え、逆に番号2,3,4にいるときには左側に移動しているときに継続的に報酬を与えました。

速度はx軸方向の速度は「rBody.velocity.x」で取得できます。

これでうまく反時計回りに回ってくれるはず!

結果がこちら

あ?あああああああああああああああ”あ”あ”ーー😫😫

もうわけわからん。

ちなみに一定時間毎にマイナス報酬を与えており、ある時間がたつと自動的にリセットさせるようにしています。

ここでBrainに与える要素の存在に気づきました。

対象物までの距離をBrainに与える

これまではキャラクター自身の位置速度Tergetまでの距離Brainに与えていました。Tergetの距離はもう使っていないので、別の要素として、対象物までの距離をBrainに与えることにしました。

こちらを参考にしました。

距離を測るためには「RaycastHit2D」という関数を使います。これでキャラクターから特定の方向にレーザーを発射してそれがぶつかったときにぶつかったものの情報を得ることができます。

以下はキャラクターから上下左右にレーザーを発射して距離を取得する部分とそれを可視化するための部分です。

シーンではこのようになります。

キャラクターからでるレーザー

注意するのはキャラクター自身を発射したレーザーの対象物から除外することです。

自身をRaycastから除外する方法

除外する方法は以下のようにオブジェクトのLayer2:Ignore Raycastを選択するだけです。

これをしないと距離が0になってしまって意味がありません。

これに気づくのにも相当時間がかかりました。トホホ

Brainに与える要素の数が変わりますので、BrainのパラメータのVector ObservationSpace Sizeを調整します。今回は上下左右の距離4つキャラクターの位置3つ(x, y, z)及びキャラクターの速度2つ(x, y)9つです(今思えば位置のzはいらなかったですね)。

学習した結果が以下です。

おおおおおおおおおおおおおー!

一応反時計回りに回っています。

より最適にするほうほうについては現在検討中です。

いざ勝負!

自分で作ったAIと自分自身で勝負してみます。

水色のあしぺん君が私です。

いざ勝負!

一周目はギリギリ勝ちました!

あしぺん
あしぺん

陸地ではしるのは苦手です・・・。

ただし二周目のはじめのコーナーでAIにぶつかると、AIの軌道が変わってしまって逆走していることがわかります。

このAIはイレギュラーが苦手なのかもしれません。

なにはともあれ、ぼっちの私でもソロプレイを脱却できました😢

作成したTempAgent.csを載せておきます。アドバイスお待ちしております。

まとめ

今回はレースゲームの第1回目ということで、コースとキャラクターを作成したのと、強化学習でAIを作成しました。学んだ点は以下のとおりです。

  • Unity2Dのベクトルの考え方を理解する。
  • 強化学習は報酬の与え方が重要
  • Raycastで対象物までの距離を得ることが可能

まだまだ、最適な解ではないと想っておりますので、これから先もガンガン進化させていきたいと思いますので、よろしくお願いいたします!!

参考サイト

コメント

タイトルとURLをコピーしました