Hi, I need to make something like Humanoid.CameraOffset
but with CFrames, how would I go on with making that? Any help is appreciated, thanks!
You can set Humanoid.CameraOffset
to a CFrame.Position
if that’s what you mean. Otherwise, you’ll have to edit the camera module.
That’s not what I want at all, I want rotation and use only CurrentCamera, so humanoid .CameraOffset is not an option for me
I see. You’ll need to edit the camera module. Fork it now.
In the root script called CamerModule, replace it with this:
--!nonstrict
--[[
CameraModule - This ModuleScript implements a singleton class to manage the
selection, activation, and deactivation of the current camera controller,
character occlusion controller, and transparency controller. This script binds to
RenderStepped at Camera priority and calls the Update() methods on the active
controller instances.
The camera controller ModuleScripts implement classes which are instantiated and
activated as-needed, they are no longer all instantiated up front as they were in
the previous generation of PlayerScripts.
2018 PlayerScripts Update - AllYourBlox
--]]
local CameraModule = {}
CameraModule.__index = CameraModule
-- NOTICE: Player property names do not all match their StarterPlayer equivalents,
-- with the differences noted in the comments on the right
local PLAYER_CAMERA_PROPERTIES =
{
"CameraMinZoomDistance",
"CameraMaxZoomDistance",
"CameraMode",
"DevCameraOcclusionMode",
"DevComputerCameraMode", -- Corresponds to StarterPlayer.DevComputerCameraMovementMode
"DevTouchCameraMode", -- Corresponds to StarterPlayer.DevTouchCameraMovementMode
-- Character movement mode
"DevComputerMovementMode",
"DevTouchMovementMode",
"DevEnableMouseLock", -- Corresponds to StarterPlayer.EnableMouseLockOption
}
local USER_GAME_SETTINGS_PROPERTIES =
{
"ComputerCameraMovementMode",
"ComputerMovementMode",
"ControlMode",
"GamepadCameraSensitivity",
"MouseSensitivity",
"RotationType",
"TouchCameraMovementMode",
"TouchMovementMode",
}
--[[ Roblox Services ]]--
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local VRService = game:GetService("VRService")
local UserGameSettings = UserSettings():GetService("UserGameSettings")
-- Static camera utils
local CameraUtils = require(script:WaitForChild("CameraUtils"))
local CameraInput = require(script:WaitForChild("CameraInput"))
-- Load Roblox Camera Controller Modules
local ClassicCamera = require(script:WaitForChild("ClassicCamera"))
local OrbitalCamera = require(script:WaitForChild("OrbitalCamera"))
local LegacyCamera = require(script:WaitForChild("LegacyCamera"))
local VehicleCamera = require(script:WaitForChild("VehicleCamera"))
-- New VR System Modules
local VRCamera = require(script:WaitForChild("VRCamera"))
local VRVehicleCamera = require(script:WaitForChild("VRVehicleCamera"))
-- Load Roblox Occlusion Modules
local Invisicam = require(script:WaitForChild("Invisicam"))
local Poppercam = require(script:WaitForChild("Poppercam"))
-- Load the near-field character transparency controller and the mouse lock "shift lock" controller
local TransparencyController = require(script:WaitForChild("TransparencyController"))
local MouseLockController = require(script:WaitForChild("MouseLockController"))
-- Table of camera controllers that have been instantiated. They are instantiated as they are used.
local instantiatedCameraControllers = {}
local instantiatedOcclusionModules = {}
-- Management of which options appear on the Roblox User Settings screen
do
local PlayerScripts = Players.LocalPlayer:WaitForChild("PlayerScripts")
PlayerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Default)
PlayerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Follow)
PlayerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Classic)
PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Default)
PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Follow)
PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Classic)
PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.CameraToggle)
end
function getOffset()
return player:GetAttribute("CameraOffset") or CFrame.identity
end
function CameraModule.new()
local self = setmetatable({},CameraModule)
-- Current active controller instances
self.activeCameraController = nil
self.activeOcclusionModule = nil
self.activeTransparencyController = nil
self.activeMouseLockController = nil
self.currentComputerCameraMovementMode = nil
-- Connections to events
self.cameraSubjectChangedConn = nil
self.cameraTypeChangedConn = nil
-- Adds CharacterAdded and CharacterRemoving event handlers for all current players
for _,player in pairs(Players:GetPlayers()) do
self:OnPlayerAdded(player)
end
-- Adds CharacterAdded and CharacterRemoving event handlers for all players who join in the future
Players.PlayerAdded:Connect(function(player)
self:OnPlayerAdded(player)
end)
self.activeTransparencyController = TransparencyController.new()
self.activeTransparencyController:Enable(true)
if not UserInputService.TouchEnabled then
self.activeMouseLockController = MouseLockController.new()
local toggleEvent = self.activeMouseLockController:GetBindableToggleEvent()
if toggleEvent then
toggleEvent:Connect(function()
self:OnMouseLockToggled()
end)
end
end
self:ActivateCameraController(self:GetCameraControlChoice())
self:ActivateOcclusionModule(Players.LocalPlayer.DevCameraOcclusionMode)
self:OnCurrentCameraChanged() -- Does initializations and makes first camera controller
RunService:BindToRenderStep("cameraRenderUpdate", Enum.RenderPriority.Camera.Value, function(dt) self:Update(dt) end)
-- Connect listeners to camera-related properties
for _, propertyName in pairs(PLAYER_CAMERA_PROPERTIES) do
Players.LocalPlayer:GetPropertyChangedSignal(propertyName):Connect(function()
self:OnLocalPlayerCameraPropertyChanged(propertyName)
end)
end
for _, propertyName in pairs(USER_GAME_SETTINGS_PROPERTIES) do
UserGameSettings:GetPropertyChangedSignal(propertyName):Connect(function()
self:OnUserGameSettingsPropertyChanged(propertyName)
end)
end
game.Workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
self:OnCurrentCameraChanged()
end)
return self
end
function CameraModule:GetCameraMovementModeFromSettings()
local cameraMode = Players.LocalPlayer.CameraMode
-- Lock First Person trumps all other settings and forces ClassicCamera
if cameraMode == Enum.CameraMode.LockFirstPerson then
return CameraUtils.ConvertCameraModeEnumToStandard(Enum.ComputerCameraMovementMode.Classic)
end
local devMode, userMode
if UserInputService.TouchEnabled then
devMode = CameraUtils.ConvertCameraModeEnumToStandard(Players.LocalPlayer.DevTouchCameraMode)
userMode = CameraUtils.ConvertCameraModeEnumToStandard(UserGameSettings.TouchCameraMovementMode)
else
devMode = CameraUtils.ConvertCameraModeEnumToStandard(Players.LocalPlayer.DevComputerCameraMode)
userMode = CameraUtils.ConvertCameraModeEnumToStandard(UserGameSettings.ComputerCameraMovementMode)
end
if devMode == Enum.DevComputerCameraMovementMode.UserChoice then
-- Developer is allowing user choice, so user setting is respected
return userMode
end
return devMode
end
function CameraModule:ActivateOcclusionModule(occlusionMode: Enum.DevCameraOcclusionMode)
local newModuleCreator
if occlusionMode == Enum.DevCameraOcclusionMode.Zoom then
newModuleCreator = Poppercam
elseif occlusionMode == Enum.DevCameraOcclusionMode.Invisicam then
newModuleCreator = Invisicam
else
warn("CameraScript ActivateOcclusionModule called with unsupported mode")
return
end
self.occlusionMode = occlusionMode
-- First check to see if there is actually a change. If the module being requested is already
-- the currently-active solution then just make sure it's enabled and exit early
if self.activeOcclusionModule and self.activeOcclusionModule:GetOcclusionMode() == occlusionMode then
if not self.activeOcclusionModule:GetEnabled() then
self.activeOcclusionModule:Enable(true)
end
return
end
-- Save a reference to the current active module (may be nil) so that we can disable it if
-- we are successful in activating its replacement
local prevOcclusionModule = self.activeOcclusionModule
-- If there is no active module, see if the one we need has already been instantiated
self.activeOcclusionModule = instantiatedOcclusionModules[newModuleCreator]
-- If the module was not already instantiated and selected above, instantiate it
if not self.activeOcclusionModule then
self.activeOcclusionModule = newModuleCreator.new()
if self.activeOcclusionModule then
instantiatedOcclusionModules[newModuleCreator] = self.activeOcclusionModule
end
end
-- If we were successful in either selecting or instantiating the module,
-- enable it if it's not already the currently-active enabled module
if self.activeOcclusionModule then
local newModuleOcclusionMode = self.activeOcclusionModule:GetOcclusionMode()
-- Sanity check that the module we selected or instantiated actually supports the desired occlusionMode
if newModuleOcclusionMode ~= occlusionMode then
warn("CameraScript ActivateOcclusionModule mismatch: ",self.activeOcclusionModule:GetOcclusionMode(),"~=",occlusionMode)
end
-- Deactivate current module if there is one
if prevOcclusionModule then
-- Sanity check that current module is not being replaced by itself (that should have been handled above)
if prevOcclusionModule ~= self.activeOcclusionModule then
prevOcclusionModule:Enable(false)
else
warn("CameraScript ActivateOcclusionModule failure to detect already running correct module")
end
end
-- Occlusion modules need to be initialized with information about characters and cameraSubject
-- Invisicam needs the LocalPlayer's character
-- Poppercam needs all player characters and the camera subject
if occlusionMode == Enum.DevCameraOcclusionMode.Invisicam then
-- Optimization to only send Invisicam what we know it needs
if Players.LocalPlayer.Character then
self.activeOcclusionModule:CharacterAdded(Players.LocalPlayer.Character, Players.LocalPlayer )
end
else
-- When Poppercam is enabled, we send it all existing player characters for its raycast ignore list
for _, player in pairs(Players:GetPlayers()) do
if player and player.Character then
self.activeOcclusionModule:CharacterAdded(player.Character, player)
end
end
self.activeOcclusionModule:OnCameraSubjectChanged((game.Workspace.CurrentCamera :: Camera).CameraSubject)
end
-- Activate new choice
self.activeOcclusionModule:Enable(true)
end
end
function CameraModule:ShouldUseVehicleCamera()
local camera = workspace.CurrentCamera
if not camera then
return false
end
local cameraType = camera.CameraType
local cameraSubject = camera.CameraSubject
local isEligibleType = cameraType == Enum.CameraType.Custom or cameraType == Enum.CameraType.Follow
local isEligibleSubject = cameraSubject and cameraSubject:IsA("VehicleSeat") or false
local isEligibleOcclusionMode = self.occlusionMode ~= Enum.DevCameraOcclusionMode.Invisicam
return isEligibleSubject and isEligibleType and isEligibleOcclusionMode
end
-- When supplied, legacyCameraType is used and cameraMovementMode is ignored (should be nil anyways)
-- Next, if userCameraCreator is passed in, that is used as the cameraCreator
function CameraModule:ActivateCameraController(cameraMovementMode, legacyCameraType: Enum.CameraType?)
local newCameraCreator = nil
if legacyCameraType~=nil then
--[[
This function has been passed a CameraType enum value. Some of these map to the use of
the LegacyCamera module, the value "Custom" will be translated to a movementMode enum
value based on Dev and User settings, and "Scriptable" will disable the camera controller.
--]]
if legacyCameraType == Enum.CameraType.Scriptable then
if self.activeCameraController then
self.activeCameraController:Enable(false)
self.activeCameraController = nil
end
return
elseif legacyCameraType == Enum.CameraType.Custom then
cameraMovementMode = self:GetCameraMovementModeFromSettings()
elseif legacyCameraType == Enum.CameraType.Track then
-- Note: The TrackCamera module was basically an older, less fully-featured
-- version of ClassicCamera, no longer actively maintained, but it is re-implemented in
-- case a game was dependent on its lack of ClassicCamera's extra functionality.
cameraMovementMode = Enum.ComputerCameraMovementMode.Classic
elseif legacyCameraType == Enum.CameraType.Follow then
cameraMovementMode = Enum.ComputerCameraMovementMode.Follow
elseif legacyCameraType == Enum.CameraType.Orbital then
cameraMovementMode = Enum.ComputerCameraMovementMode.Orbital
elseif legacyCameraType == Enum.CameraType.Attach or
legacyCameraType == Enum.CameraType.Watch or
legacyCameraType == Enum.CameraType.Fixed then
newCameraCreator = LegacyCamera
else
warn("CameraScript encountered an unhandled Camera.CameraType value: ",legacyCameraType)
end
end
if not newCameraCreator then
if VRService.VREnabled then
newCameraCreator = VRCamera
elseif cameraMovementMode == Enum.ComputerCameraMovementMode.Classic or
cameraMovementMode == Enum.ComputerCameraMovementMode.Follow or
cameraMovementMode == Enum.ComputerCameraMovementMode.Default or
cameraMovementMode == Enum.ComputerCameraMovementMode.CameraToggle then
newCameraCreator = ClassicCamera
elseif cameraMovementMode == Enum.ComputerCameraMovementMode.Orbital then
newCameraCreator = OrbitalCamera
else
warn("ActivateCameraController did not select a module.")
return
end
end
local isVehicleCamera = self:ShouldUseVehicleCamera()
if isVehicleCamera then
if VRService.VREnabled then
newCameraCreator = VRVehicleCamera
else
newCameraCreator = VehicleCamera
end
end
-- Create the camera control module we need if it does not already exist in instantiatedCameraControllers
local newCameraController
if not instantiatedCameraControllers[newCameraCreator] then
newCameraController = newCameraCreator.new()
instantiatedCameraControllers[newCameraCreator] = newCameraController
else
newCameraController = instantiatedCameraControllers[newCameraCreator]
if newCameraController.Reset then
newCameraController:Reset()
end
end
if self.activeCameraController then
-- deactivate the old controller and activate the new one
if self.activeCameraController ~= newCameraController then
self.activeCameraController:Enable(false)
self.activeCameraController = newCameraController
self.activeCameraController:Enable(true)
elseif not self.activeCameraController:GetEnabled() then
self.activeCameraController:Enable(true)
end
elseif newCameraController ~= nil then
-- only activate the new controller
self.activeCameraController = newCameraController
self.activeCameraController:Enable(true)
end
if self.activeCameraController then
if cameraMovementMode~=nil then
self.activeCameraController:SetCameraMovementMode(cameraMovementMode)
elseif legacyCameraType~=nil then
-- Note that this is only called when legacyCameraType is not a type that
-- was convertible to a ComputerCameraMovementMode value, i.e. really only applies to LegacyCamera
self.activeCameraController:SetCameraType(legacyCameraType)
end
end
end
-- Note: The active transparency controller could be made to listen for this event itself.
function CameraModule:OnCameraSubjectChanged()
local camera = workspace.CurrentCamera
local cameraSubject = camera and camera.CameraSubject
if self.activeTransparencyController then
self.activeTransparencyController:SetSubject(cameraSubject)
end
if self.activeOcclusionModule then
self.activeOcclusionModule:OnCameraSubjectChanged(cameraSubject)
end
self:ActivateCameraController(nil, camera.CameraType)
end
function CameraModule:OnCameraTypeChanged(newCameraType: Enum.CameraType)
if newCameraType == Enum.CameraType.Scriptable then
if UserInputService.MouseBehavior == Enum.MouseBehavior.LockCenter then
CameraUtils.restoreMouseBehavior()
end
end
-- Forward the change to ActivateCameraController to handle
self:ActivateCameraController(nil, newCameraType)
end
-- Note: Called whenever workspace.CurrentCamera changes, but also on initialization of this script
function CameraModule:OnCurrentCameraChanged()
local currentCamera = game.Workspace.CurrentCamera
if not currentCamera then return end
if self.cameraSubjectChangedConn then
self.cameraSubjectChangedConn:Disconnect()
end
if self.cameraTypeChangedConn then
self.cameraTypeChangedConn:Disconnect()
end
self.cameraSubjectChangedConn = currentCamera:GetPropertyChangedSignal("CameraSubject"):Connect(function()
self:OnCameraSubjectChanged(currentCamera.CameraSubject)
end)
self.cameraTypeChangedConn = currentCamera:GetPropertyChangedSignal("CameraType"):Connect(function()
self:OnCameraTypeChanged(currentCamera.CameraType)
end)
self:OnCameraSubjectChanged(currentCamera.CameraSubject)
self:OnCameraTypeChanged(currentCamera.CameraType)
end
function CameraModule:OnLocalPlayerCameraPropertyChanged(propertyName: string)
if propertyName == "CameraMode" then
-- CameraMode is only used to turn on/off forcing the player into first person view. The
-- Note: The case "Classic" is used for all other views and does not correspond only to the ClassicCamera module
if Players.LocalPlayer.CameraMode == Enum.CameraMode.LockFirstPerson then
-- Locked in first person, use ClassicCamera which supports this
if not self.activeCameraController or self.activeCameraController:GetModuleName() ~= "ClassicCamera" then
self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(Enum.DevComputerCameraMovementMode.Classic))
end
if self.activeCameraController then
self.activeCameraController:UpdateForDistancePropertyChange()
end
elseif Players.LocalPlayer.CameraMode == Enum.CameraMode.Classic then
-- Not locked in first person view
local cameraMovementMode = self:GetCameraMovementModeFromSettings()
self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(cameraMovementMode))
else
warn("Unhandled value for property player.CameraMode: ",Players.LocalPlayer.CameraMode)
end
elseif propertyName == "DevComputerCameraMode" or
propertyName == "DevTouchCameraMode" then
local cameraMovementMode = self:GetCameraMovementModeFromSettings()
self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(cameraMovementMode))
elseif propertyName == "DevCameraOcclusionMode" then
self:ActivateOcclusionModule(Players.LocalPlayer.DevCameraOcclusionMode)
elseif propertyName == "CameraMinZoomDistance" or propertyName == "CameraMaxZoomDistance" then
if self.activeCameraController then
self.activeCameraController:UpdateForDistancePropertyChange()
end
elseif propertyName == "DevTouchMovementMode" then
elseif propertyName == "DevComputerMovementMode" then
elseif propertyName == "DevEnableMouseLock" then
-- This is the enabling/disabling of "Shift Lock" mode, not LockFirstPerson (which is a CameraMode)
-- Note: Enabling and disabling of MouseLock mode is normally only a publish-time choice made via
-- the corresponding EnableMouseLockOption checkbox of StarterPlayer, and this script does not have
-- support for changing the availability of MouseLock at runtime (this would require listening to
-- Player.DevEnableMouseLock changes)
end
end
function CameraModule:OnUserGameSettingsPropertyChanged(propertyName: string)
if propertyName == "ComputerCameraMovementMode" then
local cameraMovementMode = self:GetCameraMovementModeFromSettings()
self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(cameraMovementMode))
end
end
--[[
Main RenderStep Update. The camera controller and occlusion module both have opportunities
to set and modify (respectively) the CFrame and Focus before it is set once on CurrentCamera.
The camera and occlusion modules should only return CFrames, not set the CFrame property of
CurrentCamera directly.
--]]
function CameraModule:Update(dt)
if self.activeCameraController then
self.activeCameraController:UpdateMouseBehavior()
local newCameraCFrame, newCameraFocus = self.activeCameraController:Update(dt)
if self.activeOcclusionModule then
newCameraCFrame, newCameraFocus = self.activeOcclusionModule:Update(dt, newCameraCFrame, newCameraFocus)
end
-- Here is where the new CFrame and Focus are set for this render frame
local currentCamera = game.Workspace.CurrentCamera :: Camera
currentCamera.CFrame = newCameraCFrame * getOffset()
currentCamera.Focus = newCameraFocus
-- Update to character local transparency as needed based on camera-to-subject distance
if self.activeTransparencyController then
self.activeTransparencyController:Update(dt)
end
if CameraInput.getInputEnabled() then
CameraInput.resetInputForFrameEnd()
end
end
end
-- Formerly getCurrentCameraMode, this function resolves developer and user camera control settings to
-- decide which camera control module should be instantiated. The old method of converting redundant enum types
function CameraModule:GetCameraControlChoice()
if player then
if UserInputService:GetLastInputType() == Enum.UserInputType.Touch or UserInputService.TouchEnabled then
-- Touch
if player.DevTouchCameraMode == Enum.DevTouchCameraMovementMode.UserChoice then
return CameraUtils.ConvertCameraModeEnumToStandard( UserGameSettings.TouchCameraMovementMode )
else
return CameraUtils.ConvertCameraModeEnumToStandard( player.DevTouchCameraMode )
end
else
-- Computer
if player.DevComputerCameraMode == Enum.DevComputerCameraMovementMode.UserChoice then
local computerMovementMode = CameraUtils.ConvertCameraModeEnumToStandard(UserGameSettings.ComputerCameraMovementMode)
return CameraUtils.ConvertCameraModeEnumToStandard(computerMovementMode)
else
return CameraUtils.ConvertCameraModeEnumToStandard(player.DevComputerCameraMode)
end
end
end
end
function CameraModule:OnCharacterAdded(char, player)
if self.activeOcclusionModule then
self.activeOcclusionModule:CharacterAdded(char, player)
end
end
function CameraModule:OnCharacterRemoving(char, player)
if self.activeOcclusionModule then
self.activeOcclusionModule:CharacterRemoving(char, player)
end
end
function CameraModule:OnPlayerAdded(player)
player.CharacterAdded:Connect(function(char)
self:OnCharacterAdded(char, player)
end)
player.CharacterRemoving:Connect(function(char)
self:OnCharacterRemoving(char, player)
end)
end
function CameraModule:OnMouseLockToggled()
if self.activeMouseLockController then
local mouseLocked = self.activeMouseLockController:GetIsMouseLocked()
local mouseLockOffset = self.activeMouseLockController:GetMouseLockOffset()
if self.activeCameraController then
self.activeCameraController:SetIsMouseLocked(mouseLocked)
self.activeCameraController:SetMouseLockOffset(mouseLockOffset)
end
end
end
local cameraModuleObject = CameraModule.new()
return {}
There is a function called getOffset. It returns the camera offset, and you may set it up as needed.
Edit: just tested, only works in a few specific camera types.
This is still not what I want, I wanna do it without having to edit a module, like setting Camera.CFrame
If that is what you want, then it is not possible with the default camera system without editing the module.
People have done it without editing the module, why is it now that I can’t without having to edit it???
It depends on how your camera offset works. If it is a custom camera system, then you can set it to scriptable and change the CFrame from your current camera manager. If not, then you have to do many hacky runservice types of things that I have no knowledge of.