diff --git a/README-JP.md b/README-JP.md new file mode 100644 index 0000000..c3d9781 --- /dev/null +++ b/README-JP.md @@ -0,0 +1,159 @@ +# Aimbot-ParallelEnv +これはUnity ML-Agentsに基づいたFPSゲームのマルチエージェントトレーニング環境で、特定の目標と対応する難易度を生成し、エージェントのアクションに基づいて報酬をフィードバックして強化学習エージェントをトレーニングするための環境です。 + +--- +## Description +プロジェクトは[ML-Agents 2.0.1](https://github.com/Unity-Technologies/ml-agents/tree/develop)を基にして、Unity 2021.3.14f1を使用して開発されています。 +Python側では、[mlagents-envs 0.30.0](https://pypi.org/project/mlagents-envs/)を使用してゲーム環境と通信を行います。 + +--- +## Quick start +プロジェクト[Aimbot-PPO](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-PPO)では、この環境を使用してPPOアルゴリズムのトレーニングが行われています。具体的な使用例は[AimbotEnv.py](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-PPO/src/branch/OffP-FullMNN-V2/Aimbot-PPO-Python/Pytorch/AimbotEnv.py)で記載されています。 + +--- +## ゲーム環境の概要 +本環境は初代Doomの基本モードを模したFPSゲームで、エージェントには異なる目標が与えられます。エージェントは目標の種類と位置、自身の状態に基づいて、撃つかどうか、撃つ方向、移動方向を決定します。この環境には2つのモードと3種類の目標があります:訓練モードとテストモード、そして`FreeTarget`、`GotoTarget`、`AttackTarget`です。`FreeTarget`では、エージェントは武器を使ってエリア内のすべての敵を倒す必要があります。`GotoTarget`と`AttackTarget`では、エリア内に目標ブロックが生成され、エージェントはそれに応じた異なる行動を取る必要があります。さらに、`GotoTarget`と`AttackTarget`では、生成される目標ブロックの構造に応じて5つの異なる難易度に分けられています。 + +--- +## ゲームの概要 +この環境は初代Doomの基本モードを模したFPSゲームで、エージェントには異なる目標が与えられます。エージェントは目標の種類と位置、自身の状態に基づいて、撃つかどうか、撃つ方向、移動方向を決定します。この環境には2つのモードと3種類の目標があります:訓練モードとテストモード、そして`FreeTarget`、`GotoTarget`、`AttackTarget`です。`FreeTarget`では、エージェントは武器を使ってエリア内のすべての敵を倒す必要があります。`GotoTarget`と`AttackTarget`では、エリア内に目標ブロックが生成され、エージェントはそれに応じた異なる行動を取る必要があります。さらに、`GotoTarget`と`AttackTarget`では、生成される目標ブロックの構造によって、5つの異なる難易度に分けられています。 + +--- +## モード +訓練モードとテストモードは、Python側と通信が必要で、この環境から観測されたObservationに基づいてActionを生成し、環境にフィードバックします。環境を起動する際、ゲーム内でユーザーが操作してモードを選択する必要があります。ML-Agentsにはタイムリミットが存在するため、45秒以内にモードを選択する必要があります。45秒を超えると、自動的にゲーム環境から退出します。 + +### 訓練モード +訓練モードに入る前に、手動でスタート画面から`Train-Free`、`Train-Goto`、`Train-Attack`(単一モードの訓練)または`Train-Mix`(全モードの訓練)の中から選択する必要があります。選択後、訓練モードに入りますが、この選択は後で変更できません。複数の難易度がある目標ブロック`Train-Goto`と`Train-Attack`のモードでは、スタート画面に異なる難易度の目標ブロックを生成する確率を調整するパネルが画面の左上にあります。デフォルトでは、各難易度の生成確率は均等となっています。このパネルでは、各モードの難易度の合計確率が1になるように、一つの難易度の確率を調整すると他の難易度の確率が自動的に調整されます。ユーザーは右側のロックボタンをクリックして、特定の難易度の確率をロックまたは解除することができます。このパネルは訓練モードの実行中にも表示され、ユーザーが訓練中に確率パネルを調整すると、次の目標ブロックの生成時に適用されます。 +
![TargetLevelProbabilityPanel](./ReadmeImages/LevelProbabilityPanel.jpg) + +### テストモード +テストモードでは、ユーザーが手動でエージェントに命令を指定する必要があります。画面右上のメニューをクリックすることで、目標ブロックや敵を生成したり、目標モードを切り替えたりすることができます。このモードでは、訓練モードにはない新しいターゲット`StayTarget`が追加されています。ユーザーが特定の目標を指定しない場合やゲームをクリアした場合、`StayTarget`がエージェントに割り当てられます。右上隅のメニューをクリックすると、マウスモードを切り替えて敵(Enemy)を生成するモードや、特定の難易度のターゲットブロックを生成するモードに変更できます。マウスがブロック生成モードで、マウスをゲームエリアに移動すると、生成するオブジェクトのプレビューが表示され、マウスの右クリックで指定した位置に対応する敵やブロックを生成できます。ターゲットブロックを生成すると、対応するターゲットが自動的にエージェントに割り当てられます。`FreeTarget`を割り当てる必要がある場合は、`FreeMode`ボタンをクリックします。`ClearGame`をクリックすると、すべての敵とブロックがクリアされ、エージェントのターゲットが`StayTarget`に設定されます。`StayMode`をクリックすると、エージェントのターゲットを強制的に`StayTarget`に設定できます。 + +--- +## Target + +### FreeTarget +エージェントが`FreeTarget`に割り当てられた場合、エージェントはエリア内のすべての敵を武器で倒す必要があります。このモードでは、ラウンド開始時にエリア内にランダムな数の敵が生成されます(デフォルトは6体)。エージェントはエリアのランダムな位置に生成され、向きは前回のラウンドを引き継ぎます(最初のラウンドの場合はデフォルトで角度0)。エージェントがすべての敵を倒せば勝利と判断され、30秒以内にすべての敵を倒せない場合は失敗と判断されます。 + +### GotoTarget +エージェントが`GotoTarget`に割り当てられた場合、指定された目標エリアの特定の位置に移動することが目標です。このモードでは、ラウンドが始まるとエージェントの位置は前のラウンドを引き継ぎます(最初のラウンドではマップの左下隅にデフォルトで生成されます)。Gotoの目標ブロックは、難易度の確率に応じてエリア内にランダムに生成され、異なる難易度には複数のプリセットブロックが用意されています。これらのプリセットブロックは[`SceneBlocksSet`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/TargetContainer/SceneBlocksSet.cs)によって[Prefabフォルダ](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Prefeb)に保存されています。すべての目標ブロックの合計サイズは10x10で、ブロックが生成される際にはエージェントの位置を避けます。指定された目標エリアは`FireBase`と呼ばれる円形のエリアで、その範囲は目標ブロックを超えることはありませんが、位置とサイズはプリセットブロックによって異なります。エージェントが目標ブロックの中心位置に正常に移動すると勝利と判断され、30秒以内に中心位置に移動できない場合は失敗と判断されます。 + +### AttackTarget +エージェントが`AttackTarget`に割り当てられた場合、目標エリアに火力を集中し、主な目標はブロック内のすべての敵を倒すことです。このモードでのエージェントと目標ブロックの生成戦略、および`FireBase`の構成は`GotoTarget`と同様です。しかし、`AttackTarget`の`FireBase`エリア内には必ず敵が生成されます。エージェントがブロック内のすべての敵を倒せば勝利と判断され、30秒以内にすべての敵を倒せない場合は失敗と判断されます。 + +--- +# Environment +## Observation Space +本強化学習訓練設定では、状態(State)は環境を理解し、分析する上での鍵となります。観測環境は3つの主要な部分で構成されています:`TargetState`(ターゲット状態)、`AgentState`(自機状態)、そして`RaycastState`(レイキャスト状態)。これらの観測データはML-Agentsの`VectorSensor`クラスを通じて収集され、Python側に送信され、意思決定に必要な情報を提供します。Observationのソースコードは[`MLAgentsCustomController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/MLAgentsCustomController.cs)にあるoverrideされた`CollectObservations`関数内に格納されています。 + +### TargetState(ターゲット状態) +`TargetState`は、エージェントが受け取ったTargetとその関連情報を含んで、合計六つあります。ターゲットの種類、x,y,z座標、TargetAreaの直径、そしてエージェントがTargetArea内にいるか、およびそのラウンドの残り時間などの情報を含みます。 +| 番号 | 観測項目 | サイズ | 状態空間 | 説明 | +|------|-----------------|-------|------------|---------------------------------------------------------------------------------------------------------------| +| 0 | Targetの種類 | 1 | 0,1,2,3,4 | 割り当てられたTargetの種類を記述:0=FreeTarget、1=GotoTarget、2=AttackTarget、3=DefenceTarget(未使用)、4=StayTarget | +| 1~3 | Targetの座標 | 3 | 0~47 | Target中のFirebaseの連続空間座標を記述、範囲は0から47の連続値 | +| 4 | FireBaseの直径 | 1 | 1~10 | FireBaseの直径を記述、範囲は1から10の連続値 | +| 5 | InFireBase状態 | 1 | 0,1 | エージェントがFireBase内にいるかどうかを記述、0=False、1=True | +| 6 | 残り時間 | 1 | 0~30 | ラウンドの残り時間を記述、範囲は0から30の連続値 | + +### AgentState(自機の状態) +`AgentState`は、エージェントの武器が攻撃可能な状態かどうか、エージェントのx, y, z座標、およびエージェントの向きの角度を含んでいます。エージェントのGameObjectの向きの角度を直接使用すると、0から360度の変化時に値が大幅に変化してしまいます。より良い周期的な表現を実現するために、エージェントのGameObjectの回転角度のコサインとサインの値を使用してエージェントの向きを表しています。 +| 番号 | 観測項目 | サイズ | 状態空間 | 説明 | +|------|------------|-------|---------|---------------------------------------------------------------------------------------| +| 7 | GunState | 1 | 0,1 | エージェントの武器が使用可能な状態かどうかを記述。0=False、1=True | +| 8~10 | エージェントの座標 | 3 | 0~47 | エージェントの連続空間座標を記述、範囲は0から47の連続値 | +| 11~12| エージェントの向き | 2 | -1~1 | エージェントのGameObjectの回転角度のcosとsin値を計算してエージェントの正面の向きを記述。 | + +### RaycastState(視野状態) +`RaycastState`はエージェント視界内で発射されたレイキャストが検出したオブジェクトのタグとその距離を記録します。タグはラベルエンコーディングとワンホットエンコーディングの2つの方法で記録することができ、デフォルトではラベルエンコーディングが使用されます。これは`CommonParameterContainer`オブジェクトで調整することが可能です。`RaycastState`は[`RaySensors`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/RaySensors.cs)クラスによって管理されており、非均一なレイキャストの分布が実装されています。デフォルトでは、視界の中央15%のエリアに射線がより密集して分布し、両側はより希薄です。中央の密集エリアからは5本の射線が、両側の希薄エリアからはそれぞれ7本の射線が発射されます。各レイキャストが検出可能な最大距離は100ユニットで、その距離を超えると0が返されます。視界の中央エリアのカバー範囲、エリア内のレイキャスト数、検出可能な距離は、各`Agent`のGameObjectのInspectorで調整できます。 +
射線の分布方式 +| 番号 | 観測項目 | サイズ | 状態空間 | 説明 | +|-------------------------------------------------------------------|-------------------|---------------------|--------------|----------------------------------------------------------------------------------------| +| 11~Raynum+11 | TagType(Label) | Raycast数 | 0, 1, 2 | レイキャストが検出した物体のタグ。0=Wall, 1=Enemy, 2=Player, -1=Nothing | +| 11~Raynum * 3+11 | TagType(Onehot) | Raycast数 * 3 | 0, 1 | レイキャストが検出した物体のタグ。ワンホットエンコーディングで記録 | +| Raynum+12~2* Raynum+12(ラベル),
3* Raynum+12~4* Raynum+12(ワンホット) | 距離 | Raycast数 | 0~MaxDistance| レイキャストが検出した物体までの距離。範囲は0〜MaxDistance。MaxDistanceのデフォルトは100 | + +*注:nは各エージェントのRaycast数、MaxDistanceは各エージェントのレイキャストの最大探知距離を指します。* + +--- +## Action Space +FPSゲームにおけるプレイヤーのキーボードとマウスの同時操作をシミュレートするため、本環境のActionSpaceは`Discrete Action`(離散動作)と`Continuous Action`(連続動作)の2つの部分に分かれています。`Discrete Action`はプレイヤーのキーボードによる離散的な操作を模擬し、`Continuous Action`はマウスによる連続的な操作を模擬します。両方のActionSpaceに対応する操作は[`MLAgentsCustomController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/MLAgentsCustomController.cs)内のoverrideされた`Heuristic`関数で参照できます。具体的な実装は[`AgentController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/Character/AgentController.cs)内の`MoveAgent`関数と`CameraControl`関数にあります。 + +### Discrete Action(離散動作) +`Discrete Action`では、エージェントは各方向の移動と攻撃を離散的に操作できます。移動操作には垂直移動と水平移動が含まれ、攻撃操作では武器の操作が可能です。 +| 番号 | 動作 | ActionSpace | 説明 | +|------|-------------------|----------------|---------------------------------------------------------------------------| +| 0 | 垂直移動 | 0, 1, -1 | エージェントをその正面方向の垂直軸で制御。0=停止、1=前進、-1=後退 | +| 1 | 水平移動 | 0, 1, -1 | エージェントをその正面方向の水平軸で制御。0=停止、1=右移動、-1=左移動 | +| 2 | 攻撃 | 0, 1 | エージェントの攻撃操作を制御。0=攻撃しない、1=攻撃する | + +### Continuous Action(連続動作) +`Continuous Action`では、エージェントの視点を制御できます。本環境は初期のDoomのFPS環境を模擬しているため、エージェントの視点は水平方向のみで回転可能と設定されています。 +| 番号 | 動作 | ActionSpace | 説明 | +|------|-------------------|----------------|---------------------------------------------------------------------------------| +| 0 | 水平回転 | -Inf ~ Inf | エージェントの水平方向(左右)の視角回転を制御。正の値で右に、負の値で左に回転する。 | + +--- +## Reward +与えられた各ターゲットにおいて、エージェントが異なるタスクを達成することに注力するため、リワードの設計もそれぞれ異なります。訓練モードでは、リワードは共通リワード「Common Reward」と各ターゲット専用のリワードで構成されています。 + +### Common Reward(共通リワード) +エージェントが訓練中に目標達成に役立つ行動を取るように導き、無意味な動作を避けるために、`Common Reward`では以下のリワードとペナルティが設計されています。ML-AgentsはPythonに直接ラウンド終了の結果を伝えることができないため、他のリワードの値が小さすぎたり大きすぎたりして勝利または失敗を判断できない場合に備えて、ラウンド終了時のリワードには極大値と極小値が追加されています。 +| リワード/ペナルティ | 値/シンボル | 説明 | +|-------------------|----------------|---------------------------------------------------------------| +| 非ターゲット命中 | 3 | エージェントがターゲットではない敵を撃つか倒すと得られるリワード | +| ターゲット命中 | 25 | エージェントがターゲットとなる敵を撃つか倒すと得られるリワード | +| 移動ペナルティ | -0.5 | エージェントがフィールド上を移動するときに与えられるペナルティ | +| 回転ペナルティ | $P_s$ | エージェントが視点を回転するときに与えられるペナルティ | +| 勝利リワード | 999 | ラウンドが終了し、そのラウンドでエージェントが目標を達成したときに与えられるリワード | +| 失敗リワード | 999 | ラウンドが終了し、そのラウンドでエージェントが失敗したときに与えられるリワード | + +エージェントが訓練中に無意味な回転を行わないように、また回転速度が速すぎないように、以下の関数を使用して`SpinPenalty`を与えます。40個のマウス操作の回転記録が閾値に達していない場合、小さなペナルティが与えられます。閾値を超えると、エージェントが同じ方向に持続的に回転していることがわかるので、大きなペナルティが与えられます。ここでのPstは時刻`t`の回転ペナルティ、mouseXtは時刻`t`のエージェントの`Contunuouse Action`内の回転アクションの値を指します。 + +$$ +P_{st} = +\begin{cases} + -|mouseX_t| \cdot 0.06 &, & \left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| < 10 \\ + -\left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| & , & \left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| \geq 10 +\end{cases} +$$ + +### FreeTarget +`FreeTarget`では、主な目標は全ての敵を消滅させることです。そのため、敵を追跡する能力が非常に重要です。このモードでは、2種類の報酬メカニズムが設定されています:視界の中心で敵を検出した場合、エージェントには2の報酬が与えられます。敵を検出しなかった場合、視界の中心から検出された敵までの距離 \(D\) に基づいて2未満の報酬が与えられます。距離の計算は、Raycastで検出された敵にのみ考慮され、距離が0.5に近づくほど、つまり敵の半径がRewardに近づくほど、報酬は2に近づきます。たとえば、下図では`Enemy1`と`Enemy3`との距離のみが計算され、最短距離 \(D_1\) が報酬の計算に使用されます。検出されなかった`Enemy2`と`Enemy4`は除外されます。このモードでは、すべての敵がターゲットと見なされ、敵が倒されたり、ヒットした場合、エージェントは`Common Reward`の`TargetHit Reward`を獲得します。 + +
FacingReward + +| 報酬/ペナルティ | 値/記号 | 説明 | +|----------------|----------------|-----------------------------------------| +| FacingReward | \(R_f\) | 視界の中心と検出された最も近い敵との距離に基づく報酬 | + +$$ +R_{f} = +\begin{cases} + 2 &, &min(D) \leq 0.5 \\ + \frac{1}{\sqrt{\frac{min(D)}{2}}} &,& min(D) \gt 0.5 +\end{cases} +$$ + +### GotoTarget +`GotoTarget`では、主な目標は指定された`FireBase`への移動です。時刻 \(t\) と \(t-1\) でのプレイヤーとターゲット間の距離 \(D_t\) と \(D_{t-1}\) の差に基づいて、各ステップで獲得できる距離報酬 \(R_d\) が設定されています。エージェントが目標に近づくと正の報酬が与えられ、目標から離れると0未満のペナルティが与えられます。プレイヤーが`FireBase`内にいる場合、より高い固定報酬を得ることができますが、この場合`DistanceReward`は効果を発揮しません。このモードでは、すべての敵は攻撃対象とは見なされませんが、敵をヒットまたは倒した場合、`Common Reward`の`NonTargetHit`を獲得できます。 + +| 報酬/ペナルティ | 値/記号 | 説明 | +|----------------|---------|----------------------------------------| +| DistanceReward | \(R_d\) | エージェントが目標外にいる際の目標との距離差に基づく報酬 | +| InAreaReward | 5 | エージェントが目標内にいる際の持続報酬 | + +$$ +R_{d} = (D_{t-1} - D_{t}) \cdot 20 +$$ + +### AttackTarget +`Attack Mode`では、主な目的は目標地点の敵を排除し、エージェントが目標地点に対して行動をとることを奨励することです。したがって、`FreeTarget`と同様に、目標地点への視線の移動も重要です。エージェントが目標地点に面して攻撃する場合、それは抑圧行動と見なされ、より低い`SuppressiveReward`が与えられます。目標地点に面しているが攻撃していない場合、さらに低い`FacingAreaReward`が継続して与えられます。`FireBase`内の敵は目標と見なされ、敵が倒されたりヒットした場合、エージェントは`Common Reward`の`TargetHit Reward`を獲得します。一方、`FireBase`外の敵は目標とは見なされず、敵が倒されたりヒットした場合、エージェントは`Common Reward`の`NonTargetHit Reward`のみを獲得します。 + +| 報酬/ペナルティ | 値/記号 | 説明 | +|-----------------------|---------|------------------------------------------------| +| SuppressiveReward | 5 | エージェントが`FireBase`に対して抑圧攻撃を行った際の報酬 | +| FacingAreaReward | 2 | エージェントが目標に向かっている間の継続報酬 | + +## Side Channel +環境のデバッグやUnityとPython間の非リアルタイム通信を実現するため、この環境ではML-Agentsが提供するSide Channelを使用しています。ここではhuggingfaceの[Custom Side Channels](https://github.com/huggingface/ml-agents-patch/blob/develop/docs/Custom-SideChannels.md)をベースで開発を進みました。Unity側では、Side Channelの実装は[`AimbotSideChannel.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/SideChannel/AimbotSideChannel.cs)と[`AimBotSideChannelController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/SideChannel/AimBotSideChannelController.cs)に位置しています。各ログ情報は`|`でフィールドを区切り、最初のフィールドはログのタイプで、次のフィールドはカスタムログ情報です。この環境では、UnityのSide Channelはすべての`LogType.Warning`と`LogType.Error`タイプのログをPython側に送信します。`LogType.Warning`はラウンド終了後の勝敗情報やUnityからPythonへの指示を伝えます。勝敗情報は`Warning|Result|Win`や`Warning|Result|Lose`のようになります。一方、指令は次のトレーニング終了後にモデルを保存するコマンドで、その内容は`Warning|Command`となります。 \ No newline at end of file diff --git a/README.md b/README.md index dc7b495..73a2ce2 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,43 @@ # Aimbot-ParallelEnv 这是一个基于Unity ML-Agents的基于FPS游戏的多智能体训练环境,用于生成指定目标和对应难度,并且根据Agent的Action反馈Reward来训练强化学习Agent的环境。 +--- ## Description 项目基于[ML-Agents 2.0.1](https://github.com/Unity-Technologies/ml-agents/tree/develop),使用Unity 2021.3.14f1开发。 Python侧则使用[mlagents-envs 0.30.0](https://pypi.org/project/mlagents-envs/)与环境进行通信。 + +--- ## 快速开始 在项目[Aimbot-PPO](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-PPO)中,使用了本环境进行了PPO算法的训练,其中在[AimbotEnv.py](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-PPO/src/branch/OffP-FullMNN-V2/Aimbot-PPO-Python/Pytorch/AimbotEnv.py)中给出了本环境的使用方法。 +--- ## 游戏简介 这是一个模仿初代Doom的基本模式的FPS游戏环境,Agent将会被给到不同的目标,Agent需要根据目标的类型和位置,以及自身的状态,来决定是否开枪射击,射击的方向,以及移动方向。环境内包含两种模式和3种目标,分别是:训练模式和测试模式,以及`FreeTarget`,`GotoTarget`和`AttackTarget`。`FreeTarget`需要Agent使用武器击倒区域内所有的敌人,`GotoTarget`和`AttackTarget`会在区域内生成一个目标区块,Agent需要对目标区块进行不同的对应来完成任务。同时`GotoTarget`和`AttackTarget`中,根据生成目标区块的构造不同,分为了5种不同的难度。 -### 模式介绍 +--- +## 模式介绍 训练模式和测试模式都需要与Python侧进行通信,根据从本环境中观测到的Observation生成Action并传递回本环境。在环境启动时需要使用者在游戏内进行操作来选择模式,由于ML-Agents存在Time limit,所以需要在45s内进行模式选择,超过45s后将自动退出游戏环境。 -#### 训练模式 +### 训练模式 在进入训练模式前,使用者需要从Start界面中,只训练单个模式的`Train-Free`,`Train-Goto`,`Train-Attack`和训练所有模式的`Train-Mix`中进行选择。在选择后,将会进入训练模式,并且注意该选择不能在之后进行更改。对于拥有多种难度的目标区块的模式`Train-Goto`和`Train-Attack`,在Start界面将会有一个用来调整生成不同难易度目标区块的概率的面板,默认的生成概率为平均。面板为了保证该模式各难易度生成概率总和为1,在调整某一难度的生成概率时,其他难度的生成概率将会被自动调整,用户可以通过点击右侧锁定按钮以锁定或解锁某一难度的生成概率。该面板将在训练模式执行中也可见,用户在训练模式执行中对概率面板进行调整后将会在应用在下次目标区块生成时。 -![TargetLevelProbabilityPanel](./ReadmeImages/LevelProbabilityPanel.jpg) -#### 测试模式 +
![TargetLevelProbabilityPanel](./ReadmeImages/LevelProbabilityPanel.jpg) + +### 测试模式 测试模式中,用户需要手动来对Agent下达命令,用户可以通过点击右上角的菜单来执行生成目标区块或敌人,切换目标模式等。在该模式中,比训练模式多出一个新的Target为StayTarget,当用户未指定目标或者清空游戏时将会以将该Target指派给Agent。通过点击右上角的菜单,可以将鼠标的模式切换为生成Enemy,或者生成对应难度Target区块的模式,此时鼠标移动到游戏区域内后将会出现生成物件的预览,内点击鼠标右键则可以在对应的位置生成对应的Enemy或者区块。当生成Target区块时会自动指派对应target给agent,需要指派`FreeTarget`时则需要点击`FreeMode`按钮。通过点击`ClearGame`可以清空所有enemy和区块并将Agent的目标指派为`StayTarget`,通过点击`StayMode`则可以强制将Agent目标指派为`StayTarget`。 -### 目标介绍 +--- +## 目标介绍 -#### FreeTarget +### FreeTarget 在Agent被指派到`FreeTarget`时,Agent需要使用武器击倒区域内所有的敌人。在该模式中,一个回合开始时将会在区域内随机生成一定数量的敌人,默认为6,Agent将会生成在区域的随机位置,Agent朝向继承上一回合,如果是第一回合则是默认角度为0。Agent在成功击倒所有敌人后被判断为胜利,或者超过30s未能成功击倒所有敌人则被判断为失败。 -#### GotoTarget + +### GotoTarget 在Agent被指派到`GotoTarget`时,Agent的目标是移动到指定目标区块的指定位置。在该模式中,一个回合开始时Agent的位置总是继承上一个回合,如果是第一回合将会默认生成在地图的左下角。Goto的目标区块将会根据难度概率在区域内随机生成对应难度的预设区块,不同难易度拥有多个已保存好的预设区块,这些预设区块通过[`SceneBlocksSet`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/TargetContainer/SceneBlocksSet.cs)保存在[Prefab文件夹](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Prefeb)中。所有目标区块的总大小为10*10,生成目标区块时区块将会避开Agent的所在位置。指定目标区域是名为`FireBase`的圆形区域,该区域所覆盖的范围不会超过目标区块,但是它的位置和大小会根据预设区块的不同而不同。Agent在成功移动到目标区块中心位置后被判断为胜利,或者超过30s未能成功移动到目标区块中心位置则被判断为失败。 -#### AttackTarget + +### AttackTarget 在Agent被指派到`AttackTarget`时,Agent的目标是对目标区域进行火力压制,同时主要目标是击倒区块内所有敌人。在该模式中,Agent与目标区块的生成策略还有`FireBase`的构成与`GotoTarget`相同。但是在`AttackTarget`的`FireBase`区域内必定会有Enemy生成。Agent在成功击倒区块内所有敌人后被判断为胜利,或者超过30s未能成功击倒区块内所有敌人则被判断为失败。 +--- # Environment ## Observation Space 在本强化学习训练设置中,观测数据是理解和交互环境的关键。观测环境由三个主要部分组成:`TargetState`(目标状态)、`AgentState`(自机状态)和`RaycastState`(射线探测状态)。这些观测数据通过ML-Agents的`VectorSensor`类进行收集,并发送给Python侧,为决策提供必要的信息。Observation的Source Code位于[`MLAgentsCustomController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/MLAgentsCustomController.cs)中被override的`CollectObservations`函数中。 @@ -49,8 +59,8 @@ Python侧则使用[mlagents-envs 0.30.0](https://pypi.org/project/mlagents-envs/ | 8~10 | Agent坐标 | 3 | 0~47 | 描述Agent的连续空间坐标,取值范围为0到47之间的连续值 | | 11~12 | Agent朝向 | 2 | -1~1 | 通过计算Agent GameObject旋转角度的余弦和正弦以描述Agent的正面朝向。 | ### RaycastState(射线探测状态) -`RaycastState`记录了视野内发射的射线探测到的Object的Tag及其距离、其中Tag可以通过Label Encoding和OneHot Encoding两种方式来记录,默认使用Label Encoding,可以在Object`CommonParameterContainer`中进行调整。`RaycastState`由[`RaySensors`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/RaySensors.cs)类管理。它实现了射线的非均匀分布,默认视野中间15%的区域射线分布更密集,两侧则较为稀疏,默认中间密集部分共射出5条,两侧稀疏部分各7条。每个射线可探测的对象最远距离为100个单位,超过探测距离则返回0。其中视野中间区域的覆盖范围,区域内RayCast数量以及可探测距离均可以在每个`Agent`GameObject的Inspector中进行调整。 -射线分布方式 +`RaycastState`记录了视野内发射的射线探测到的Object的Tag及其距离、其中Tag可以通过Label Encoding和OneHot Encoding两种方式来记录,默认使用Label Encoding,可以在Object`CommonParameterContainer`中进行调整。`RaycastState`由[`RaySensors`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/RaySensors.cs)类管理。它实现了射线的非均匀分布,默认视野中间15%的区域射线分布更密集,两侧则较为稀疏,默认中间密集部分共射出5条,两侧稀疏部分各7条。 每个射线可探测的对象最远距离为100个单位,超过探测距离则返回0。其中视野中间区域的覆盖范围,区域内RayCast数量以及可探测距离均可以在每个`Agent`GameObject的Inspector中进行调整。 +
射线分布方式 | Num | Observation | Size | State Space | Description | |------------------------------------------------------------------|-----------------|--------------------|---------------|----------------------------------------------------------------------------------| | 11~Raynum+11 | TagType(Label) | Number of Raycasts | 0, 1, 2 | 描述Raycast所探测到物体的Tag, 0=Wall, 1=Enemy, 2=Player, -1=Nothing | @@ -59,6 +69,7 @@ Python侧则使用[mlagents-envs 0.30.0](https://pypi.org/project/mlagents-envs/ *注:n为每个Agent的RayCast数量,MaxDistance为每个Agent的RayCast最大探测距离* +--- ## Action Space 为了模拟FPS游戏中玩家对于键盘和鼠标的同时操作,本环境中的Action Space分为两个部分,分别是`Discrete Action`和`Continuous Action`。其中`Discrete Action`用于模拟玩家对于键盘的离散操作,`Continuous Action`用于模拟玩家对于鼠标的连续操作。两个Action Space的对应操作可以参考于[`MLAgentsCustomController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/MLAgentsCustomController.cs)中被override的`Heuristic`函数。具体实现则于[`AgentController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/Character/AgentController.cs)中的`MoveAgent`和`CameraControl`函数中。 ### Discrete Action @@ -74,6 +85,7 @@ Python侧则使用[mlagents-envs 0.30.0](https://pypi.org/project/mlagents-envs/ |-----|-------------------|--------------|---------------------------------------------------------------------------------| | 0 | Vertical Rotation | -Inf~Inf | 控制Agent在水平方向(左右)上的视角旋转。正值使视角向右,负值则使视角向左旋转。 | +--- ## Reward 在各给定的目标中,为了让Agent注重于完成不同的任务,Reward的设计也有所不同。在训练模式中,Reward由共同Reward`Common Reward`和各个目标的专用Reward组成。 ### Common Reward @@ -98,8 +110,7 @@ $$ ### FreeTarget 在`FreeTarget`中,主要目标是消灭所有敌人,因此,追踪敌人的能力非常关键。本模式设置了两种奖励机制:当视野中心探测到敌人时给到Agent一个为2的Reward,当未探测到敌人时,则根据探测到的敌人距离视野中心的距离 $D$ 给到一个小于`2`的Reward。距离的计算仅考虑通过 Raycast 探测到的敌人,且当距离越接近0.5,即Enemy半径则Reward越接近`2`。例如,在图中,只计算了与`Enemy1`和`Enemy3`的距离,并且取用最短距离 $D_1$ 来计算Reward。未探测到的`Enemy2`和`Enemy4`被排除在外。在这个模式下,所有敌人都被视为目标,当敌人被击败或击中时,Agent 将获得`Common Reward`中的`TargetHit Reward`。 - -FacingReward +
FacingReward | Reward/Penalty | Value/Symbol | Description | |----------------|----------------------|----------------------------------------| @@ -131,5 +142,6 @@ $$ | SuppressiveReward | 5 | Agent对`FireBase`进行压制攻击时获得奖励 | | FacingAreaReward | 2 | Agent面朝目标时获得的持续奖励 | +--- ## Side Channel 为了方便对于环境的调试和实现Unity与Python间非实时通信,本环境中使用了ML-Agents提供的Side Channel,用于向Python侧传递一些额外的信息。这里我参考了huggingface的[Custom Side Channels](https://github.com/huggingface/ml-agents-patch/blob/develop/docs/Custom-SideChannels.md)。在Unity端,Side Channel的实现位于[`AimbotSideChannel.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/SideChannel/AimbotSideChannel.cs)和[`AimBotSideChannelController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/SideChannel/AimBotSideChannelController.cs)中。每个Log信息将会以`|`来分隔字段,其中第一个字段为Log的类型,随后字段为自定义Log信息。在本环境中,Unity的SideChannel将会发送所有`LogType.Warning`和`LogType.Error`类型的Log到Python侧。其中`LogType.Warning`会传递一回合结束后的的胜败信息和从Unity发送至Python的指令。胜败信息将类似于`Warning|Result|Win`和`Warning|Result|Lose`。而指令则传递了在下一训练结束后保存模型的命令,它的内容为`Warning|Command`。 \ No newline at end of file diff --git a/UserSettings/EditorUserSettings.asset b/UserSettings/EditorUserSettings.asset index 3d09235..76a94ac 100644 --- a/UserSettings/EditorUserSettings.asset +++ b/UserSettings/EditorUserSettings.asset @@ -9,10 +9,10 @@ EditorUserSettings: value: 55090405535008090b5608764626074415151c79747c74602b7c1861e4b96c6c flags: 0 RecentlyUsedSceneGuid-1: - value: 5703025650035e5d090f087148715d4443161e2c7e2c20357b281b31b0e43060 + value: 5453565f53020f085f5d0e72157a0844454f4c2b757d7265757a4f64b7b4313c flags: 0 RecentlyUsedSceneGuid-2: - value: 5453565f53020f085f5d0e72157a0844454f4c2b757d7265757a4f64b7b4313c + value: 5703025650035e5d090f087148715d4443161e2c7e2c20357b281b31b0e43060 flags: 0 RecentlyUsedSceneGuid-3: value: 06570c0704040b0e5a575520137b5e44154f192e292d22367c2c4866b7b3376f diff --git a/UserSettings/Layouts/default-2021.dwlt b/UserSettings/Layouts/default-2021.dwlt index d57d40b..4baee65 100644 --- a/UserSettings/Layouts/default-2021.dwlt +++ b/UserSettings/Layouts/default-2021.dwlt @@ -38,12 +38,12 @@ MonoBehaviour: m_EditorClassIdentifier: m_PixelRect: serializedVersion: 2 - x: 300 - y: 404 + x: 535 + y: 182 width: 1175 height: 749 m_ShowMode: 4 - m_Title: Package Manager + m_Title: Hierarchy m_RootView: {fileID: 5} m_MinSize: {x: 875, y: 300} m_MaxSize: {x: 10000, y: 10000} @@ -97,7 +97,7 @@ MonoBehaviour: m_MinSize: {x: 100, y: 121} m_MaxSize: {x: 4000, y: 4021} vertical: 0 - controlID: 139 + controlID: 105 --- !u!114 &5 MonoBehaviour: m_ObjectHideFlags: 52 @@ -194,7 +194,7 @@ MonoBehaviour: m_MinSize: {x: 300, y: 200} m_MaxSize: {x: 24288, y: 16192} vertical: 0 - controlID: 119 + controlID: 52 --- !u!114 &9 MonoBehaviour: m_ObjectHideFlags: 52 @@ -219,7 +219,7 @@ MonoBehaviour: m_MinSize: {x: 100, y: 200} m_MaxSize: {x: 8096, y: 16192} vertical: 1 - controlID: 120 + controlID: 53 --- !u!114 &10 MonoBehaviour: m_ObjectHideFlags: 52 @@ -256,7 +256,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0} - m_Name: PackageManagerWindow + m_Name: ProjectBrowser m_EditorClassIdentifier: m_Children: [] m_Position: @@ -265,14 +265,14 @@ MonoBehaviour: y: 501 width: 609 height: 198 - m_MinSize: {x: 801, y: 271} - m_MaxSize: {x: 4001, y: 4021} - m_ActualView: {fileID: 19} + m_MinSize: {x: 231, y: 271} + m_MaxSize: {x: 10001, y: 10021} + m_ActualView: {fileID: 18} m_Panes: - {fileID: 18} - {fileID: 19} - m_Selected: 1 - m_LastSelected: 0 + m_Selected: 0 + m_LastSelected: 1 --- !u!114 &12 MonoBehaviour: m_ObjectHideFlags: 52 @@ -297,7 +297,7 @@ MonoBehaviour: m_MinSize: {x: 100, y: 200} m_MaxSize: {x: 8096, y: 16192} vertical: 1 - controlID: 16 + controlID: 78 --- !u!114 &13 MonoBehaviour: m_ObjectHideFlags: 52 @@ -397,8 +397,8 @@ MonoBehaviour: m_Tooltip: m_Pos: serializedVersion: 2 - x: 300 - y: 434 + x: 535 + y: 212 width: 608 height: 480 m_ViewDataDictionary: {fileID: 0} @@ -635,9 +635,9 @@ MonoBehaviour: m_PlayAudio: 0 m_AudioPlay: 0 m_Position: - m_Target: {x: -375.51465, y: 271.2252, z: -354.55728} + m_Target: {x: 462, y: 315.5, z: 0} speed: 2 - m_Value: {x: -375.51465, y: 271.2252, z: -354.55728} + m_Value: {x: 461.9999, y: 315.5, z: -0.00004226652} m_RenderMode: 0 m_CameraMode: drawMode: 0 @@ -688,9 +688,9 @@ MonoBehaviour: speed: 2 m_Value: {x: -0.31611243, y: -0.22831854, z: 0.07866721, w: -0.9174724} m_Size: - m_Target: 12.124355 + m_Target: 10 speed: 2 - m_Value: 12.124355 + m_Value: 10 m_Ortho: m_Target: 0 speed: 2 @@ -827,8 +827,8 @@ MonoBehaviour: m_Tooltip: m_Pos: serializedVersion: 2 - x: 636 - y: 831 + x: 535 + y: 713 width: 608 height: 177 m_ViewDataDictionary: {fileID: 0} @@ -918,22 +918,22 @@ MonoBehaviour: m_ListAreaState: m_SelectedInstanceIDs: m_LastClickedInstanceID: 0 - m_HadKeyboardFocusLastEvent: 0 + m_HadKeyboardFocusLastEvent: 1 m_ExpandedInstanceIDs: c62300008a5c000000000000 m_RenameOverlay: m_UserAcceptedRename: 0 - m_Name: - m_OriginalName: + m_Name: Train + m_OriginalName: Train m_EditFieldRect: serializedVersion: 2 x: 0 y: 0 width: 0 height: 0 - m_UserData: 0 + m_UserData: 38084 m_IsWaitingForDelay: 0 m_IsRenaming: 0 - m_OriginalEventType: 11 + m_OriginalEventType: 0 m_IsRenamingFilename: 1 m_ClientGUIView: {fileID: 11} m_CreateAssetUtility: @@ -967,10 +967,10 @@ MonoBehaviour: m_Tooltip: m_Pos: serializedVersion: 2 - x: 0 - y: 520 - width: 608 - height: 177 + x: 64 + y: 780 + width: 961 + height: 259 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default @@ -995,8 +995,8 @@ MonoBehaviour: m_Tooltip: m_Pos: serializedVersion: 2 - x: 1 - y: 19 + x: 1144 + y: 212 width: 334 height: 371 m_ViewDataDictionary: {fileID: 0} @@ -1006,9 +1006,9 @@ MonoBehaviour: m_SceneHierarchy: m_TreeViewState: scrollPos: {x: 0, y: 0} - m_SelectedIDs: - m_LastClickedID: 0 - m_ExpandedIDs: 32fbffff + m_SelectedIDs: 5ef40000 + m_LastClickedID: 62558 + m_ExpandedIDs: 9aebffff80ecffff2aeeffff2aefffff60f2ffff22f3ffff72f5ffffbaf5ffffdaf5ffff32fbffff86f50000 m_RenameOverlay: m_UserAcceptedRename: 0 m_Name: @@ -1052,8 +1052,8 @@ MonoBehaviour: m_Tooltip: m_Pos: serializedVersion: 2 - x: 1 - y: 411 + x: 1144 + y: 604 width: 334 height: 286 m_ViewDataDictionary: {fileID: 0} @@ -1108,8 +1108,8 @@ MonoBehaviour: m_Tooltip: m_Pos: serializedVersion: 2 - x: 946 - y: 19 + x: 1480 + y: 212 width: 229 height: 678 m_ViewDataDictionary: {fileID: 0}