Custom Camera System!

Hey Developers!

Hey guys, I made a very interesting, (To me at least) Funny, working camera system. And I have chosen to open-source it in case any of you wish to use it inside of one of your games.

About the camera system:

The camera system is a very smooth tweening camera that follows your players head. I didn’t like the roblox’s form of the camera for a special puzzle game I’m making, so I decided to make a new one and happend to stubble across what I bring to you today.

Features:

  • Zoom (Keybinds, R & T)
  • LookLeft or LookRight (Keybinds, Q & E)
  • Smooth follow
  • Adjusted First Person (Realistic-ish, still funky.)

NOTICE!

The code isn’t made for anything serious, It’s a little unstable (first-person wise) and is mainly focused on being enjoyed in cool / testable games. If you wish for it to be configured for something serious please send me a message!

Setup:

Your setup should look something like this:

image

  • The value I added (Active) it so you can chose weather or not to use the mouse feature, just toggle the active value on or off.

The location of the camera should be under your starter character scripts, or “Custom_Rig” if you happen to be using one. (Not recommended, will configure if you guys need it.)

Here is the file for installation if you don’t feel like looking over the code:

cam.rbxm (3.4 KB)

Visible features/Video:

Requested to view what the camera system has to offer, I have put it on a stream-able. Please provide feedback in the comments on anything you’d like changed, I’m available to do so.

New update log:

  • Camera lock for first person. (Simply aim where you want it to lock to, then zoom all the way in.)
  • Accessories cleanup. Suggested by: uhi_o
  • Easier usage for everyone, forum post cleanup.

Soon to be updated:

  • Shake effects
  • Dampening on certain animations. (For first person.)
  • Cleaner first person movement overall.
  • Camera Type values adjustable by module/bool values. (Overall update)

Source code:

-- Assets:
local Locked = false
self = script.Parent

-- Services
local RunService = game:GetService("RunService")
local TS = game:GetService("TweenService")
local UIS = game:GetService("UserInputService")

-- Camera
local Camera = workspace.CurrentCamera
Camera.CameraType = Enum.CameraType.Scriptable

-- Camera Tween
local function TweenTo(Pos)
	if Locked == false then
    	TS:Create(Camera,TweenInfo.new(2,Enum.EasingStyle.Quint,Enum.EasingDirection.Out),{CFrame = Pos * CFrame.Angles(270,math.rad(2),0)}):Play()
	elseif Locked == true then
		TS:Create(Camera,TweenInfo.new(.1,Enum.EasingStyle.Quint,Enum.EasingDirection.Out),{CFrame = Pos * CFrame.Angles(270,math.rad(2),0)}):Play()
	end
end

-- Values
local lookRight = false
local lookLeft = false

local Default = true -- If they want to stop looking right or left.
local Zooming = true -- If they want zooming enabled.

local Y = 5
local X = 15

-- Main function
UIS.InputBegan:Connect(function(io, IsTyping)
	if IsTyping then return end
	
	-- Left:
	if io.KeyCode == Enum.KeyCode.Q and lookLeft == false and lookRight == false and Locked == false  then
		Default = false
		lookLeft = true
		lookRight = false
		RunService.RenderStepped:Connect(function()
			if lookLeft == true and lookRight == true then
				lookRight = false
    			TweenTo(self.Head.CFrame * CFrame.new(-5,Y,X))
			elseif lookLeft == true and lookRight == false then
				TweenTo(self.Head.CFrame * CFrame.new(-5,Y,X))
			end
		end)
	elseif io.KeyCode == Enum.KeyCode.Q and lookLeft == true and lookRight == false and Locked == false  then
		Default = true
		lookLeft = false
		RunService.RenderStepped:Connect(function()
			if lookLeft == false and lookRight == false then
    			TweenTo(self.Head.CFrame * CFrame.new(0,Y,X))
			end
		end)
	end
	
	-- Right:
	if io.KeyCode == Enum.KeyCode.E and lookRight == false and lookLeft == false and Locked == false  then
		Default = false
		lookLeft = false
		lookRight = true
		RunService.RenderStepped:Connect(function()
			if lookRight == true and lookLeft == true then
				lookLeft = false
    			TweenTo(self.Head.CFrame * CFrame.new(5,Y,X))
			elseif lookRight == true and lookLeft == false then
				TweenTo(self.Head.CFrame * CFrame.new(5,Y,X))
			end
		end)
	elseif io.KeyCode == Enum.KeyCode.E and lookRight == true and lookLeft == false and Locked == false then
		Default = true
		lookRight = false
		RunService.RenderStepped:Connect(function()
			if lookRight == false and lookLeft == false then
    			TweenTo(self.Head.CFrame * CFrame.new(0,Y,X))
			end
		end)
	end
	
	-- Zoom:
	if io.KeyCode == Enum.KeyCode.R then
		if X <= 45 and Y <= 15 then
			X = X + 15
			Y = Y + 5
			if X > 0 and X <= 15 and Y > 0 and Y <= 5 then
				print(
					"Unlocked!"
				)
				Locked = false
			end
		end
	elseif io.KeyCode == Enum.KeyCode.T then
		if X >= 15 and Y >= 5 then
			X = X - 15
			Y = Y - 5
			if X == 0 and Y == 0 then
				warn(
					"Locked!"
				)
				Locked = true
			end
		end
	end
end)
-- Mouse Settings:
RunService.RenderStepped:Connect(function()
	warn(
		script.Active.Value,
		Locked
	)
	if (script.Active.Value) and not (Locked) then
		require(script.Mouse).FollowMouse()
	end
end)

