[Claudia][Unity]自然言語によって動く2Dプラットフォーム
はじめに
CysharpからAnthropic Claude APIの.NET用、非公式クライアントライブラリClaudiaが登場しました。このライブラリの登場により、Unity上で簡単にClaude3のAPIを呼び出すことができます。また、Function Callingと呼ばれるLLMの出力から関数を呼び出す機能も簡単に実装できます。今回はClaudiaのチュートリアルとして、自然言語の入力からプレイヤーを操作できる2Dプラットフォームを実施します。
要所のコードを解説したのちに、最後は簡単に試せるサンプルコードを添付しています。使用するライブラリ
今回使用するライブラリとして、Cysharpから登場したAnthropic Claude APIの.NET用、非公式クライアントライブラリClaudiaを使用します。Claudiaの導入方法に関しては、今回は触れませんがClaudiaリポジトリのReadMeを読むことで導入できます。
使用するアセット
2Dプラットフォームを作成するために、今回はUnity Technologiesが作成しているPlatformer Microgameを使用します。こちらのアセットは、2Dプラットフォームの基本が実装されているアセットになります。
成果物
今回作成する成果物は、2Dプラットフォーマーにおけるプレイヤーの操作を、自然言語による入力で可能としたものになります。
図1:自然言語による2Dプラットフォーマーのプレイヤー操作
実装概要
実装概要としては、キャラクターの操作に必要な関数を用意します。ユーザーからClaude3に質問文(自然言語による操作)を投げます。Claude3は用意された関数から、最適な関数と引数を返します。これらの情報を元に、手元で関数を呼び出す形になります。
次の章で、実装の詳細を紹介しています。
実装詳細
まず、ユーザーの自然言語の入力と、利用可能な関数一覧と説明をClaude3に渡します。Claude3は、利用可能な関数で最適な関数を返します。
ここで該当する処理がなけれ関数がなければ関数の呼び出しはありません。
図2:実装概要
関数をClaude3が返す際に、関数に引数があればClaude3は最適な引数を渡します。これによって得られた関数と、引数を手元で実行する形になります。
具体例で考えてみましょう。移動の関数を定義した際に、移動方向(-1,+1)と移動速度(0~1)を引数に持たせます。ユーザーから右に走れという指示があれば、引数として+1と1が渡されます。ゆっくり左に歩けと指示があれば、引数として-1と0.1が渡されます。
図3:FunctionCalling概要
実際に実行される関数内部で、プレイヤーの移動速度を変更してあげることで動作する様になります。
FunctionCallingで呼び出す関数
2Dプラットフォームの操作を自然言語で実現する上で、FunctionCallingで呼び出すためのいくつかの関数を定義する必要があります。
今回のチュートリアルにおいて、適切な関数を用意することが重要になります。今回は、Move関数、Stop関数、Jump関数、RunAndJump関数を用意しました。
Move関数
まず、Move
関数では、移動方向と移動速度を引数に渡されると、PlayerController
のmove
を変更します。
/// <summary>
/// 移動プログラム
/// </summary>
/// <param name="moveDirection">移動方向、左-1、右+1</param>
/// <param name="speed">移動速度0~1</param>
/// <returns></returns>
[ClaudiaFunction]
static bool Move(int moveDirection, float speed)
{
PlayerMoveController.move.x = moveDirection * speed;
return true;
}
図4:Move
Stop関数
Stop
関数が呼び出されるとプレイヤーの動作を停止させます。
/// <summary>
/// 移動を止まる
/// </summary>
/// <returns></returns>
[ClaudiaFunction]
static bool Stop()
{
PlayerMoveController.move.x = 0;
return true;
}
図5:Stop
Jump関数
Jump
関数が呼び出されるとプレイヤーのジャンプフラグをONにして、ジャンプします。
/// <summary>
/// ジャンプ処理
/// </summary>
/// <returns></returns>
[ClaudiaFunction]
static bool Jump()
{
PlayerMoveController.jump = true;
return true;
}
図6:Jump
RunAndJump関数
RunAndJump
処理では走りと、ジャンプの処理を同時に行います。
/// <summary>
/// 走りながらジャンプ
/// </summary>
/// <param name="moveDirection">移動方向、左-1、右+1</param>
/// <param name="speed">移動速度0~1</param>
/// <returns></returns>
static bool RunAndJump(int moveDirection, float speed)
{
PlayerMoveController.jump = true;
PlayerMoveController.move.x = moveDirection * speed;
return true;
}
図7:RunAndJump
Claude3との連携
適切な関数が用意できたら、Claude3 APIを呼び出す処理が必要になります。
Claude3への命令関数が、InstructionClaude3
になります。anthropic
にはAPIキーなどの情報を代入。input
にはClaude3へのユーザーの入力を代入されます。message
はLLMからの応答になります。FunctionTools.SystemPrompt
には先ほど作成した関数の情報があり、それとinput
がClaude3に渡されることで適切な関数をmessage
として返します。
返されたmessage
を元に、FunctionCallingで呼び出す関数を手元で呼び出します。適切な関数が呼び出されていれば、move
と、jump
が変化します。変化した変数をplayerController
に渡してあげることで、playerController
側で移動処理や、ジャンプ処理が実行されます。
private async Task InstructionClaude3()
{
var anthropic = new Anthropic()
{
ApiKey = "APIキー"
};
var input = new Message
{
Role = Roles.User,
Content = instructionField.text,
};
var message = await anthropic.Messages.CreateAsync(new()
{
Model = Models.Claude3Haiku,
MaxTokens = 1024,
System = FunctionTools.SystemPrompt,
StopSequences = new[] { StopSequnces.CloseFunctionCalls },
Messages = new[] { input },
});
await FunctionTools.InvokeAsync(message);
playerController.Move = move;
playerController.Jump = jump;
instructionField.text = "";
}
PlayerControllerの修正
Claudiaとの連携に合わせて、Platformer MicrogameのPlayerControllerを修正する必要があります。具体的な修正点は以下のとおりです。
- Spaceボタンを押したときにJumpさせないで、FunctionCallingによってジャンプ処理へ変更。
- 十字キーによる移動を行わないで、FunctionCallingによる移動処理へ変更。
- moveやjumpをFunctionCallingから呼び出すためのプロパティの作成。
それでは、実際の行数と共に修正点を紹介します。
39行目
FunctionCallinで呼び出すためのプロパティ作成。
public Vector2 Move
{
get => move;
set => move = value;
}
public bool Jump
{
get => jump;
set => jump = value;
}
58行、67~70行目
十字キーによる移動を行わない為、処理の削除。
58行目
- move.x = Input.GetAxis("Horizontal");
67~70行目
- else
-{
- move.x = 0;
-d}
59行目
Spaceボタンを押したときにJumpさせないで、FunctionCallingの結果でジャンプを行う。
-if (jumpState == JumpState.Grounded && Input.GetButtonDown("Jump"))
+if (jumpState == JumpState.Grounded && jump)
完全版のサンプルコード
簡単に今回のチュートリアルが試せるサンプルコードを提示します。
PlayerInstruction
using Claudia;
using System.Threading.Tasks;
using Platformer.Mechanics;
using UnityEngine;
using UnityEngine.UI;
public class PlayerInstruction : MonoBehaviour
{
[SerializeField]
public InputField instructionField;
private Anthropic anthropic;
[SerializeField]private PlayerController playerController;
[SerializeField]private Button button;
public static Vector2 move;
public static bool jump;
void Start()
{
button.onClick.AddListener(OnDecisionButtonClicked);
}
private async void OnDecisionButtonClicked()
{
await InstructionClaude3();
}
private async Task InstructionClaude3()
{
var anthropic = new Anthropic()
{
ApiKey = "APIキー"
};
var input = new Message
{
Role = Roles.User,
Content = instructionField.text,
};
var message = await anthropic.Messages.CreateAsync(new()
{
Model = Models.Claude3Haiku,
MaxTokens = 1024,
System = FunctionTools.SystemPrompt,
StopSequences = new[] { StopSequnces.CloseFunctionCalls },
Messages = new[] { input },
});
await FunctionTools.InvokeAsync(message);
playerController.Move = move;
playerController.Jump = jump;
instructionField.text = "";
}
}
public static partial class FunctionTools
{
/// <summary>
/// 移動プログラム
/// </summary>
/// <param name="moveDirection">移動方向、左-1、右+1</param>
/// <param name="speed">移動速度0~1</param>
/// <returns></returns>
[ClaudiaFunction]
static bool Move(int moveDirection, float speed)
{
PlayerMoveController.move.x = moveDirection * speed;
return true;
}
/// <summary>
/// 移動を止まる
/// </summary>
/// <returns></returns>
[ClaudiaFunction]
static bool Stop()
{
PlayerMoveController.move.x = 0;
return true;
}
/// <summary>
/// ジャンプ処理
/// </summary>
/// <returns></returns>
[ClaudiaFunction]
static bool Jump()
{
PlayerMoveController.jump = true;
return true;
}
/// <summary>
/// 走りながらジャンプ
/// </summary>
/// <param name="moveDirection">移動方向、左-1、右+1</param>
/// <param name="speed">移動速度0~1</param>
/// <returns></returns>
static bool RunAndJump(int moveDirection, float speed)
{
PlayerMoveController.jump = true;
PlayerMoveController.move.x = moveDirection * speed;
return true;
}
}
PlayerController
39行目
FunctionCalling呼び出しのためのプロパティ作成。
public Vector2 Move
{
get => move;
set => move = value;
}
public bool Jump
{
get => jump;
set => jump = value;
}
58行、67~70行目
十字キーによる移動を行わない為、処理の削除。
58行目
- move.x = Input.GetAxis("Horizontal");
67~70行目
- else
-{
- move.x = 0;
-d}
59行目
Spaceボタンを押したときにJumpさせないで、FunctionCallingの結果でジャンプを行う。
-if (jumpState == JumpState.Grounded && Input.GetButtonDown("Jump"))
+if (jumpState == JumpState.Grounded && jump)
おわりに
今回は、Claudiaを使って自然言語で2Dプラットフォーマーを操作するためのチュートリアルを紹介しました。ClaudiaはC#でClaude3を利用する上で、非常に優れたOSSになっています。
想像力次第でなんでもできると思います。この機会にお試しください。
この記事が良いと思った方は、いいねとフォローをお願いします!
Discussion