Roblox はVRでプレイできるだけではなく、ゲームを作り公開することもできます。いきなり、Roblox でVRゲームを作るのはハードルが高いので、まずはゲームを作ろうからご覧ください。
ここではVRゲームを作るにあたり、基本的なことを書いていくことにします。
VRの検出
まずは、VR以外のプラットフォームではプレイできなくします。VRから実行されていなかった場合は、ゲームから強制退出させます。
- テンプレート「Baseplate」から新規作成します。
- StarterPlayer の StarterPlayerScripts の中に LocalScript を追加します。
- 名前を VRcheck とし、以下のプログラムを入力します。
local Players = game:GetService("Players")
local VRService = game:GetService("VRService")
if VRService.VREnabled then -- VR機の検出
print("VR in enabled")
else
print("VR not enabled")
Players.LocalPlayer:kick("このゲームはVR専用です")
end
プレイヤー視点にする
デフォルトでは、三人称視点(キャラクターの後ろからの眺め)での開始となります。ゲームに入った瞬間から、キャラクター目線でプレイさせたい場合は、一人称視点に変更しなくてはなりません。こちらはプログラムではなく設定で対応できます。
- StarterPlayer の CameraMode プロパティを「LockFirstPerson」に変更します。
これで一人称視点になります。(コントローラーを使っての三人称視点への切り替えは不可)
コントローラーからの入力
VRのコントローラーは、UserInputService の InputBegan と InputEnded のイベントを使用します。
InputBegan はボタンが押されたときに発生するイベントで、InputEnded はボタンが離されたときに発生するイベントです。
テストとして、ボタンが正しく認識されているかどうかを調べるために、パーツにキーコードを表示するプログラムを作成してみます。
- Workspace に Part を1つ追加します。
- Part の中に SurfaceGui を追加します。
- SurfaceGui の中にTextLabelを追加します。
- StarterPlayer の StarterPlayerScripts に LocalScriptを追加します。
- 名前を VRinput にし、以下のプログラムを入力します。
local UserInputService = game:GetService("UserInputService")
local VRService = game:GetService("VRService")
-- ボタンを押したときに呼ばれる
local function onInputBegan(input, processed)
workspace.Part.SurfaceGui.TextLabel.Text = tostring(input.KeyCode)
end
-- ボタンを離したときに呼ばれる
local function onInputEnded(input, processed)
workspace.Part.SurfaceGui.TextLabel.Text = ""
end
UserInputService.InputBegan:Connect(onInputBegan)
UserInputService.InputEnded:Connect(onInputEnded)
パーツの色を変える
次に、特定のボタンが押されたら、パーツの色が変わるようにしてみたいと思います。まず、上記で挿入したパーツの色を変えてみます。VRinput を以下のように変更します。
local UserInputService = game:GetService("UserInputService")
local VRService = game:GetService("VRService")
-- ボタンを押したときに呼ばれる
local function onInputBegan(input, processed)
workspace.Part.SurfaceGui.TextLabel.Text = tostring(input.KeyCode)
if input.KeyCode == Enum.KeyCode.ButtonR2 then
workspace.Part.BrickColor = BrickColor.random()
end
end
-- ボタンを離したときに呼ばれる
local function onInputEnded(input, processed)
workspace.Part.SurfaceGui.TextLabel.Text = ""
end
UserInputService.InputBegan:Connect(onInputBegan)
UserInputService.InputEnded:Connect(onInputEnded)
押されたボタンは、input.KeyCode に入ります。「Enum.KeyCode.〇〇〇〇」で判断できます。〇〇〇〇の部分は以下の名称になります。
上記のプログラムはローカル側で実行しますので、他のプレーヤーにはパーツの色が変わったように見えません。Robloxはマルチプレイヤーゲームなので、全プレイヤーに同じものを見せるにはサーバー側でパーツの色を変えなくてはなりません。
もちろんソロプレイのゲームでしたら、この方法で問題ないでしょう。
パーツの色を変える(サーバー処理)
ローカル(クライアント)からサーバーのプログラムを実行したい場合は、RemoteEvent を使います。せっかくなのでパーツの色を変えるだけではなく、他のプロパティも操作できるようにしてみます。
- ReplicatedStorage に RemoteEvent を追加し、名前を ChangePartEvent にします。
- ServerScriptService に Script を追加し、名前を EventScript にします。
- 以下のプログラムを入力します。
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local changePartEvent = ReplicatedStorage.ChangePartEvent
changePartEvent.OnServerEvent:Connect(function(player,part,properties)
for name,value in pairs(properties) do
part[name] = value
end
end)
RemoteEvent の OnServerEvent が、クライアントから呼ばれるものになります。第1パラメーターはPlayer になり、第2パラメータ以降はユーザーが自由に設定することができます。今回は変更したいパーツと、プロパティとその値をテーブルで指定できるようにしました。
呼ぶ方のプログラムは VRinput を少し変更するだけです。
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local VRService = game:GetService("VRService")
-- ボタンを押したときに呼ばれる
local function onInputBegan(input, processed)
workspace.Part.SurfaceGui.TextLabel.Text = tostring(input.KeyCode)
if input.KeyCode == Enum.KeyCode.ButtonR2 then
ReplicatedStorage.ChangePartEvent:FireServer(workspace.Part,{ BrickColor = BrickColor.random() })
end
end
-- ボタンを離したときに呼ばれる
local function onInputEnded(input, processed)
workspace.Part.SurfaceGui.TextLabel.Text = ""
end
UserInputService.InputBegan:Connect(onInputBegan)
UserInputService.InputEnded:Connect(onInputEnded)
RemoteEvent の FireServer でサーバープログラムを呼ぶことができます。ただし、FireServer はイベントをキューに入れるだけです。FireServer を多用するとラグが発生し、画面の反応が悪くなるので注意が必要です。そんなこともあり、この ChangePartEvent は、一度に複数個のプロパティが変更できるような仕様にしました。
ChangePartEvent:FireServer(workspace.Part,{ BrickColor = BrickColor.random() , Anchored = false } )
このようにカンマで区切ることで、複数のプロパティが同時に指定できます。
ビームを使う
今度は、コントローラーのビームが当たっているパーツを変化させようと思います。ビームは標準で出ていますのでそのまま利用します。
空中にパーツを複数個設置し、ビームが当たっているパーツのアンカーをオフにし、落下させようと思います。
- 複数のパーツを空中に設置し、すべてAnchoredをONにします。
- VRinput を以下のように変更します。
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local VRService = game:GetService("VRService")
local Players = game:GetService("Players")
local mouse = Players.LocalPlayer:GetMouse()
-- ボタンを押したときに呼ばれる
local function onInputBegan(input, processed)
if input.KeyCode == Enum.KeyCode.ButtonR2 then
if mouse.Target then
ReplicatedStorage.ChangePartEvent:FireServer(mouse.Target,{ Anchored = false })
end
end
end
UserInputService.InputBegan:Connect(onInputBegan)
VRでも、GetMouse() が使用できます。これはクリック(タップ)した場所の情報が取得できるものです。mouse.Target にクリックされたオブジェクトが入りますのでそれが利用できます。ただし上記のプログラムでは、Baseplate もアンカーオフの対象になっていますので、地面をクリックすると落下してしまうでしょう。この辺りはVRだからというものではないので、ご自身で修正してください。
パーツを移動させる
ビームとボタンを使って、パーツを動かしてみようと思います。VRでも DragDetector が使えるようなのでそちらを使用したいところなのですが、ここでは上記のプログラムを追加する形で行ってみたいと思います。
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local VRService = game:GetService("VRService")
local Players = game:GetService("Players")
local mouse = Players.LocalPlayer:GetMouse()
local dragPart = nil
local offset = nil
-- ボタンを押したときに呼ばれる
local function onInputBegan(input, processed)
if input.KeyCode == Enum.KeyCode.ButtonR2 then
local target = mouse.Target
if target and target.Locked == false then
dragPart = target
offset = target.Position - mouse.Hit.p
end
end
end
-- ボタンを離したときに呼ばれる
local function onInputEnded(input, processed)
if input.KeyCode == Enum.KeyCode.ButtonR2 then
if dragPart then
ReplicatedStorage.ChangePartEvent:FireServer(dragPart,{ Position = dragPart.Position })
dragPart = nil
end
end
end
mouse.Move:Connect(function()
if dragPart then
local newPosition = mouse.Hit.p + offset
dragPart.Position = newPosition
-- (A)
end
end)
UserInputService.InputBegan:Connect(onInputBegan)
UserInputService.InputEnded:Connect(onInputEnded)
「target.Locked == false」を入れることでBaseplateが無視されるようにしています。このプログラムでは、ボタンを離したときにRemoteEventを呼んでサーバー側のパーツの位置を変更しています。どのプレイヤーからでもリアルタイムに動かしている様子を見せたい場合は、(A)の部分に、「 ReplicatedStorage.ChangePartEvent:FireServer(dragPart,{ Position = dragPart.Position })」を入れます。ただし、これを行うと重くなるかも知れません。
なお、このプロググラムはパーツの位置に制限を設けていませんので、かなり遠くの方に移動でき、地面にも埋まってしまうでしょう。この辺りはゲームの仕様により異なりますので、ご自身で修正してください。
ステップアップ
VRゲームで有名な Beat Saber というものがあります。これこそVRならではのゲームではないでしょうか。そこで今度はビームサーベルを持ち、自由に動かしてみたいと思います。
ゲーム設定
1つ肝心なことを忘れていました。Steam VRを使えば、Roblox Studio 上でVRゲームの動作確認ができますが、Quest の Roblox から入って実行したい場合は、「ゲーム設定」を変更しなくてはなりません。
ファイルメニューのゲーム設定を実行し、Basic info(基本情報)のPlayable Devices(プレイできるデバイス)の「VR」の項目にチャックを入れます。
最後にファイルメニューの「Publish to Roblox(Robloxに公開)」を実行すれば、Quest の Roblox でプレイできるようになります。
作成したゲームのプレイ方法
Quest の Roblox に入っても、作った自身のゲームが表示されないためすぐにプレイできません。事前にパソコンで一度プレイしておけば、ホームの「続ける」に表示されますのでこれを活用します。
- パソコンのブラウザで、Robloxのホーム に行き、「作成」をクリックします。
- バーチャル空間の中から作成したゲームをさがし、「…」をクリックします。
- メニューから「Robloxで表示」をクリックすると、そのゲームに移動します。
そのゲームをプレイすれば、次からは「続ける」の方に表示されますので、Quest の Roblox 上にも表示されるでしょう。
5 thoughts on “VRゲームの作成”
コメントを残す
コメントを投稿するにはログインしてください。
管理人様
鍛冶屋です.いつもお世話になっております。
夏休み期間中を少しオーバーしましたが,プログラミングの基礎を終えました.
本来行いたかったMeta Questでの メタバース構築のため,このページを勉強させていただこうと思っています.
このVRの作成のコンテンツをお借りして YouTube動作を冊得させていただいてよいでしょうか?
よろしくお願いいたします.
追伸:
管理人さん ユーザーネームはどのように発音すればよいのでしょうか?
よろしければ,フリガナを教えていただけると嬉しいです.よろしくお願いいたします.
ちなみに 私は ブラックスミス→訳すと 鍛冶屋(カジヤ)です.
大変お疲れさまでした。
コンテンツ、どうぞご自由にご利用ください。
schilverberch は「シルバーベルヒ」と読みます。
シルバーベルヒ様
快諾ありがとうございます.
明日から,木金土の週3回で勉強していきます.
今後とも よろしくお願いいたします.
鍛冶屋
シルバーベルヒ様
いつもありがとうございます.
早速最初のコードを実行してみました.
もちろんVR専用なのでPC用のアプリで動かすとNGなのですが,確認してみたら,少し不具合があるようです.
なお,MetaQuest2では,コンソールLogに「VR is enabled.」と表示され,無事に動作しました!
来週から,次のスクリプトに進みます.
まず,動作確認したくて,コンテンツでは動作確認がページの最後ですが,いきなりやってしまいました.
以下,報告しておきます.
■不具合
退出するときのダイアログで固まる.
ちゃんと”このゲームはVR専用です”と表示されるが,続いて(エラーコード:278)となり,退出できず,ウィンドウがフリーズしました.
■以下,Studio内のエラーコードです.
「’WaitForChild」とあるので,なにかのチャイルドが生成される前にキックされるためにエラーが出ているのでしょうか?
08:33:48.189 Players.BlacksmithMT.PlayerScripts.PlayerScriptsLoader:7: attempt to index nil with ‘WaitForChild’ – Studio
08:33:48.190 Stack Begin – Studio
08:33:48.190 Script ‘Players.BlacksmithMT.PlayerScripts.PlayerScriptsLoader’, Line 7 – Studio
08:33:48.190 Stack End – Studio
08:33:48.662 Disconnect from 127.0.0.1|57285 – Studio
08:33:53.221 Infinite yield possible on ‘RbxCharacterSounds:WaitForChild(“AtomicBinding”)’ – Studio
08:33:53.222 Stack Begin – Studio
08:33:53.222 Script ‘Players.BlacksmithMT.PlayerScripts.RbxCharacterSounds’, Line 7 – Studio
08:33:53.222 Stack End – Studio
WaitForChild でエラーになる場合は、そのオブジェクトが存在しないときに出るものですので、その辺りを確認してみましょう。