【Roblox】マルチスレッド処理
はじめに
ゲーム開発において処理の高速化の手法として使われることが多いマルチスレッド処理ですが、Robloxの開発で使用されるLuauでも実装することができます。今回はマルチスレッド処理の実装方法について紹介します。
Robloxバージョン:0.641.2.6410741
マルチスレッド処理とは
マルチスレッドについて一応軽く説明しておきます。本来スクリプトは順番に実行されるものですが、この方法だとゲーム内に多くのコンテンツが配置されたときに処理時間が長くなってしまい、遅延が発生してしまいます。そこでマルチスレッド処理ではタスクを複数のスレッドを使用して同時に実行することで処理時間を短くすることができます。
Robloxでのマルチスレッド処理は「Parallel Luau」を使用することで実装することができます。
Parallel Luauの実装
マルチスレッド処理を実装するにはスクリプトをActorインスタンスの子オブジェクトにする必要があります。
Actorインスタンスの子オブジェクトに配置したスクリプトがすべて並列処理になるわけではなく、スクリプトからtask.desynchronize()
とtask.synchronize()
を使ってシリアル実行と並列実行を切り替えることができます。
--[[
この間シリアル実行
]]
task.desynchronize() -- 並列実行に切替
--[[
この間並列実行
]]
task.synchronize() -- シリアル実行に切替
--[[
この間シリアル実行
]]
シグナルコールバックをスケジュールして、トリガー時にコードを並列実行する場合は:ConnectParallel()
メソッドを使用できます。
local RunService = game:GetService("RunService")
RunService.Heartbeat:ConnectParallel(function()
--[[
この間並列実行
]]
task.synchronize()
--[[
この間シリアル実行
]]
end)
スレッドセーフティ
並列実行中は、通常時と同様にDataModel階層のほとんどのインスタンスにアクセスできますが、一部のAPIプロパティと関数は安全に読み書きできません。
安全レベル | プロパティ | 関数 |
---|---|---|
Unsafe | 並列実行から読み取りと書き込みを行うことはできません。 | 並列実行から呼び出すことができません。 |
ReadParallel | 並列実行から読み取ることはできますが、書き込むことはできません。 | 該当なし。 |
LocalSafe | 同じアクター内で使用できます。ほかのアクターから並列して読み取ることはできますが、書き込むことはできません。 | 同じアクター内で呼び出すことができますが、ほかのアクターから並列して呼び出すことはできません。 |
Safe | 読み取りと書きことが可能です。 | 呼び出すことができます。 |
APIメンバーのスレッドセーフティタグは、APIリファレンスで確認できます。
上記はHumanoidのプロパティです。
「並列読みとり」と書かれているものが表の「ReadParallel」に該当するものになり、読み取りのみ可能になっています。場合によってはAPIリファレンスに記載のないものがありますが、基本的には「Unsafe」に設定されています。
スレッド間通信
Actor Messaging API
シリアルまたは並列コンテキストのスクリプトから、同じデータ モデル内のアクターにデータを送信できます。この API を介した通信は非同期で実行されます。
メッセージを分類するためのトピックを定義する必要があります。
各メッセージは1つのアクターにのみ送信できますが、そのアクターは内部的にメッセージにバインドされた複数のコールバックを持つことができます。
メッセージの送信
-- メッセージを送信するアクターを取得
local workerActor = workspace.WorkerActor
-- アクターにメッセージを送信
workerActor:SendMessage("topic_01", "Hello World!")
メッセージの受信
local actor = script:GetActor()
-- シリアル実行する場合
actor:BindToMessage("topic_01", function(receiveMessage)
print(actor.Name, "-", receiveMessage)
end)
-- 並列実行する場合
actor:BindToMessageParallel("topic_01", function(receiveMessage)
print(actor.Name, "-", receiveMessage)
end)
SharedTable
複数のアクターで実行されているスクリプトからアクセスできるテーブルのようなデータ構造です。SharedTableを別のアクターに送信しても、データのコピーは作成されません。1つのアクターによるSharedTableの更新はすべてのアクターに即座に反映されます。
SharedTableを共有するにはSharedTableRegistryを使用します。
テーブルの作成
local SharedTableRegistry = game:GetService("SharedTableRegistry")
local sharedTable = SharedTable.new()
sharedTable[1] = "AAA"
sharedTable["x"] = 1234
-- SharedTableRegistryにテーブルを登録
-- 第1引数:テーブル名 第2引数:SharedTable
SharedTableRegistry:SetSharedTable("TestTable", sharedTable)
テーブルの使用
local SharedTableRegistry = game:GetService("SharedTableRegistry")
-- テーブルの取得
-- 第1引数:テーブル名
local sharedTable = SharedTableRegistry:GetSharedTable("TestTable")
print(sharedTable)
--▼ {
-- [1] = "AAA",
-- ["x"] = 1234
-- }
ダイレクトデータモデルの送信
DataModelを使用して、複数のスレッド間の通信を容易にすることもできます。これにより、異なるアクターがプロパティや属性を書き込み、読み取ることができます。ただし、スレッドの安全性を維持するために、並行して実行されるスクリプトは通常DataModelに書き込むことができません。
通信にDataModelを直接使用すると制限が伴い、スクリプトが頻繁に同期される可能性があり、スクリプトのパフォーマンスに影響を与える可能性があります。
まとめ
今回はマルチスレッド処理について紹介しました。
並列実行に対応しているAPIがまだ少ない状態ではありますが、Robloxでは多くの処理を同時に行う必要があることが多いので並列処理を上手く使って、処理の高速化を図っていきましょう。
お読みいただきありがとうございました。
参考
当社ではRobloxを活用したゲームの開発、 また企業の商品やサービスの認知度拡大に寄与する3Dワールドの制作など、 Robloxにおける様々な活用支援を行っております。 Robloxのコンテンツ開発をご検討されている企業様は、お気軽にご相談ください。 landho.co.jp/
Discussion