この記事はVR法人HIKKYのアドベントカレンダー8日目の記事です。
はじめに
こんにちは。HIKKYのしらピーです。
Vket Cloudを使ったコンテンツ制作やコンテンツの監修の業務を行っております。
弊社開発のWebブラウザ上で動作するVRエンジン「Vket Cloud」は2025年11月、バージョン16にアップデートし、ワールドの機能制作に便利な新しい開発機能が増えました。
本記事ではバージョン16にてHeliScript 1 に新しく増えた機能「イベントリスナー」の使用方法について紹介します。
イベントリスナーについて
公式マニュアルでは、以下のページに記載があります。
イベントリスナーを使うことで、同期や値渡しといったスクリプト間連携をシンプルな実装で行うことができます。
例えば、スクリプトAの関数αが実行されたタイミングでスクリプトBの関数βを実行したい、といった挙動をスクリプトA側ではイベントの発行、スクリプトB側ではイベントリスナーの登録を行うことで実装が出来ます。
JavaScriptで使用可能なイベントリスナーのHeliScript版となります。
既存機能との違い
Item.CallComponentMethod
Itemクラスには、アイテムに設定されているコンポーネントのメソッドを呼び出す、CallComponentMethodというメソッドがあります。
こちらはアイテムが持つスクリプトのメソッドを指定して直接実行できますが、引数はstring型で名称を指定する必要があるため、仕様を変更しようとした際に不具合が発生する原因になりがちというデメリットがあります。
つまり、スクリプト間の連携はできるものの、密に結合した関係となってしまいます。
HSMessage
HeliScriptではスクリプト間の同期・値渡しに使用可能な機能として、独自クラスのHSMessageがあります。
こちらは、一度に複数の情報を渡すことができ、string型以外の情報も渡すことができますが、Item.CallComponentMethod()同様送信先アイテムを指定して送る必要がある、密結合のスクリプト連携となります。
そのため、実装内容によっては送信先の増減に応じて実装を修正しないといけない点、アクティビティクラスやUIで用いているスクリプトに対しての送信が困難である点が課題となります。
サンプル
実装例として、イベントリスナーを使ったワールドのサンプルを用意してみました。
※画像をクリックするとワールドが立ち上がります。
- 3つのオブジェクトが上下に移動しながらだんだんと速くなっています。
- 赤色のエリアに入っている間すべてのオブジェクトが大きくなり、青色のエリアに入っている間すべてのオブジェクトが小さくなります。
- オブジェクトの前の球体をクリックすると、オブジェクトの大きさと速さが初期状態に戻ります。
スクリプトは以下の2つです。
オブジェクト側スクリプトと実装方法(クリックして展開)
// イベントリスナーを登録し、発火されたイベントに合わせた挙動を行うスクリプト
component ListenerObject
{
Item _self; // 自分自身(オブジェクト)のアイテム情報
float _size, _speed; // 大きさ、速度情報変数
Vector3 _objPos; // 初期オブジェクト座標
bool _isIncrease, _isDecrease; // 大きくなっているか、小さくなっているかのフラグ
bool _isUp; // 上方向に進んでいるかどうかのフラグ
const int _SpeedDefault = 3; // 速度初期値
const int _SizeDefault = 1; // 大きさ初期値
public ListenerObject()
{
_self = hsItemGetSelf(); // アイテム情報を変数に登録
_speed = _SpeedDefault; // 速度を初期速度に設定
_size = _SizeDefault; // 大きさを初期サイズに設定
_objPos = _self.GetPos(); // 初期位置を座標情報に登録
// イベントリスナーを登録
// (オブジェクト名)_initializeのイベントが発火されたら初期化
hsAddEventListener("%s_initialize" % _self.GetName(), Initialize);
// Growのイベントが発火されたらSetGrow実行
hsAddEventListener("Increase", SetIncrease);
// Shrinkのイベントが発火されたらSetShrink実行
hsAddEventListener("Decrease", SetDecrease);
}
// 毎フレーム実行関数
public void Update()
{
// 1フレームの長さが0.1秒を超える場合、実行しない(放置対策)
if(hsSystemGetDeltaTime() > 0.1){return;}
// 速度を1秒当たり0.2加算
_speed += hsSystemGetDeltaTime() / 5;
// Y座標を速度分だけ加算(この時、下方向に進む場合は減算になるようにする)
_objPos.y += _speed * hsSystemGetDeltaTime() * (_isUp ? 1 : -1);
// 上方向に進んでいる際4以上、下方向に進んでいる場合0以下であれば、進行方向を反転
if(( _isUp && _objPos.y >= 4 ) || ( !_isUp && _objPos.y <= 0 )){
_isUp = !_isUp;
}
// 座標計算結果をオブジェクトに適用
_self.SetPos(_objPos);
// 大きくなるフラグが有効になっている際、だんだん大きくする(上限あり)
if(_isIncrease && _size < 3){
_size += hsSystemGetDeltaTime() / 1.5;
}
// 小さくなるフラグが有効になっている際、だんだん小さくする(下限あり)
if(_isDecrease && _size > 0.5){
_size -= hsSystemGetDeltaTime() / 1.5;
}
// 大きさ情報をオブジェクトに適用
_self.SetScale(makeVector3(_size, _size, _size));
}
// 初期化メソッド 速度と大きさを初期値にする
void Initialize(string param){
_speed = _SpeedDefault;
_size = _SizeDefault;
}
// 大きくするフラグの有効/無効切り替え
// 引数となるstring型変数の値が"true"となっている場合、フラグを有効化
void SetIncrease(string flag){
_isIncrease = (flag == "true");
}
// 小さくするフラグの有効/無効切り替え
// 引数となるstring型変数の値が"true"となっている場合、フラグを有効化
void SetDecrease(string flag){
_isDecrease = (flag == "true");
}
}
イベント発火制御スクリプト(クリックして展開)
// イベント発火用メソッドを持つスクリプト
component EventController
{
// 引数で登録された名称のアイテムの初期化イベントを発火
void InitializeTrigger(string name){
hsDispatchEvent("%s_initialize" % name, "");
}
// 大きくするイベントを発火
void IncreaseArea(string flag){
hsDispatchEvent("Increase", flag);
}
// 小さくするイベントを発火
void DecreaseArea(string flag){
hsDispatchEvent("Decrease", flag);
}
}
Unity上での実装方法について
スクリプトの関数実行はクリック判定となる球体コライダーにVKC Attribute Action Trigger Unityコンポーネント、エリア判定となるボックスコライダーにVKC Item Area Collider Unityコンポーネントをアタッチし、それぞれアクションから関数呼び出しを利用して実装しています。

