🎥

【Roblox】特定の場面でCameraの向きを変えたい

に公開

1. はじめに

RobloxでCameraの向きなどを変更したい時、思ったように変わってくれず困った経験はないでしょうか?
座標を動かしても変わってくれなかったり、角度を変えても反映されるかどうかがなぜかまちまち…
上手くいったと思ったら、今度はスマートデバイスだと機能しない!? 等々…初めは結構苦労すると思います。
CameraTypeをScriptableにして一から自分でCameraの動作を作ってしまえばある意味解決なのですが、「ちょっとCameraの向きを変えたいだけなのに!」という時にそれをやるのはさすがに無駄が多いですよね。
そこで今回は、基本はRobloxのデフォルトのCamera動作に任せていて、特定の場面でちょっとだけCameraをコントロールする方法についてご紹介します。

バージョン: 0.660.0.6600648

2. 問題の原因と対処法

RobloxのCameraは、CameraTypeがScriptableならソースコードで自由にコントロールできるのですが、それ以外だとRoblox側で管理されている部分についてはコントロールできません。
CameraTypeやその動作状況によって管理されている要素は異なるため、こちらがコントロールできるかがその時々でまちまちになったりします。
よくある問題が、Roblox Studioで実行した時は一見角度の変更ができているように見えても、実はPCだとCameraのデフォルトが「定番」(CameraTypeでいうTrack)なのに対し、スマートデバイスだと「Follow」であり、FollowはCameraの角度も管理しているため、デバイスによって角度がコントロールできたりできなかったりしてしまう…というパターンです。

この問題の対処法は、変更したい値を管理していないCameraTypeに設定し、値を変更した後に元のCameraType(基本的にはCustom)に戻す、という形になるのですが、このとき値の変更にScriptableを使うとうまくいかない場合があります。
CameraTypeをScriptableにした後、別のCameraTypeにすると、そのCameraTypeのデフォルトの角度に設定されてしまい変更が利かない場合があるためです。
基本的に、変更を行う際のCameraTypeは角度を管理していないTrackを使用するとよいでしょう。

さらに、RobloxのCamera関連の処理はRunService.RenderSteppedイベントから実行されているため、値などを変更した後、一度RenderSteppedイベントが発火しないと適切に反映されません。
値を変更した後、RunService.RenderStepped:Waitを入れて次のRenderSteppedを待ってから元のCameraTypeに戻すことで、値を反映することができます。

3. 角度を変える

ではここからは、前述の対処法を踏まえて実際に角度を変更してみます。
以下は、UI上のボタンを押すことで、特定のターゲット(今回はworkspace直下に置いたPart)に対して即カメラを向けるサンプルコードです。

 --!strict

 -- RunServiceを取得.
local RunService = game:GetService("RunService")

 -- LocalPlayerを取得.
local player = game:GetService("Players").LocalPlayer

 -- UI上のボタンを取得.
local button = script.Parent

 -- Cameraを向けるターゲットを取得.
local target = workspace:WaitForChild("TargetPart")

 --[[
	目標の座標にカメラを向ける関数.
	@param targetPos Vector3	ターゲットの座標.
]]
local function targetCamera(targetPos: Vector3)
	-- Cameraを取得.
	local camera = workspace.CurrentCamera
	if not camera then
		return
	end
	
	-- CameraTypeを保持しておく.
	local preCameraType = camera.CameraType

	-- Headを取得.
	local character = player.Character or player.CharacterAdded:Wait()
	local head = character:WaitForChild("Head")::BasePart
	
	-- CameraTypeをTrackにする.
	camera.CameraType = Enum.CameraType.Track
	
	-- Cameraの角度をHeadからターゲットへの角度にする.
	camera.CFrame = CFrame.new(head.Position, targetPos)
	
	-- 次のRenderSteppedイベントを待ち、Roblox側のCamera処理を一度通過させて値を反映する.
	RunService.RenderStepped:Wait()
	
	-- CameraTypeを元に戻す.
	camera.CameraType = preCameraType
end

 -- イベントを接続.
button.Activated:Connect(function(inputObject :InputObject, clickCount :number)
	targetCamera(target.Position)
end)

