kintoneGeeks blog

kintoneに関連する情報を発信しています

VRChatのブース来場者数をカウントする方法

はじめに

11月にメタフェス2023のkintoneブースを出展しました!

www.j-cast.com

実は今回のVR出展では、ブースの来場者をカウントする仕組みを設置していて、kintoneにデータを保管していました。この記事では、この仕組みで取得したデータの共有と、実装する方法について案内をします。

⚠️ 注意事項
後述しますが、kintoneに登録されたデータはブース来場のタイミングだけであり、ユーザの個人情報の記録はしていません。

来場者カウントの仕組み

来場者カウントの仕組みにはUdon、IFTTTとkintoneを使います。

メタフェスのkintoneブース内でユーザがコライダーと接触した時に、UdonのString LoadingでIFTTTにリクエストを投げます。リクエストを受信するIFTTTは、Webhook機能を使ってkintone API経由でkintoneアプリにレコードを追加します。後は二重登録が防げるような仕組みをUdon側に実装すれば、来場者のタイミングをkintoneに記録する来場者カウントの仕組みが出来上がります。

来場のタイミングがVRChatからIFTTTを通してkintoneに登録されるフロー

メタフェスでの来場者カウントの結果

メタフェスのイベント中は、kintoneに来場者の来場タイミングが常に登録されるような実装をしていました。kintoneアプリに登録されたデータの一部はこちらです。1行のデータが『レコード』という単位で、1つの来場カウントとして扱われます。

kintoneに登録された来場者タイミングのデータ

イベント期間中の3日間のデータを集めると、このようになります。細かい数字は伏せさせて頂きますが、Y軸が来場者数、X軸が一時間ごとに分けた時間の軸になります。

11月3日 00:00 から 11月5日 23:59 までの来場者数をグラフ化したデータ

X軸がかなり細かくなってるので、見やすいように1日ごとに枠をつけました。21:00から02:00にかけて、来場者の波が来ていることがわかります。

1日ごとのデータに枠をつけたグラフ

このようなデータを集めることによって、どのような時間帯にブーススタッフを配置するのが効果的なのか等の仮説が立てやすくなります。

では実際にどのようにkintone、IFTTTとUdonを組み合わせれば、VRChatのブース来場者のカウントが出来るかを案内します。

kintoneアプリの準備

来場者カウントの仕組みでは、kintoneはデータを管理する場所として使います。 まずはデータが登録されるkintoneアプリを作りましょう。

アプリの作成

kintoneの環境にログインし*1、新しいアプリを作成します。

『はじめから作成』を選択し、文字列(1行)日時日付時刻のフィールドをドラッグ&ドロップで配置します。文字列(1行)のフィールド名を『イベント名』に変更し、フィールドコードを『name』に変更します。他のフィールドは初期設定のままにします。

文字列(1行)フィールドだけ名前とフィールドコードを変更する

配置と設定が終わったら『フォームを保存』をクリックします。

APIトークンの生成

設定タブを選択し、APIトークンをクリックします。APIトークンを1つ生成して、アクセス権の「レコード追加」にチェックが入ってることを確認します。APIトークンとアプリIDは後のIFTTTの設定で使うので、手元にメモしておきましょう。

APIトークンを生成したらトークンの文字列とアプリIDをメモします

APIトークンとアプリIDのメモが出来たら『保存』をクリックします。

アプリの公開

これでkintoneアプリの設定の準備が出来たので、「アプリを公開」(場合によっては「アプリを更新」)をクリックします。これで、データをためるための箱の準備が出来ました。

まだ何もデータが入っていないkintoneアプリが完成しました

次に、データが自動的にkintoneアプリに入るように、IFTTTの設定をしていきます。

IFTTTの準備

来場者カウントの仕組みでは、IFTTTはkintoneとUdon(VRChat)の中継役として活躍します。Udonから送信される情報を受信したら、kintoneにレコードを登録する設定を準備します。

If This の設定

IFTTTにログインし、Createをクリックして新しいAppletを作成します。『Webhooks』を検索して選択し、『Receive a web request』を選択します。『Receive a web request』は、Udonからのリクエストを受信するためのトリガー設定です。

VRChatからリクエストを受け取るための受け口をIFTTTに設定します

選択したら適当な名前をつけて、Create trigger をクリックします。

これでIFTTTのIf Then の設定は終わります。他のシステムでWebhookの設定に慣れてる方はここでWebhook URLが表示されなくて戸惑うかもしれませんが、Webhook URLの詳細については後述するステップで案内します。

Then Thatの設定

『Webhooks』を検索して選択し、『Make a web request』を選択します。このアクションは、任意のエンドポイントに対してHTTPリクエストを送信することが出来ます。

kintoneにレコードを追加するためのアクションをIFTTTに設定します

Webhooksの設定でkintoneにリクエストを送るための情報を記載します。レコードを追加するAPIのドキュメントと下記の図を参考にリクエスト内容を設定します。

kintoneのリクエスト情報が設定されたIFTTTのWebhooksアクション

