この記事は Iwaken Lab. Advent Calendar 2025 1日目の記事です。
【Unity】XREAL One Pro + XREAL Eye のカメラ映像を Gemini API で解析する
こんにちは、XR エンジニアのイワケンです。
GoogleからAndroid XRが発表され、ARグラスとAIの距離がどんどん縮まっています。そんな中、「ARグラスで見ているものをAIに解析させたい」と思っている方も多いのではないでしょうか。
この記事では、Unity を使って XREAL One Pro + XREAL Eye でカメラ映像をキャプチャし、Gemini API で解析するまでの実装を紹介します。
※ XREAL One でも同じコードで動作します。
この開発は、TokyoNodeXRハッカソンの作品「eX Attendant」で活用しました。
前提知識:XREAL Eye について
XREAL Eye とは
XREAL One / One Pro 単体にはカメラが搭載されていません。XREAL Eye というアクセサリを装着することで、RGBカメラが使えるようになります。
「XREAL Air 2 Ultra にはカメラがあるのでは?」と思われるかもしれませんが、あちらはモノクロのトラッキング用センサーのため、カラー映像の取得には使用できません。
開発時の制約について
開発者アプリでは 3DoF までの対応となります(通常使用時は6DoF)。
開発環境
この記事の実装は、以下の環境で動作確認しています。
- Unity 2022.3.62f2
- XREAL SDK 3.0.0
- UniTask 2.5.0以上
動作確認には XREAL Beam Pro を使用しています。Android スマートフォンでも DP Alt Mode 対応であれば動作しますが、とりあえず試す場合は、Beam Pro での開発がおすすめです。
XREAL SDK の導入
-
XREAL Developer Download から
com.xreal.xr.tar.gzをダウンロード - Unity で「Window」→「Package Manager」を開く
- 左上の「+」→「Add package from tarball...」を選択
- ダウンロードした
com.xreal.xr.tar.gzを選択してインポート - 「Edit」→「Project Settings」→「XR Plug-in Management」で XREAL を有効化
詳細は 公式ドキュメント を参照してください。
Gemini API キーの取得
Google AI Studio にアクセスし、「Get API key」→「Create API key」で取得できます。無料枠で1日約1,500リクエスト使用できるため、開発用途には十分です。
UniTask のインストール
この記事のサンプルコードでは、非同期処理を行うために UniTask を使用しています。
- Unity で「Window」→「Package Manager」を開く
- 左上の「+」→「Add package from git URL...」を選択
- 以下のURLを入力して「Add」をクリック
https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
実装
やりたいことはシンプルで、カメラで撮った画像を Gemini に送信して結果を表示するだけです。
ただし、ここで重要な注意点があります。
プレビュー用の XREALRGBCameraTexture から直接キャプチャすると真っ黒な画像になります。
キャプチャには XREALPhotoCapture を使用してください。この点は公式ドキュメントに明記されておらず、実装時にかなり時間を取られました。
以下、実際のコードを紹介します。
1. カメラの初期化
using System;
using System.Text;
using Unity.XR.XREAL;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using TMPro;
using Cysharp.Threading.Tasks;
/// <summary>
/// XREAL Eye のカメラ映像を Google Gemini API で解析するクラス
///
/// 【注意】本コードは動作確認・学習用のサンプルです。
/// 本番運用では以下の強化を推奨します:
/// - 例外処理の追加(ネットワークエラー、タイムアウトなど)
/// - 画像サイズの制限チェック(Base64文字列長の制限)
/// - リトライロジックの実装
/// - JSONシリアライザの使用(Newtonsoft.Json など)
/// </summary>
public class XREALGemini : MonoBehaviour
{
[Header("UI References")]
[SerializeField] RawImage previewImage;
[SerializeField] RawImage capturedImage;
[SerializeField] TextMeshProUGUI resultText;
[Header("Gemini Settings")]
[SerializeField] string apiKey = "YOUR_GEMINI_API_KEY_HERE";
[SerializeField] string analysisPrompt = "この画像に何が映っていますか?日本語で答えてください。";
// Gemini API 設定(定数化して将来の変更に対応しやすく)
private const string GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/models/";
private const string GEMINI_MODEL = "gemini-2.5-flash";
private const string GEMINI_ACTION = ":generateContent";
// Gemini API URL(APIキーをクエリパラメータで渡す)
private string ApiUrl => $"{GEMINI_BASE_URL}{GEMINI_MODEL}{GEMINI_ACTION}?key={apiKey}";
private XREALRGBCameraTexture m_RGBCameraTexture;
// キャプチャした画像を保持
private string capturedBase64Image;
private Texture2D capturedTexture;
void Start()
{
InitializeXREALCamera();
}
void InitializeXREALCamera()
{
// XREAL Eye の RGB カメラテクスチャを作成
m_RGBCameraTexture = XREALRGBCameraTexture.CreateSingleton();
// カメラキャプチャを開始
m_RGBCameraTexture.StartCapture();
Debug.Log("XREAL Eye Camera initialized");
}
}
2. プレビュー表示
カメラはYUVフォーマットで出力されるため、専用のシェーダーが必要になります。
void Update()
{
// カメラプレビューを更新
UpdateCameraPreview();
}
void UpdateCameraPreview()
{
// YUV フォーマットのテクスチャを取得
var yuvTextures = m_RGBCameraTexture.GetYUVFormatTextures();
if (yuvTextures[0] != null)
{
// プレビュー画像に YUV テクスチャを設定
previewImage.texture = yuvTextures[0];
previewImage.material.SetTexture("_UTex", yuvTextures[1]);
previewImage.material.SetTexture("_VTex", yuvTextures[2]);
}
}
3. 画像キャプチャの準備
ここが重要なポイントです。プレビュー用とキャプチャ用で異なるAPIを使用します。
using System.Linq;
private XREALPhotoCapture photoCapture;
private Resolution cameraResolution;
// XREALPhotoCapture を初期化
async UniTask InitializePhotoCaptureAsync()
{
var tcs = new UniTaskCompletionSource();
XREALPhotoCapture.CreateAsync(false, (captureObject) =>
{
photoCapture = captureObject;
// サポートされている最高解像度を取得
cameraResolution = XREALPhotoCapture.SupportedResolutions
.OrderByDescending(res => res.width * res.height)
.First();
Debug.Log($"PhotoCapture initialized: {cameraResolution.width}x{cameraResolution.height}");
tcs.TrySetResult();
});
await tcs.Task;
}
4. 撮影処理
// キャプチャボタンから呼び出される公開メソッド
public async void Capture()
{
await CaptureAsync();
}
// 非同期でカメラ映像をキャプチャ
public async UniTask CaptureAsync()
{
// PhotoCapture が未初期化なら初期化
if (photoCapture == null)
{
await InitializePhotoCaptureAsync();
}
// PhotoMode を開始
await StartPhotoModeAsync();
// 写真をキャプチャ
await TakePhotoAsync();
// PhotoMode を停止
await StopPhotoModeAsync();
}
// PhotoMode を開始
async UniTask StartPhotoModeAsync()
{
var tcs = new UniTaskCompletionSource();
var cameraParams = new CameraParameters
{
cameraType = Unity.XR.XREAL.CameraType.RGB,
hologramOpacity = 0.0f,
frameRate = 30,
cameraResolutionWidth = cameraResolution.width,
cameraResolutionHeight = cameraResolution.height,
pixelFormat = CapturePixelFormat.PNG,
blendMode = BlendMode.CameraOnly,
captureSide = CaptureSide.Single
};
photoCapture.StartPhotoModeAsync(cameraParams, (result) =>
{
Debug.Log("PhotoMode started");
tcs.TrySetResult();
}, true);
await tcs.Task;
}
// 写真をキャプチャ
async UniTask TakePhotoAsync()
{
var tcs = new UniTaskCompletionSource();
photoCapture.TakePhotoAsync((result, photoCaptureFrame) =>
{
// PNG形式のバイト配列を直接取得
byte[] imageBytes = photoCaptureFrame.TextureData;
// Base64 形式に変換
capturedBase64Image = Convert.ToBase64String(imageBytes);
// Texture2D に変換して表示
var texture = new Texture2D(cameraResolution.width, cameraResolution.height);
photoCaptureFrame.UploadImageDataToTexture(texture);
capturedTexture = texture;
capturedImage.texture = texture;
Debug.Log($"Photo captured: {imageBytes.Length / 1024}KB");
tcs.TrySetResult();
});
await tcs.Task;
}
// PhotoMode を停止
async UniTask StopPhotoModeAsync()
{
var tcs = new UniTaskCompletionSource();
photoCapture.StopPhotoModeAsync((result) =>
{
Debug.Log("PhotoMode stopped");
tcs.TrySetResult();
});
await tcs.Task;
}
5. 解析処理
// 解析ボタンから呼び出される公開メソッド
public async void Analyze()
{
if (string.IsNullOrEmpty(capturedBase64Image))
{
Debug.LogError("No captured image to analyze. Please capture an image first.");
return;
}
await AnalyzeAsync();
}
// 非同期でキャプチャした画像を解析
public async UniTask AnalyzeAsync()
{
await SendToGeminiAsync(capturedBase64Image);
}
6. 撮影と解析を連続実行
// ボタンから呼び出される公開メソッド
public async void CaptureAndAnalyze()
{
await CaptureAndAnalyzeAsync();
}
// 非同期でキャプチャと解析を順番に実行
public async UniTask CaptureAndAnalyzeAsync()
{
// まずキャプチャを実行
await CaptureAsync();
// キャプチャが成功したら解析を実行
if (!string.IsNullOrEmpty(capturedBase64Image))
{
await SendToGeminiAsync(capturedBase64Image);
}
else
{
Debug.LogError("Failed to capture image");
}
}
7. Gemini API に送信
// 非同期で Gemini API に画像を送信
async UniTask SendToGeminiAsync(string base64Image)
{
// 注意: 手書きJSONは簡便だが、プロンプト中に " が含まれると壊れる可能性があります
// 実運用では Newtonsoft.Json などのシリアライザ使用を推奨
// Gemini API 用の JSON データを構築
string jsonData = $@"{{
""contents"": [
{{
""parts"": [
{{
""text"": ""{EscapeJsonString(analysisPrompt)}""
}},
{{
""inline_data"": {{
""mime_type"": ""image/png"",
""data"": ""{base64Image}""
}}
}}
]
}}
]
}}";
Debug.Log("Sending to Gemini API...");
// UnityWebRequest を作成
using (UnityWebRequest request = new UnityWebRequest(ApiUrl, "POST"))
{
byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonData);
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
// Gemini は Authorization ヘッダーではなく、クエリパラメータで API キーを渡す
// リクエスト送信(UniTask で await)
await request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
string response = request.downloadHandler.text;
ParseGeminiResponse(response);
}
else
{
Debug.LogError($"Error: {request.error}");
Debug.LogError($"Response: {request.downloadHandler.text}");
}
}
}
// JSON文字列内の特殊文字をエスケープ
string EscapeJsonString(string str)
{
return str
.Replace("\\", "\\\\")
.Replace("\"", "\\\"")
.Replace("\n", "\\n")
.Replace("\r", "\\r")
.Replace("\t", "\\t");
}
Gemini API は ?key= でAPIキーを渡す点が OpenAI と異なります。
8. レスポンス解析
using TMPro;
[Header("UI References")]
[SerializeField] TextMeshProUGUI resultText;
void ParseGeminiResponse(string jsonResponse)
{
try
{
var response = JsonUtility.FromJson<GeminiResponse>(jsonResponse);
if (response.candidates != null && response.candidates.Length > 0)
{
var candidate = response.candidates[0];
if (candidate.content != null && candidate.content.parts != null && candidate.content.parts.Length > 0)
{
string result = candidate.content.parts[0].text;
if (resultText != null)
{
resultText.text = result;
}
Debug.Log($"Gemini Response: {result}");
}
}
}
catch (Exception e)
{
Debug.LogError($"Failed to parse response: {e.Message}");
Debug.LogError($"Response JSON: {jsonResponse}");
if (resultText != null)
{
resultText.text = $"Failed to parse response: {e.Message}";
}
}
}
// Gemini レスポンス用のデータクラス
[Serializable]
public class GeminiResponse
{
public GeminiCandidate[] candidates;
}
[Serializable]
public class GeminiCandidate
{
public GeminiContent content;
}
[Serializable]
public class GeminiContent
{
public GeminiPart[] parts;
}
[Serializable]
public class GeminiPart
{
public string text;
}
9. クリーンアップ
void OnDestroy()
{
// XREAL Eye のカメラ停止
if (m_RGBCameraTexture != null && m_RGBCameraTexture.IsCapturing)
{
m_RGBCameraTexture.StopCapture();
Debug.Log("XREAL Eye Camera stopped");
}
// PhotoCapture のリソースを解放
if (photoCapture != null)
{
photoCapture.Dispose();
photoCapture = null;
Debug.Log("PhotoCapture disposed");
}
}
実装時の注意点
1. プレビューとキャプチャは別API
| 用途 | 使用するAPI |
|---|---|
| プレビュー | XREALRGBCameraTexture |
| キャプチャ | XREALPhotoCapture |
プレビューから直接キャプチャしようとすると真っ黒な画像になります。この点が最もハマりやすいポイントです。
2. 開発者アプリは3DoFまで
通常使用では6DoFですが、開発者アプリでは3DoFまでの対応となります。公式ドキュメントに明記されていない点なので注意してください。
3. APIキーの管理
サンプルでは直書きしていますが、本番では ScriptableObject や環境変数で管理してください。
本番運用時の注意点
- プライバシー:カメラ映像を外部APIに送信するため、ユーザーへの説明と同意取得が必須です
-
パーミッション:AndroidManifest に
CAMERAとINTERNETを追加してください - エラー処理:サンプルでは省略しているため、本番では適切なエラーハンドリングを追加してください
まとめ
この記事では、XREAL Eye + Gemini API で「見ているものをAIに解析させる」実装を紹介しました。
実装時の最大のポイントは「プレビューとキャプチャで別のAPIを使用する」という点です。ここさえ押さえておけば、スムーズに実装できると思います。
ARグラス × AI の組み合わせは、これから様々な可能性が広がっていく分野だと思います。ぜひこの記事を参考に、実装を試してみていただければと思います。
完全なスクリプト
以下に、今回紹介した実装の完全なスクリプトを掲載します。サンプルのため、エラー処理は最低限となっています。
using System;
using System.Linq;
using System.Text;
using Unity.XR.XREAL;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using TMPro;
using Cysharp.Threading.Tasks;
/// <summary>
/// XREAL Eye のカメラ映像を Google Gemini API で解析するクラス(UniTask + XREALPhotoCapture 版)
///
/// 【注意】本コードは動作確認・学習用のサンプルです。
/// 本番運用では以下の強化を推奨します:
/// - 例外処理の追加(ネットワークエラー、タイムアウトなど)
/// - 画像サイズの制限チェック(Base64文字列長の制限)
/// - リトライロジックの実装
/// - JSONシリアライザの使用(Newtonsoft.Json など)
/// </summary>
public class XREALGemini : MonoBehaviour
{
[Header("UI References")]
[SerializeField] private RawImage previewImage;
[SerializeField] private RawImage capturedImage;
[SerializeField] private TextMeshProUGUI resultText;
[Header("Gemini Settings")]
[SerializeField] private string apiKey = "YOUR_GEMINI_API_KEY_HERE";
[SerializeField] private string analysisPrompt = "この画像に何が映っていますか?日本語で答えてください。";
// Gemini API 設定(定数化して将来の変更に対応しやすく)
private const string GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/models/";
private const string GEMINI_MODEL = "gemini-2.5-flash";
private const string GEMINI_ACTION = ":generateContent";
// Gemini API URL(APIキーをクエリパラメータで渡す)
private string ApiUrl => $"{GEMINI_BASE_URL}{GEMINI_MODEL}{GEMINI_ACTION}?key={apiKey}";
private XREALRGBCameraTexture m_RGBCameraTexture;
private XREALPhotoCapture photoCapture;
private Resolution cameraResolution;
// キャプチャした画像を保持
private string capturedBase64Image;
private Texture2D capturedTexture;
void Start()
{
InitializeXREALCamera();
}
void Update()
{
// カメラプレビューを更新
UpdateCameraPreview();
}
void OnDestroy()
{
// XREAL Eye のカメラ停止
if (m_RGBCameraTexture != null && m_RGBCameraTexture.IsCapturing)
{
m_RGBCameraTexture.StopCapture();
Debug.Log("XREAL Eye Camera stopped");
}
// PhotoCapture のリソースを解放
if (photoCapture != null)
{
photoCapture.Dispose();
photoCapture = null;
Debug.Log("PhotoCapture disposed");
}
}
/// <summary>
/// XREAL Eye のカメラを初期化
/// </summary>
void InitializeXREALCamera()
{
// XREAL Eye の RGB カメラテクスチャを作成
m_RGBCameraTexture = XREALRGBCameraTexture.CreateSingleton();
// カメラキャプチャを開始
m_RGBCameraTexture.StartCapture();
Debug.Log("XREAL Eye Camera initialized");
}
/// <summary>
/// カメラプレビューを更新(YUV フォーマット)
/// </summary>
void UpdateCameraPreview()
{
// YUV フォーマットのテクスチャを取得
var yuvTextures = m_RGBCameraTexture.GetYUVFormatTextures();
if (yuvTextures[0] != null)
{
// プレビュー画像に YUV テクスチャを設定
previewImage.texture = yuvTextures[0];
previewImage.material.SetTexture("_UTex", yuvTextures[1]);
previewImage.material.SetTexture("_VTex", yuvTextures[2]);
}
}
/// <summary>
/// XREALPhotoCapture を初期化
/// </summary>
async UniTask InitializePhotoCaptureAsync()
{
var tcs = new UniTaskCompletionSource();
XREALPhotoCapture.CreateAsync(false, (captureObject) =>
{
photoCapture = captureObject;
// サポートされている最高解像度を取得
cameraResolution = XREALPhotoCapture.SupportedResolutions
.OrderByDescending(res => res.width * res.height)
.First();
Debug.Log($"PhotoCapture initialized: {cameraResolution.width}x{cameraResolution.height}");
tcs.TrySetResult();
});
await tcs.Task;
}
/// <summary>
/// カメラ映像をキャプチャ(公開メソッド:Button などから呼び出し可能)
/// </summary>
public async void Capture()
{
await CaptureAsync();
}
/// <summary>
/// カメラ映像をキャプチャして Texture2D と Base64 形式で保存
/// </summary>
public async UniTask CaptureAsync()
{
// PhotoCapture が未初期化なら初期化
if (photoCapture == null)
{
await InitializePhotoCaptureAsync();
}
// PhotoMode を開始
await StartPhotoModeAsync();
// 写真をキャプチャ
await TakePhotoAsync();
// PhotoMode を停止
await StopPhotoModeAsync();
}
/// <summary>
/// PhotoMode を開始
/// </summary>
async UniTask StartPhotoModeAsync()
{
var tcs = new UniTaskCompletionSource();
var cameraParams = new CameraParameters
{
cameraType = Unity.XR.XREAL.CameraType.RGB,
hologramOpacity = 0.0f,
frameRate = 30,
cameraResolutionWidth = cameraResolution.width,
cameraResolutionHeight = cameraResolution.height,
pixelFormat = CapturePixelFormat.PNG,
blendMode = BlendMode.CameraOnly,
captureSide = CaptureSide.Single
};
photoCapture.StartPhotoModeAsync(cameraParams, (result) =>
{
Debug.Log("PhotoMode started");
tcs.TrySetResult();
}, true);
await tcs.Task;
}
/// <summary>
/// 写真をキャプチャ
/// </summary>
async UniTask TakePhotoAsync()
{
var tcs = new UniTaskCompletionSource();
photoCapture.TakePhotoAsync((result, photoCaptureFrame) =>
{
// PNG形式のバイト配列を直接取得
byte[] imageBytes = photoCaptureFrame.TextureData;
// Base64 形式に変換
capturedBase64Image = Convert.ToBase64String(imageBytes);
// Texture2D に変換して表示
var texture = new Texture2D(cameraResolution.width, cameraResolution.height);
photoCaptureFrame.UploadImageDataToTexture(texture);
capturedTexture = texture;
capturedImage.texture = texture;
Debug.Log($"Photo captured: {imageBytes.Length / 1024}KB");
tcs.TrySetResult();
});
await tcs.Task;
}
/// <summary>
/// PhotoMode を停止
/// </summary>
async UniTask StopPhotoModeAsync()
{
var tcs = new UniTaskCompletionSource();
photoCapture.StopPhotoModeAsync((result) =>
{
Debug.Log("PhotoMode stopped");
tcs.TrySetResult();
});
await tcs.Task;
}
/// <summary>
/// キャプチャした画像を AI で解析(公開メソッド:Button などから呼び出し可能)
/// </summary>
public async void Analyze()
{
if (string.IsNullOrEmpty(capturedBase64Image))
{
Debug.LogError("No captured image to analyze. Please capture an image first.");
return;
}
await AnalyzeAsync();
}
/// <summary>
/// キャプチャした画像を Gemini API で解析
/// </summary>
public async UniTask AnalyzeAsync()
{
await SendToGeminiAsync(capturedBase64Image);
}
/// <summary>
/// カメラ映像をキャプチャして AI で解析(公開メソッド:Button などから呼び出し可能)
/// </summary>
public async void CaptureAndAnalyze()
{
await CaptureAndAnalyzeAsync();
}
/// <summary>
/// カメラ映像をキャプチャして、完了後に Gemini API で解析
/// </summary>
public async UniTask CaptureAndAnalyzeAsync()
{
// まずキャプチャを実行
await CaptureAsync();
// キャプチャが成功したら解析を実行
if (!string.IsNullOrEmpty(capturedBase64Image))
{
await SendToGeminiAsync(capturedBase64Image);
}
else
{
Debug.LogError("Failed to capture image");
}
}
/// <summary>
/// Gemini API に画像を送信して解析
/// </summary>
async UniTask SendToGeminiAsync(string base64Image)
{
// 注意: 手書きJSONは簡便だが、プロンプト中に " が含まれると壊れる可能性があります
// 実運用では Newtonsoft.Json などのシリアライザ使用を推奨
// Gemini API 用の JSON データを構築
string jsonData = $@"{{
""contents"": [
{{
""parts"": [
{{
""text"": ""{EscapeJsonString(analysisPrompt)}""
}},
{{
""inline_data"": {{
""mime_type"": ""image/png"",
""data"": ""{base64Image}""
}}
}}
]
}}
]
}}";
Debug.Log("Sending to Gemini API...");
// UnityWebRequest を作成
using (UnityWebRequest request = new UnityWebRequest(ApiUrl, "POST"))
{
byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonData);
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
// Gemini は Authorization ヘッダーではなく、クエリパラメータで API キーを渡す
// リクエスト送信(UniTask で await)
await request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
string response = request.downloadHandler.text;
ParseGeminiResponse(response);
}
else
{
Debug.LogError($"Error: {request.error}");
Debug.LogError($"Response: {request.downloadHandler.text}");
}
}
}
/// <summary>
/// JSON文字列内の特殊文字をエスケープ
/// </summary>
string EscapeJsonString(string str)
{
return str
.Replace("\\", "\\\\")
.Replace("\"", "\\\"")
.Replace("\n", "\\n")
.Replace("\r", "\\r")
.Replace("\t", "\\t");
}
/// <summary>
/// Gemini からのレスポンスを解析
/// </summary>
void ParseGeminiResponse(string jsonResponse)
{
try
{
var response = JsonUtility.FromJson<GeminiResponse>(jsonResponse);
if (response.candidates != null && response.candidates.Length > 0)
{
var candidate = response.candidates[0];
if (candidate.content != null && candidate.content.parts != null && candidate.content.parts.Length > 0)
{
string result = candidate.content.parts[0].text;
if (resultText != null)
{
resultText.text = result;
}
Debug.Log($"Gemini Response: {result}");
}
}
}
catch (Exception e)
{
Debug.LogError($"Failed to parse response: {e.Message}");
Debug.LogError($"Response JSON: {jsonResponse}");
if (resultText != null)
{
resultText.text = $"Failed to parse response: {e.Message}";
}
}
}
}
// Gemini レスポンス用のデータクラス
[Serializable]
public class GeminiResponse
{
public GeminiCandidate[] candidates;
}
[Serializable]
public class GeminiCandidate
{
public GeminiContent content;
}
[Serializable]
public class GeminiContent
{
public GeminiPart[] parts;
}
[Serializable]
public class GeminiPart
{
public string text;
}
使い方
- スクリプトをGameObjectにアタッチ
- InspectorでUI要素とAPIキーを設定
- ボタンに
CaptureAndAnalyze()を設定
AndroidManifestに CAMERA と INTERNET パーミッションを忘れずに。