-- Default:
RunService.RenderStepped:Connect(function()
	if Default == true then
		TweenTo(self.Head.CFrame * CFrame.new(0,Y,X))
	end
end)

-- Accessories:
RunService.RenderStepped:Connect(function()
	for _, v in pairs(self:GetChildren())do
		if X == 0 and Y == 0 then
			if v.Name == 'Head' then
				v.LocalTransparencyModifier = 1
				v.CanCollide = false
			end
		else
			if v:IsA'Part' or v:IsA'UnionOperation' or v:IsA'MeshPart' then
				v.LocalTransparencyModifier = 0
				v.CanCollide = false
			end
		end
		if v:IsA'Accessory' and X == 0 and Y == 0 then
			v:FindFirstChild('Handle').LocalTransparencyModifier = 1
			v:FindFirstChild('Handle').CanCollide = false
		end
		if v:IsA'Hat' and X == 0 and Y == 0 then
			v:FindFirstChild('Handle').LocalTransparencyModifier = 1
			v:FindFirstChild('Handle').CanCollide = false
		end
     end
end)

Follow-up module:


local RunService = game:GetService("RunService")

local Player = game.Players.LocalPlayer
local PlayerMouse = Player:GetMouse()

local Camera = workspace.CurrentCamera

local Character = Player.Character or Player.CharacterAdded:Wait()
local Head = Character:WaitForChild("Head")
local Neck = Head:WaitForChild("Neck")

local Torso = Character:WaitForChild("UpperTorso")
local Waist = Torso:WaitForChild("Waist")

local Humanoid = Character:WaitForChild("Humanoid")
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")

local NeckOriginC0 = Neck.C0
local WaistOriginC0 = Waist.C0

Neck.MaxVelocity = 1/3

module.FollowMouse = function()
	local CameraCFrame = Camera.CoordinateFrame
	
	if Character:FindFirstChild("UpperTorso") and Character:FindFirstChild("Head") then
		local TorsoLookVector = Torso.CFrame.lookVector
		local HeadPosition = Head.CFrame.p
		
		if Neck and Waist then
			if Camera.CameraSubject:IsDescendantOf(Character) or Camera.CameraSubject:IsDescendantOf(Player) then
				local Point = PlayerMouse.Hit.p
				
				local Distance = (Head.CFrame.p - Point).magnitude
				local Difference = Head.CFrame.Y - Point.Y
				
				Neck.C0 = Neck.C0:lerp(NeckOriginC0 * CFrame.Angles(-(math.atan(Difference / Distance) * 0.5), (((HeadPosition - Point).Unit):Cross(TorsoLookVector)).Y * 1, 0), 0.5 / 2)
				Waist.C0 = Waist.C0:lerp(WaistOriginC0 * CFrame.Angles(-(math.atan(Difference / Distance) * 0.5), (((HeadPosition - Point).Unit):Cross(TorsoLookVector)).Y * 0.5, 0), 0.5 / 2)
			end
		end
	end	