URLには自分の使用しているkintone環境のサブドメインが入れます。

Additional HeadersのAPIトークンは、kintoneアプリの準備でメモしたAPIトークンを入れます。

Bodyには、どのkintoneアプリにデータを入れるのかと、どのフィールドにどういうデータを入れるかの情報をJSON形式で記載します。今回は上記の図のBodyをベースに情報をセットします。appに対する数字にはメモしたアプリIDを入れます。namevalueに対する文字列には好きな文字列を入れます。この例では、メタフェスのイベントに関するデータであることが分かるような文字列にしています。他のフィールドの情報はJSON内で設定していませんが、日時系のフィールドはレコード追加時に追加されたタイミングが入るような初期設定がkintoneアプリ側でされています。

設定が終わったらCreate action/Update actionをクリックし、ContiuneFinishを押してAppletの設定を終了させます。

Webhook URL の確認

次に、If Thenのトリガーとなるリクエスト先のWebhook URLを取得します。 https://ifttt.com/maker_webhooks からWebhooks integrationのページにアクセスして、Documentationをクリックします。

IFTTTのWehooksのURL設定はDocumentationから確認が出来る

Webhookのリクエスト先が記載されています。後ほどUnityで使うので、URLを手元にメモしておきましょう。{event}には、If ThenのWebhookを作った時につけたイベント名が入ります。

このWebhook URLに対してリクエストを送信するとIFTTTのトリガーになります

Udonの準備

UdonとはVRChat社が開発したプログラミング言語です。このステップではUnity上で作成したワールドにUdonのスクリプトを加えて、IFTTTのトリガーを走らせる設定をします。

VCCで新規プロジェクトの作成

VRChat Creator Companion (VCC)でワールド作成用の新しいプロジェクトを作成します。メタフェス2023当時はUnity2019でワールドを作成しましたが、来場者カウントの仕組みはUnity2022でも動作することは確認出来ています。VCCでの準備が整ったらOpen ProjectでUnityを開きます。

VCCでWorld作成用のテンプレートを選択してUnityを立ち上げます

ブースの作成

Emtpy Objectを新規作成し、『SpawnPoint』と名付け、シーンに配置します。SpawnPointがプレイヤーのスポーン位置になるので、位置を調整します。調整が終わったら、SpawnPointVRC Scene DescriptorのComponentをつけます。

次に、ブースを作ります。この記事ではてっとり早く進めるために、下記のアセットを使ってシーンに配置します。床も配置されてない場合、それもシーンに配置しましょう。

booth.pm

シーンに配置されたScene Descriptor付きのオブジェクトとブースのオブジェクト

コライダーの設定

Empty Object を新しく作成し、「Entrance」と名付けます。このEntranceにユーザが接触したら、Udonスクリプトが走るようにこれから設定していきます。

EntranceBox ColliderのComponentをつけ、is Trigger のチェックをつけておきます。Entranceの位置や、付与したBox Colliderのサイズを調整します。ブースの入り口となるところに配置すると良いでしょう。

ブースの入り口にコライダーを設置します

Udonスクリプトの作成

EntranceUdon Behaviorのコンポーネントをつけます。「Udon C# Program Asset」を選択し、New ProgramCreate Script をクリックします。VisitorCounter.csと名付け、エディタでこのスクリプトを開き、内容を下記のように更新し、保存します。

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.SDK3.StringLoading;
using VRC.Udon.Common.Interfaces;

public class VisitorCounter : UdonSharpBehaviour
{
    [SerializeField] private VRCUrl RequestURL;
    private bool hasTriggered = false; //VRCStringDownloader.LoadURLが1回だけ走るためのフラグ

    public override void OnPlayerTriggerEnter(VRCPlayerApi player)
    {
        if (!hasTriggered)
        {
            VRCStringDownloader.LoadUrl(RequestURL, (IUdonEventReceiver)this);
            hasTriggered = true;
        }
    }

    public override void OnStringLoadSuccess(IVRCStringDownload response)
    {
        Debug.Log(response.Result);
    }

    public override void OnStringLoadError(IVRCStringDownload response)
    {
        Debug.Log(response.Error);
    }
}

スクリプトの解説

    [SerializeField] private VRCUrl RequestURL;

RequestURLはリクエストを送信する対象のURLを指定するための変数です。こちらは[SerializeField]で宣言してるため、Unityのエディタ上にURLを入力することが出来るようになります。

private bool hasTriggered = false;

後のOnPlayerTriggerEnter()メソッドの内容が1回だけしか走らないようにするためのフラグ管理です。プレイヤーが複数回コライダーに接触することでIFTTTに複数回リクエストを送信すると1人のプレイヤーの来場者カウントが重複してしまうので、プレイヤーごとに1回だけしかリクエストが送信されないように調整をしています。

    public override void OnPlayerTriggerEnter(VRCPlayerApi player)
    {
        if (!hasTriggered)
        {
            VRCStringDownloader.LoadUrl(RequestURL, (IUdonEventReceiver)this);
            hasTriggered = true;
        }
    }