Camera.CameraSubjectがHumanoidの場合、Headが視界の中心となるため、その座標からターゲットに向いている角度のCFrameを設定すれば、Cameraもその対象の方を向くというわけです。
CFrameを変更しているので座標も変わってしまうのでは? と思うかもしれませんが、Cameraの座標はRoblox側が管理しており変更できないため、角度のみ反映されます。
ちなみに、このままだとHeadとターゲットがぴったり被ってしまうため、ターゲットを見やすい視界にするならHumanoid.CameraOffsetを少し上に設定しておくとよいです。

4. 距離を変える

ついでに距離も変更してみます。
前述の通り、Cameraの座標は基本的にRoblox側が管理しており、Scriptable以外だと意図通りに変更することが難しかったり、変更できなかったりします。
また、ユーザーがマウスホイールやピンチイン・アウトで操作できるCameraの距離も、直接的に変更する手段はありません。
ただし、Player.CameraMaxZoomDistance、Player.CameraMinZoomDistanceで、距離の上限と下限は設定できるため、それを用いて間接的に設定することは可能です。
先ほどのサンプルに、距離の変更も追加してみます。

 --!strict

 -- RunServiceを取得.
local RunService = game:GetService("RunService")

 -- LocalPlayerを取得.
local player = game:GetService("Players").LocalPlayer

 -- UI上のボタンを取得.
local button = script.Parent

 -- Cameraを向けるターゲットを取得.
local target = workspace:WaitForChild("TargetPart")

+-- リセットするズーム距離.
+local ZOOM_DISTANCE = 12.5

 --[[
-	目標の座標にカメラを向ける関数.
+	目標の座標にカメラを向け、Zoom距離をリセットする関数.
	@param targetPos Vector3	ターゲットの座標.
]]
local function targetCamera(targetPos: Vector3)
	-- Cameraを取得.
	local camera = workspace.CurrentCamera
	if not camera then
		return
	end
	
	-- CameraTypeを保持しておく.
	local preCameraType = camera.CameraType

+	-- ズーム距離を保持しておく.
+	local preCameraMaxZoomDistance = player.CameraMaxZoomDistance
+	local preCameraMinZoomDistance = player.CameraMinZoomDistance

	-- Headを取得.
	local character = player.Character or player.CharacterAdded:Wait()
	local head = character:WaitForChild("Head")::BasePart
	
	-- CameraTypeをTrackにする.
	camera.CameraType = Enum.CameraType.Track
	
	-- Cameraの角度をHeadからターゲットへの角度にする.
	camera.CFrame = CFrame.new(head.Position, targetPos)
	
+	-- Cameraの最小・最大距離を同じにすることで、現在の距離を特定の値にする.
+	player.CameraMaxZoomDistance = ZOOM_DISTANCE
+	player.CameraMinZoomDistance = ZOOM_DISTANCE
	
	-- 次のRenderSteppedイベントを待ち、Roblox側のCamera処理を一度通過させて値を反映する.
	RunService.RenderStepped:Wait()
	
+	-- Cameraの最小・最大距離を元に戻す.
+	player.CameraMaxZoomDistance = preCameraMaxZoomDistance
+	player.CameraMinZoomDistance = preCameraMinZoomDistance
	
	-- CameraTypeを元に戻す.
	camera.CameraType = preCameraType
end

 -- イベントを接続.
button.Activated:Connect(function(inputObject :InputObject, clickCount :number)
	targetCamera(target.Position)
end)

これで、カメラの角度と距離が変更できるようになりました!

5. まとめ

  • Roblox側のCamera処理が管理している値は変更できない
  • CameraTypeをTrackにすれば、角度の変更が可能
  • 距離を直接変更することはできないが、一時的に最小・最大距離を設定することで間接的に変更できる
  • 値を変更した上で、一度RenderSteppedの発火を待つことで、Roblox側のCamera処理が動いて値を反映できる

RobloxのCamera関連はなかなか厄介な要素が絡み、角度を変えたいだけでもどうすれば安定して変えられるのか非常にわかりづらいですが、わかってしまえばやり方自体は簡単です。
ぜひ、活用してみてください!
最後までお読みいただき、ありがとうございました!

6. 参考

https://create.roblox.com/docs/en-us/reference/engine/classes/Camera
https://create.roblox.com/docs/en-us/reference/engine/enums/CameraType
https://create.roblox.com/docs/en-us/reference/engine/classes/Player#CameraMaxZoomDistance
https://create.roblox.com/docs/en-us/reference/engine/classes/Humanoid#CameraOffset
https://create.roblox.com/docs/en-us/reference/engine/classes/RunService#RenderStepped

ランド・ホー Roblox開発チーム

Discussion