0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unity×gRPCでVR描画データを送信する

Last updated at Posted at 2025-02-27

はじめに

本記事は2025年3月1日(土)に開催した、VRプロフェッショナルアカデミー主催の 『第16回VRフェス ~VRが創るミライ★ARが救うセカイ』virtual reality festival vol.16 にて、VRエキスパートコース16期生として出展させていただいた作品 『ペンと鍵の部屋VR』 の補助資料になります。

こちらの作品で VRアカデミー賞・審査員特別賞 をいただきました!

本プロジェクトでは、VR空間内で描画したものを解析し、アイテムとして生成する仕組み を実装するために gRPC通信 を活用しました。

概要

Unity(MetaQuest3)とgRPCを活用して、VR内の描画データをサーバーへ送信する仕組みについて記述しています。

  • gRPCとは?
    • Googleが開発した高性能なRPC(Remote Procedure Call)フレームワーク
    • ProtoBufを使用して通信データを最適化
    • リアルタイム通信に適している

gRPCのプロトコル定義

以下は、VRの描画データを送信するために作成したProtoBuf定義です。

syntax = "proto3";

option csharp_namespace = "VRAcademyAudition";

package vracademyaudition;

// ヘルスチェック
message HealthCheckRequest {}
message HealthCheckResponse {
    enum ServingStatus {
        UNKNOWN = 0; // 不明な状態
        SERVING = 1; // サービス中
        NOT_SERVING = 2; // サービス停止中
    }
    ServingStatus status = 1; // サービスの状態
    string message = 2; // エラーメッセージなど
}

// 3Dベクトル情報
message Vector3Proto {
    float x = 1;
    float y = 2;
    float z = 3;
}

// 色情報
message Color {
    float r = 1;
    float g = 2;
    float b = 3;
    float a = 4; // アルファ値(透明度)
}

// 線データ
message Line {
    repeated Vector3Proto positions = 1; // LineRenderer.GetPositions()
    float width = 2; // 線の太さ
    Color color = 3; // 線の色
}

// クライアント情報の定義
message ClientInfo {
    enum ClientType {
        UNKNOWN = 0;
        DEVELOPMENT = 1;    // 開発環境
        PRODUCTION = 2;     // 本番環境
    }
    ClientType type = 1;
    string device_id = 2;           // デバイス固有ID
    string device_name = 3;         // デバイス名
    string system_info = 4;         // システム情報
    string app_version = 5;         // アプリケーションバージョン
}

// 描画データの定義
message DrawingData {
    string drawing_id = 1;              // 一意識別子
    string scene_id = 2;                // シーンID
    int64 draw_timestamp = 3;           // 描画タイムスタンプ(UNIX時間)
    repeated Line draw_lines = 4;       // 複数のLineRendererデータ
    Vector3Proto center = 5;            // 描画全体の中心点    
    bool use_ai = 6;                    // AIによる処理を行うかどうか
    string client_id = 7;               // クライアントID
    ClientInfo client_info = 8;         // クライアント情報
    map<string, string> metadata = 9;   // メタデータ
}

// アップロード結果を表すメッセージ
message UploadResponse {
    bool success = 1;        // アップロード成功フラグ
    string message = 2;      // メッセージ(エラー時はエラー内容)
    string upload_id = 3;    // アップロードされたデータの識別子
}

// AI判定結果を表すメッセージ(クライアント)
message ShapeRecognitionClient {
    bool success = 1;         // 処理成功フラグ
    string drawing_id= 2;       // 描画データID
    string prefab_name = 3;     // 生成するプレハブ名
    string error_message = 4;   // エラーメッセージ
}