プレイヤーがコライダーにぶつかった際にこのOnPlayerTriggerEnter(VRCPlayerApi player)メソッドが走ります。VRCStringDownloader.LoadUrl()メソッドを使って、指定されたURLに対してHTTPリクエストを非同期で送信します。if (!hasTriggered)hasTriggered = true;で前述した重複リクエストを防いでいます。

ちなみにVRCStringDownloader.LoadUrl()の仕様としては対象URLにGETリクエストが送信されます。今回の来場者カウントの仕組みとしてはPOSTリクエストの方が気持ちとしてはしっくりは来るのですが、UdonからはPOSTリクエストは送信出来ないですし、IFTTTもGETリクエストを受け付けるので、これは気持ちだけの問題なので良しとしましょう。

    public override void OnStringLoadSuccess(IVRCStringDownload response)
    {
        Debug.Log(response.Result);
    }

    public override void OnStringLoadError(IVRCStringDownload response)
    {
        Debug.Log(response.Error);
    }

リクエストが成功した場合はOnStringLoadSuccess()メソッドが、失敗した場合はOnStringLoadError()メソッドが走ります。デバッグ用にレスポンス内容がコンソールに出力するようにしています。

IFTTT Webhook URLの設定

Entranceオブジェクトをインスペクターで確認します。スクリプトコンポーネントにRequest URLというテキストボックスが出来ているはずです。このテキストボックスにWebhook URL の確認セクションでメモしたIFTTTのWebhookURLを入力します。

InspectorのスクリプトコンポーネントにIFTTTのWebhook URLを入力します

これで来場者カウントの仕組みが整いました。実際にテストしてみましょう。

実行テスト

Unity上で上部の実行ボタンをクリックすると、Gameタブの中で矢印キーを使ってプレイヤーの操作が出来ます。コライダーがある位置までプレイヤーを移動させましょう。Consoleを確認し、『Congratulations! You've fired the {イベント名} JSON event』とレスポンスが返されていたらIFTTTへのリクエストが成功しています。

プレイヤーがコライダーに接触したらIFTTTにリクエストが送信されレスポンスがコンソールに出力される

IFTTTへのリクエストが成功していたら、kintoneアプリも確認しましょう。新しいレコードが追加されていたらIFTTTの連携が成功しています。

kintoneアプリに来場者がコライダーと接触した時間が登録されている

来場者カウントの制限

以上がVRChat上で行った来場者カウントの仕組みの設定方法でした。この記事を書いてる2023年12月現在、この仕組みにいくつか制限があるのでそちらも案内します。

Allow untrusted urls の設定が必要

VRCStringDownloader.LoadUrl()の仕様で、通常プレイヤーはVRChat側がホワイトリストに載せたドメインのみにしかリクエストが送れません。IFTTTはそのホワイトリストには載っていません。この制限を回避するには、プレイヤー自身が個人の設定でAllow untrusted urls という設定をオンにしないといけません*2

動的なパラメータが送れない

VRCStringDownloader.LoadUrl()の引数には、URL先を指定するVRCUrl型のオブジェクトを使用しています。設定出来るURLは事前にUnity内で決め打ちする必要があり、ゲーム実行中(つまりはVRChatのワールド公開中)には変更することが出来ません*3。そのため、プレイヤーやワールドの情報を取得してからその情報をURLのパラメータとして付与してリクエストを送る、ということは出来ません。

予め指定したURLにしかリクエストが送信出来ないので、プレイヤーの表示名などをパラメータにつけてリクエストを送ることは出来ない

重複カウントは完全には防げてない

同じプレイヤーが複数回コライダーに接触しても、リクエストは複数回送らないようにUdonスクリプト内で設定しました。ですが、これはあくまでも同じインスタンス内に居続ければの話になります。一度ワールドから出て、後日ワールドにジョインしてコライダーに接触した場合、IFTTTにリクエストは送られてしまいます。

終わりに

来場者カウントの仕組みはいかがでしたでしょうか。リアルの展示とは違って、VRでは24時間のカウントが出来たり、わりと正確なカウントが出来るのが強みですね。

メタフェスのイベント中は実験のように試してみましたが、無事にデータがkintoneに追加されて安心しました。IFTTTの無料のアカウントでも大量のWebhookリクエストの処理が出来たので、この点でもIFTTTはかなりお勧め出来るサービスだと思っています。今のところVRChat側の制限で動的なパラメータの付与が出来ないですが、ボタンを複数置いて、それぞれ別パラメータ付きの固定のURLを設置して、IFTTT経由でkintoneにデータを登録するという使い道もあったりします。今後、VRCStringDownloader.LoadUrl()で送信出来るURLの縛りがどう変わっていくか見ものですね!

*1:Cybozu Developer Networkから無料の開発者ライセンスを取得することが出来ます

*2:なので、kintoneに最終的に登録されるデータは実際のカウントより少ないと見て良いと思います

*3:厳密に言えばVRCUrlInputField経由で更新することは可能ですが、ユーザ側の入力が必要なため今回のユースケースには適してません