Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
13
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

Unity ML-Agentsで強化学習できる2Dシューティングゲームをつくってみた

概要

Unity ML-Agentsで強化学習させるのに、これまでサンプルを利用していたのですが、Agentや報酬をどうやって設定すればよいのか、いまいち理解できてなかったので、自分でゲームを作ってみました。

ちなみにゲーム開発経験はありません。
なので、ベースとなるゲームについては下記チュートリアルをほぼそのまま参考にさせていただきました。感謝!

【Unity入門】60分で作るシューティングゲーム 第1回
http://nn-hokuson.hatenablog.com/entry/2016/07/04/213231

実際に強化学習させている様子です。

仕様

ゲームの仕様です。まだ実装できてないものもあります。
強化学習をさせてながめてて面白くなりそうな仕様にしました。

  • 2D
  • シューティング
  • ゲームオーバーなし
  • 自機からミサイルたくさん発射される
  • 自機は左右に移動できる
  • 自機が移動すると勝手にミサイルが発射される
  • 隕石的なものがランダムに落ちてくる
  • 隕石は大きさと質量がランダム
  • 隕石にミサイルが当たると破壊できる
  • 隕石の質量によって破壊に必要なミサイル数が変わる
  • 隕石にミサイルが当たると得点が入る
  • 自機に隕石が当たるとマイナス得点が入る
  • 獲得した得点によって隕石の量が増える
  • 獲得した得点によってミサイルの発射頻度が変わる
  • 得点が画面上部に表示される
  • 得点はリセットされず、維持される
  • 得点の桁数が増えると画面表示量が増えて邪魔になる

手順

GitHubに作成したゲームを置いてます。READMEを参照ください。(なげやり

強化学習させるのにしたこと

本題です。ゲームを作成するところまでは良かったのですが、Unity ML-Agentsで強化学習させるのに、試行錯誤したので、ポイントをまとめてみます。
Unity ML-Agentsのバージョンは0.4.0になります。

オブジェクトの配置とスクリプト

AcademyオブジェクトとBrainは親子関係になるようにGameObjectを新規作成して配置します。
Brainにはml-agents/unity-environment/Assets/ML-Agents/Scripts/Brain.csを紐つけます。

Academyにはml-agents/unity-environment/Assets/ML-Agents/Examples/Template/Scripts/TemplateAcademy.csをコピペしてファイル名とクラス名を変更したコードを紐つけます。

RocketオブジェクトにはチュートリアルだとRocketControllerスクリプトを用意して紐付けれているけれど、これをml-agents/unity-environment/Assets/ML-Agents/Examples/Template/Scripts/TemplateAgent.csをコピペして紐つけなおします。

状態取得

下記記事にあるRayPerception を利用して、隕石の位置を検知しようとしましたが、2Dには対応していませんでした。

【Unity ML-Agents】強化学習で物体を避ける
https://qiita.com/God_KonaBanana/items/e56fcee3ae544be7dfca

なので、泥臭くロケットと隕石の位置を自前で取得してAddVectorObs で設定しています。

状態数は固定である必要があるので、最後に足りない分を0で埋めてます。うーん泥臭い。
隕石オブジェクトを取得するにはオブジェクトにTagを設定しています。

RocketAgent.cs
    public override void CollectObservations()
    {
        AddVectorObs(transform.position.x);
        AddVectorObs(transform.position.y);
        var rockCount = 1;
        foreach (var rock in GameObject.FindGameObjectsWithTag("Rock"))
        {
            AddVectorObs(rock.transform.position.x);
            AddVectorObs(rock.transform.position.y);
            AddVectorObs(rock.GetComponent<RockController>().endurance);

            if (rockCount++ >= 20)
            {
                break;
            }
        }
        for (int i = rockCount;i <= 20;i++)
        {
            AddVectorObs(0f);
            AddVectorObs(0f);
            AddVectorObs(0f);
        }
    }

報酬設定

RocketAgent内で報酬設定するにはAddReward をそのまま呼べるのですが、隕石を破壊した際にも報酬を設定したい。となると玉オブジェクトに実装があるので、どうしようと考えて、以下のようにRocketAgentsにpublicなメソッドを追加して対応しました。

RocketAgent.cs
    public void Rocket_AddReward(float reward)
    {
        AddReward(reward);
        if (reward < 0)
        {
            Done();
        }
    }
BulletController.cs
    GameObject.Find("Rocket").GetComponent<RocketAgent>().Rocket_AddReward(3f);

スピード設定

Academyオブジェクトで学習時のスピードを指定できますが、なにも対応していないと、隕石の発生頻度はあがりつつも、落下スピードはそのままで、超大量の隕石が発生。。。とか挙動が面白くなります。

なので、速度設定している箇所にTime.timeScale を追加して対応しました。

RockController.cs
    void Start () {
        this.fallSpeed = (0.01f + 0.01f * Random.value) * Time.timeScale;
        this.rotSpeed = (5f + 3f * Random.value) * Time.timeScale;
        this.endurance = 1f + Random.value * 5f;
    }

というわけで、強化学習させるための2Dシューティングゲームを作ることができました。
ただ、学習をさせてみると、まだ報酬設定やハイパーパラメータの設定がいまいちなようで、まだまだ詰めが必要な状況です。
興味がある方はぜひここからチューニングしてみてくださいね^^

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
13
Help us understand the problem. What are the problem?