API呼び出し最適化ガイド#
VCIのALL関数やメッセージの過剰な呼び出しは、ルーム全体のパフォーマンス低下やVCI通信流量制限機能による制限の原因となります。 本記事では、これらのAPI呼び出しを最適化し、通信負荷を低減するための指針と実践例を示します。
通信最適化の指針#
VCIスクリプト設計時は、API呼び出しの必要性を吟味し、最低限の頻度で実行することが重要です。 通信を最適化するための指針は主に以下の3つです。
イベント駆動で必要なタイミングでのみ通信
update()
/updateAll()
内での実行条件の絞り込み毎フレームの通信を避け、条件を満たした時だけAPIを呼び出すことで負荷を軽減します。
アイテム内同期変数の利用
低負荷かつ制限対象外のAPIのため、リアルタイム要求が厳しくない変数の同期に適しています。
特に、updateAll()
は、全てのクライアントで毎フレーム実行されるため、その関数内部でネットワーク通信を行うAPI(ALL関数やメッセージなど)を無条件に呼び出すと、過剰な通信が発生しやくなります。
作成したVCIが過剰通信になっていないか、また、1個あたりの通信量は少ないが多数配置により過剰通信になっていないかを意識するとよいでしょう。
通信量の確認には VCINetworkPanel をご活用ください。
実践例:カウント値の同期#
ここでは、Use でカウントアップした値(count
)を、参加タイミングに関わらず全プレイヤーに同期し、テキストに表示するケースを考えます。
このケースを例に、3つの指針に基づいた実践例を示します。
説明を簡略化するため、VCI全体の所有権を持つプレイヤーのみが Use でカウントアップでき、VCI全体の所有権を持つプレイヤー所有権の移動(ルーム移動など)を考慮しないものとします。
実践例1:イベント駆動で必要なタイミングでのみ通信#
Use イベント発生時に、ALL関数 (ExportAssets._ALL_SetText()
) でカウントを同期します。
また、途中参加プレイヤーに同期させるために、メッセージを利用します。
-- 変数 count の初期化
local count = 0
-- 参加プレイヤー環境におけるVCIスクリプト読み込み後に、
-- count値の初期同期要求メッセージを送信
vci.message.EmitToSelf("InitialSyncRequest", true)
------------------------------------------------------------
-- Useでカウントアップするときに同期
------------------------------------------------------------
function onUse(use)
if vci.assets.IsMine then
count = count + 1
vci.assets._ALL_SetText("TextObject", tostring(count))
vci.state.Set("count",count) -- 途中参加プレイヤーに同期させるために必要
end
end
--- countに差分がない場合でも参加プレイヤーにcountを同期させる
function OnReceiveInitialSyncRequest(sender, name, message)
if vci.assets.IsMine then
vci.assets._ALL_SetText("TextObject", tostring(count))
end
end
--- 初期同期要求メッセージを受け取る
vci.message.On("InitialSyncRequest", OnReceiveInitialSyncRequest)
実践例2:update()
/updateAll()
内でのALL関数実行条件の絞り込み#
update()
/updateAll()
内でALL関数を使わざるを得ない場合、ALL関数の実行条件を絞り込みます。
ALL関数実行条件の絞り込み例として以下が挙げられます。
条件1:VCI全体の所有権を持つプレイヤーのみが実行する
条件2:更新頻度を落として実行する
条件3:同期したいデータに差分があった場合のみ実行する
-- 変数の初期化
local count = 0
local prev_count = 0
local interval = 1
local prev_time = 0
-- 参加プレイヤー環境におけるVCIスクリプト読み込み後に、
-- count値の初期同期要求メッセージを送信
vci.message.EmitToSelf("InitialSyncRequest", true)
------------------------------------------------------------
-- ALL関数の実行条件を絞る
------------------------------------------------------------
function updateAll()
-- 全てのプレイヤー環境で必要な処理
-- (省略)
--
-- 条件を絞ってからALL関数を実行
-- 条件1: VCI全体の所有権を持つプレイヤーのみ実行
if vci.assets.IsMine then
local current_time = os.time()
-- 条件2: 更新頻度を落として実行 (指定したinterval秒おきに実行)
if os.difftime(current_time, prev_time) >= interval then
-- 条件3: 同期したいデータ(count)に差分があった場合のみ実行
if count ~= prev_count then
vci.assets._ALL_SetText("TextObject", tostring(count))
prev_count = count
end
prev_time = current_time
end
end
end
-- Useでカウントアップ
function onUse(use)
if vci.assets.IsMine then
count = count + 1
end
end
--- countに差分がない場合でも参加プレイヤーに同期させる
function OnReceiveInitialSyncRequest(sender, name, message)
if vci.assets.IsMine then
vci.assets._ALL_SetText("TextObject", tostring(count))
end
end
--- 初期同期要求メッセージを受け取る
vci.message.On("InitialSyncRequest", OnReceiveInitialSyncRequest)
実践例3:アイテム内同期変数の利用#
変数を複数プレイヤー間で同期する目的の場合はアイテム内同期変数を利用することを検討してください。 アイテム内同期変数はALL関数やメッセージと比べると同期されるまでの時間が遅い反面、負荷が低い特性があり、VCI通信流量制限の対象APIには含まれません。
-- アイテム内同期変数 "count" に値が設定されていない場合、初期化
if vci.assets.IsMine then
if vci.state.Get("count") == nil then
vci.state.Set("count", 0)
end
end
-- 変数の初期化
local count = 0
local interval = 1
local prev_time = 0
------------------------------------------------------------
-- アイテム内同期変数の読み取り(通信は発生しない)
------------------------------------------------------------
function updateAll()
local current_time = os.time()
if(os.difftime(current_time, prev_time) >= interval) then
count = vci.state.Get("count") -- 通信は発生しない
vci.assets.SetText("TextObject", tostring(count))
prev_time = current_time
end
end
------------------------------------------------------------
-- アイテム内同期変数の書き込み(通信が発生)
------------------------------------------------------------
-- Useでカウントアップ
function onUse(use)
if vci.assets.IsMine then
count = count + 1
vci.state.Set("count",count)
end
end