// AI判定結果を表すメッセージ(サーバー)
message ShapeRecognitionServer {
    string result_id = 1;       // 結果ID
    string drawing_id = 2;      // 描画データID
    string scene_id = 3;        // シーンID
    string shape_id = 4;        // 形状ID
    string prefab_name = 5;     // プレハブ名
    bool success = 6;           // 処理成功フラグ
    int32 score = 7;            // 信頼度スコア
    string reasoning = 8;       // AI判定の理由
    int32 process_time_ms = 9;  // 処理時間(ミリ秒)
    string model_name = 10;     // 利用したモデル名
    string api_response = 11;   // APIのレスポンス
    string error_message = 12;  // エラーメッセージ
    string client_id = 13;      // クライアントID
}

// サービス定義
service DrawingService {
    // ヘルスチェック用
    rpc CheckHealth (HealthCheckRequest) returns (HealthCheckResponse);
    
    // データのアップロード用
    rpc UploadDrawing (DrawingData) returns (UploadResponse);

    // 描画データを処理してAI判定結果を返す
    rpc ProcessDrawing (DrawingData) returns (ShapeRecognitionClient);
}

UnityでのgRPCクライアント実装

Grpc.Net.Clientと**Cysharp.Threading.Tasks(UniTask)**を使用してgRPC通信を非同期処理として実装しました。

1. gRPCクライアントの初期化

using UnityEngine;
using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Net.Http;
using Cysharp.Threading.Tasks;
using Grpc.Net.Client;
using VRAcademyAudition;

public class GrpcClient : MonoBehaviour
{
    [SerializeField] private string serverAddress = "http://localhost:50051";
    private GrpcChannel channel;
    private DrawingService.DrawingServiceClient client;

    private async void Start()
    {
        await InitializeClient();
    }

    private async UniTask InitializeClient()
    {
        channel = GrpcChannel.ForAddress(serverAddress);
        client = new DrawingService.DrawingServiceClient(channel);
        Debug.Log("gRPC Client Initialized");
    }
}

2. サーバーへの描画データ送信

public async UniTask<string> SendDrawingDataAsync(LineRenderer[] lineRenderers, Vector3 center)
{
    List<Line> worldLines = new List<Line>();
    foreach (var lr in lineRenderers)
    {
        worldLines.Add(ConvertToProtoLine(lr));
    }

    var drawingData = new DrawingData
    {
        DrawingId = Guid.NewGuid().ToString(),
        SceneId = "TestScene",
        DrawTimestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
        Center = new Vector3Proto { X = center.x, Y = center.y, Z = center.z },
        UseAi = true,
        ClientId = SystemInfo.deviceUniqueIdentifier,
        DrawLines = { worldLines }
    };

    var uploadResponse = await client.UploadDrawingAsync(drawingData);
    return uploadResponse.Success ? uploadResponse.UploadId : null;
}

3. gRPCのヘルスチェック

public async UniTask<bool> CheckServerHealth()
{
    try
    {
        var request = new HealthCheckRequest();
        var response = await client.CheckHealthAsync(request);
        return response.Status == HealthCheckResponse.Types.ServingStatus.Serving;
    }
    catch (Exception ex)
    {
        Debug.LogError($"Health check failed: {ex.Message}");
        return false;
    }
}

4. サンプルデータ
MetaQuest3から送信するデータの例を示します。

{
  "drawing_id": "1234567890abcdef",
  "scene_id": "TestScene",
  "draw_timestamp": 1715678901234,
  "draw_lines": [
    {
      "positions": [
        { "x": 0.0, "y": 1.0, "z": 2.0 },
        { "x": 1.0, "y": 2.0, "z": 3.0 }
      ],
      "width": 0.05,
      "color": { "r": 1.0, "g": 0.0, "b": 0.0, "a": 1.0 }
    }
  ],
  "center": { "x": 0.5, "y": 1.5, "z": 2.5 },
  "use_ai": true,
  "client_id": "device-123456"
}

まとめ

  • gRPCを活用してVR描画データをサーバーへ効率的に送信
  • UniTaskを活用した非同期処理でスムーズな通信を実現
  • ProtoBufを定義し、軽量かつ高速な通信を可能に

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?