前回の記事で、転んでも壊れない強靭な「物理ボディ」を作り上げました。 今回は、そのボディに命を吹き込む「AIの脳みそ(スクリプト)」を作成します。
「機械学習のコード」と聞くと難しそうですが、やることはシンプルに3つだけです。
- 観察(Observation): 「今、体がどうなってる?」をAIに教える。
- 行動(Action): AIの指令を「関節のパワー」に変換する。
- 報酬(Reward): 「前に進んだら褒める」ルールを作る。
これらをまとめた WalkerAgent.cs を作っていきましょう!
第6章:Agentスクリプトの作成と準備
まずはスクリプトファイルを作ります。
- Projectウィンドウで右クリック > Create > C# Script。
- 名前を
WalkerAgentにします(※名前を変えると後で面倒なので統一しましょう)。 - スクリプトを開き、以下のように
Agentクラスを継承させます。
C#
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Actuators;
// MonoBehaviour ではなく Agent を継承するのがポイント!
public class WalkerAgent : Agent
{
// ここに体のパーツを登録するための変数を書きます
public Transform target; // 目標地点(ゴール)
public Transform hips; // 腰(体の中心)
// 各関節のコンポーネントを操作するためにリスト化します
ConfigurableJoint[] joints;
Rigidbody[] rbs;
// 初期位置を覚えるための変数
Vector3 startingPos;
// ゲーム開始時に1回だけ呼ばれる設定
public override void Initialize()
{
// 体についている全てのJointとRigidbodyを取得
joints = GetComponentsInChildren<ConfigurableJoint>();
rbs = GetComponentsInChildren<Rigidbody>();
// スタート地点を記憶
startingPos = hips.position;
}
// エピソード(学習の1回分)が始まるたびに呼ばれるリセット処理
public override void OnEpisodeBegin()
{
// 全身の速度をゼロにして、回転をリセット
foreach (var rb in rbs)
{
rb.linearVelocity = Vector3.zero; // Unity 6では velocity ではなく linearVelocity 推奨
rb.angularVelocity = Vector3.zero;
}
// スタート位置に戻す(少しランダムにずらすと汎用性が上がる)
hips.position = startingPos + new Vector3(Random.Range(-0.5f, 0.5f), 0, 0);
hips.rotation = Quaternion.identity;
}
}
【解説】
OnEpisodeBeginは、転んでやり直しになるたびに呼ばれます。 毎回同じ場所からスタートするとAIが「場所ごとのカンニング」をしてしまうので、Random.Rangeで少し左右にずらすのが「賢いAI」を育てるコツです。
第7章:AIの「目」を作る(CollectObservations)
AIは画面を見ていません。数字で世界を認識します。 「ターゲットまでの距離」や「体の傾き」を数字として渡してあげましょう。
C#
public override void CollectObservations(VectorSensor sensor)
{
// 1. ターゲット(ゴール)との距離と方向
// 今の腰の位置からターゲットへのベクトルを渡します
sensor.AddObservation(target.position - hips.position);
// 2. 腰(Hips)の向き
// 自分がどっちを向いているか、傾いているかを知るため
sensor.AddObservation(hips.forward);
sensor.AddObservation(hips.up);
// 3. 各関節の状態
foreach (var joint in joints)
{
// 関節が今どれくらい曲がっているか?
sensor.AddObservation(joint.transform.localRotation);
// その関節がどれくらいの速さで動いているか?
// (これを入れないと、振動したりカクカクしやすくなります)
sensor.AddObservation(joint.transform.GetComponent<Rigidbody>().angularVelocity);
}
}
【なぜこれが必要?】
目隠しでスイカ割りをすることを想像してください。
「右に3メートル」と言われないと動けませんよね?
AIも同じです。
また、「関節の速度(angularVelocity)」 を渡すのが重要で、
これがないとAIは力の加減が分からず、
常にフルパワーで動いてしまいます。
第8章:AIの「筋肉」を動かす(OnActionReceived)
AIは状況を見て、例えば「右足を30度曲げろ!」といった命令(数値)を出します。 これを受け取って、実際の関節(Configurable Joint)を動かす処理です。
C#
public override void OnActionReceived(ActionBuffers actionBuffers)
{
// AIから送られてきた数値リスト(-1.0 〜 1.0 の範囲)
var continuousActions = actionBuffers.ContinuousActions;
// すべての関節に対してループ処理
for (int i = 0; i < joints.Length; i++)
{
// Configurable Joint の「目標角度(TargetRotation)」を操作します
// AIの数値を、実際の角度(例:-30度〜30度)に変換
float xTarget = continuousActions[i * 3 + 0] * 30f;
float yTarget = continuousActions[i * 3 + 1] * 30f;
float zTarget = continuousActions[i * 3 + 2] * 30f;
// Jointに「この角度になれ!」と命令する
joints[i].targetRotation = Quaternion.Euler(xTarget, yTarget, zTarget);
}
// --- ここでついでに「報酬」も計算します ---
// 【重要】頭(腰)の位置をチェック
// 地面に落ちたら(高さが0.5以下になったら)失敗!
if (hips.position.y < 0.5f)
{
SetReward(-1.0f); // 罰を与える
EndEpisode(); // やり直し
}
else
{
// 生きていれば、ターゲットに向かう速度に応じて報酬を与える
// hips.linearVelocity.z は「前方への速度」です
float moveSpeed = hips.GetComponent<Rigidbody>().linearVelocity.z;
AddReward(moveSpeed * 0.1f);
}
}
【解説:Actionの仕組み】
前回設定したSpring(バネ)は、「targetRotation(目標角度)に戻ろうとする力」でしたね。
AIはこのtargetRotationを毎フレーム操作することで、筋肉のように手足を動かします。
i * 3しているのは、
1つの関節につきX, Y, Zの3軸分の命令を受け取るためです。
第9章:人間がテスト操作できるようにする(Heuristic)
学習を始める前に、「そもそも関節が正しく動くか?」をキーボードでテストできるようにします。 これを書いておかないと、バグったときに原因が「AIの頭が悪い」のか「プログラムがミスってる」のか分かりません。
C#
public override void Heuristic(in ActionBuffers actionBuffersOut)
{
var continuousActionsOut = actionBuffersOut.ContinuousActions;
// スペースキーを押したら「全ての関節を曲げる」テスト
float input = Input.GetKey(KeyCode.Space) ? 1.0f : 0.0f;
for (int i = 0; i < joints.Length; i++)
{
// 全ての関節のX軸に 1.0 を入力してみる
continuousActionsOut[i * 3 + 0] = input;
}
}
第10章:Unityエディタでのコンポーネント設定
スクリプトが書けたので、Unity画面に戻って設定を行います。
10-1. スクリプトのアタッチ
Hierarchyにある ロボットの親オブジェクト(Walker) に、今作った WalkerAgent.cs をドラッグ&ドロップします。
10-2. 変数の割り当て
Inspectorに Target や Hips の枠が表示されているはずです。
- Target: ゴール地点にするオブジェクト(Cubeなどを作って配置)を入れます。
- Hips: ロボットの
mixamorig:Hipsをドラッグして入れます。
10-3. Behavior Parameters の設定
Agentスクリプトをアタッチすると、自動的に Behavior Parameters というコンポーネントも追加されます。ここがAIの設定画面です。
- Behavior Name:
WalkerBrain(好きな名前) - Vector Observation > Space Size:「自動計算」されません!
- ここは手動で計算する必要がありますが、今はとりあえず
100くらいの大きな数字を入れておけばエラーになりません(実行時にコンソールに「実際は〇〇個でした」と出るので、後でその数字に直せばOKです)。
- ここは手動で計算する必要がありますが、今はとりあえず
- Actions > Continuous Actions: ここが重要です。
- 関節の数 × 3軸 です。
- 例えば関節が10個あるなら、
30にします。 - (ここが足りないと
IndexOutOfRangeExceptionエラーが出ます)
10-4. Decision Requester の追加
最後に、Decision Requester というコンポーネントを追加(Add Component)してください。
- Decision Period:
5- 「5フレームに1回、AIに判断させる」という意味です。
- 毎フレーム判断させると動きがプルプル震えてしまうので、5回に1回くらいが人間らしい動きになります。
第11章:動作テスト
いきなり学習させる前に、キーボードで動くか確認します。
Behavior Parametersの Behavior Type をHeuristic Onlyに変更します。- Unityの再生ボタンを押します。
- スペースキーを押してみてください。
- ロボットがビクン!と反応して関節が動けば成功です。
- もし動かなければ、関節の
Configurable Jointの設定(Springなど)か、Actionの数が見直しましょう。
テストがOKなら、Behavior Type を Default に戻しておいてください。
次回予告
これで「体」と「脳」がつながりました! しかしまだAIの脳は「空っぽ」なので、再生してもその場で崩れ落ちるだけです。
次回、いよいよ完結編 【Vol.3:学習実行とパラメータ調整編】 です。
- 「設定ファイル(YAML)」の書き方
- コマンド一発で学習を開始する方法
- 学習ログの見方(ちゃんと賢くなってる?)
- 賢くなった脳みそ(.onnx)をゲームに組み込む方法
ここまで来れば、オリジナル歩行AIの完成は目の前です!
[今回のチェックリスト]
- [ ] Agentスクリプトを作成し、親オブジェクトにアタッチした。
- [ ] HipsとTargetを割り当てた。
- [ ] Behavior Parameters の Action数を(関節数×3)に設定した。
- [ ] Decision Requester を追加した。
- [ ] Heuristicモードでスペースキーを押して体が動いた。