end

return module

Make sure to name the followup module “Mouse” and put it under your “Camera” script inside the character! And add a bool value called “Active” to toggle on weather or not to follow the mouse! Without the value, the script will not run!

(Should probably update that…)

Thanks for so much engagement on my first official topic! And I hope to continue to get more positive feedback and critique on my code and what to improve. Thank you everyone!

~Scriptuyter

80 Likes

Nice!
Sure to use and credit it in my game and see what imaginative features I can do!

It’s not 100% made for any type of serious game, It’s just meant to be “funky” in a way, and is pretty cool to control in my opinion. So use it at your own risk, though If you still wish to use it I can edit it to more fit your needs, and if more people request I’ll make it a default setting.

3 Likes

Make sure to check out the new solution so you can make use of it properly, sorry about that.

2 Likes

Just tried it out and it is ABSOLUTELY incredible! It is so funky and cool!

11/10

2 Likes

Thanks for open sourcing something wonderful like that. I could surely use it for my ragdoll game for some funny camera movements.
Just one thing, if you are going to work on future updates I would recommend you make the player’s accessories invisible while in first person.
Have a nice day!
Thank!

1 Like

I can update that quickly actually, when I get back from work I will surely take that into consideration, Thanks for the really nice suggestion!

~Scriptuyter

2 Likes

Looks really good! I love it when users open source projects for others to use. Much respect for this, pretty clean and cool to use. This will definitely be useful for people trying to make an RPG.

2 Likes

I’ve shortened the title a bit. You should try to keep instructions within the body of the thread itself. Cool stuff, by the way.

3 Likes

I just made the addition to the code that you mentioned, here you go:

-- Accessories:
RunService.RenderStepped:Connect(function()
	for _, v in pairs(character:GetChildren())do
		if X == 0 and Y == 0 then
			if v.Name == 'Head' then
				v.LocalTransparencyModifier = 1
				v.CanCollide = false
			end
		else
			if v:IsA'Part' or v:IsA'UnionOperation' or v:IsA'MeshPart' then
				v.LocalTransparencyModifier = 1
				v.CanCollide = false
			end
		end
		if v:IsA'Accessory' and X == 0 and Y == 0 then
			v:FindFirstChild('Handle').LocalTransparencyModifier = 1
			v:FindFirstChild('Handle').CanCollide = false
		end
		if v:IsA'Hat' and X == 0 and Y == 0 then
			v:FindFirstChild('Handle').LocalTransparencyModifier = 1
			v:FindFirstChild('Handle').CanCollide = false
		end
     end
end)```
2 Likes

I am just getting loops of errors in the output like this.
I will try to figure something out.

 [08:43:37.319 - Transparency is not a valid member of Accessory](rbxopenscript://www.dummy.com/dummy?scriptGuid=%7B02C24C3E%2D88EF%2D499A%2DA1D9%2D0FEAE617ADA3%7D&gst=2#113)

08:43:37.321 - Stack Begin

[08:43:37.322 - Script 'Workspace.uhi_o.LocalScript', Line 113](rbxopenscript://www.dummy.com/dummy?scriptGuid=%7B02C24C3E%2D88EF%2D499A%2DA1D9%2D0FEAE617ADA3%7D&gst=2#113)

08:43:37.324 - Stack End

Did that work for you?

Thanks for trying!

Edit: It works properly but the error in the output is bugging me. If there is any way to fix that I would apreciate it but it’s fine.

1 Like

I went back and re-vised the code, please re-look over it and tell me if there is any errors.

1 Like

I’m confused by what you mean here. Could you give a bit more detail on setting up the script, please?

1 Like

I figured out how finally and updated the thread so everyone can kind of use it. I would like your opinion on the updated version if you happen to see this, thanks.

Weeee!
https://gyazo.com/c6e98afd0524acb4ecfa884e7917c951

Edit: Haha this is fun to play around!

3 Likes

Thanks!

You should try using the follow module to and see if you like how that works. It’s all good though, your choice!

1 Like

Updated source, please go back over the topic.

I didnt really understand but because of the GIF show by MmadProgeammer. Now I do, its pretty incredible and satisfying

1 Like

The streamable was just how i’d express itself from a game POV.

2 Likes

Is this also possible to active it when u click the right mouse click?

1 Like