↑初期化用クリック判定の設定。String型引数に対象となるアイテムの名称を入れています。

↑大きくするエリアコライダーの設定。String型引数にtrue/falseを入れることで侵入時に巨大化開始、退出時に巨大化停止となるようにしています。
関数紹介
hsDispatchEvent(string, string)
イベントを発火する関数です。
第1引数で指定した名称のイベントを発火します。
第2引数はイベントリスナー側で実行される関数の引数に渡される値です。
hsAddEventListener(string, Func<void, string>)
イベントを登録する関数です。
第1引数でイベントを指定します。
第2引数でイベント発火時に実行する関数を指定します。
これらの他にも指定したイベントリスナーの登録を解除するhsRemoveEventListener(string, Func<void, string>)、すべてのイベントリスナーの登録を解除するhsClearEventListener(string)があります。
まとめ
イベントリスナーを使うことでスクリプト間の同期・値渡しといった連携をシンプルな実装で実現できます。
また、イベントリスナーによるスクリプト連携は疎結合であるため、保守性・拡張性に富んだ実装が可能となります。
おわりに
Vket Cloudは公式Discordサーバーがあります。
「こういったワールドを作りたいけどどうしたらいいか分からない」、「制作中にこんなエラーが出た!解決方法を教えてほしい」といった、ワールド制作の不明点はこちらのサーバーの質問・要望・不具合報告-forumに投稿していただけると、スタッフが解決方法や実装方法について回答します。お気軽にどうぞ!


