Problem with making 2D camera

For the current hours I’ve been trying to build a 2D camera that moves only on the Z axis. After a little bit of navigation on the Roblox documentation page I managed to create this script by myself:

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local camera = game.Workspace.CurrentCamera
local plr = Players.LocalPlayer

local height = 2
local depth = 20

local function getRoot()
	local char = plr.Character
	local root = nil
	if char then
		root = char:FindFirstChild("HumanoidRootPart")
	end
	return root
end
task.spawn(function()
	local function updateCamera()
		while task.wait() do
			local con = getRoot()

			if con then
				local rootPos = con.Position + Vector3.new(0, height, 0)
				local cameraPos = Vector3.new(rootPos.X, rootPos.Y, depth)

				camera.CFrame = CFrame.lookAt(rootPos, cameraPos)
			end
		end
	end

	RunService:BindToRenderStep("Focus", Enum.RenderPriority.Camera.Value + 1, updateCamera)
end)

The script that is on the Roblox documentation page works as the following sentence says. As soon as you spawn it locks 20 studs away from you on the Z axis. And if you try to get yourself in the opposite direction of the orientation, as soon as you reach 0 studs from the original position you start to glitch. The thing that I want is to constantly keep a 20 studs distance from the player’s HumanoidRootPart; not to lock it.

I’ve done everything in the upper script but when I try to move my mouse, I simply can’t. Test the script yourself if you want to know what I mean.

Anyway, here is the second script from the documentation page if you are interested:


local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local player = Players.LocalPlayer
local camera = workspace.CurrentCamera

local CAMERA_DEPTH = 24
local HEIGHT_OFFSET = 2

local function updateCamera()
    local character = player.Character
    if character then
        local root = character:FindFirstChild("HumanoidRootPart")
        if root then
            local rootPosition = root.Position + Vector3.new(0, HEIGHT_OFFSET, 0)
            local cameraPosition = Vector3.new(rootPosition.X, rootPosition.Y, CAMERA_DEPTH)
            camera.CFrame = CFrame.lookAt(cameraPosition, rootPosition)
        end
    end
end

RunService:BindToRenderStep("SidescrollingCamera", Enum.RenderPriority.Camera.Value + 1, updateCamera)

I want to peacefully move my mouse while keeping a 20 studs distance from the character, that’s all. Any attempt of solution will be appreciated.

2 Likes

Hello!

I don’t know if this is the solution to your problem, but I will try my best.

Firstly, I would recommend removing the while loop inside the updateCamera function, because the RunService:BindToRenderStep(), where your function is already bound to, is already a kind of loop. This will create an effect, where every render loop a new, infinite while loop is being created.

Secondly, to avoid weird camera behavior, one can set the camera property CameraType to either Scriptable or Custom.

The problem is that the Camera is constantly positioned at the root position, and will look towards itself, except that Z is being 20. To fix this, we want to position the camera 20 studs away of the player, and make it look at the character, like this:

camera.CFrame = CFrame.lookAt(cameraPos, rootPos)

However, this still wouldn’t fix the issue, since the depth is not added to the root position, but instead set to a constant. So what we can do is add the Vector3 to the rootpos like this:

local cameraPos = rootPos + Vector3.new(0, 0, depth)

This should keep the camera constantly 20 studs away of the HumanoidRootPart, so, at the end, updateCamera looks like this:

local function updateCamera()
	local con = getRoot()

	if con then
		local rootPos = con.Position + Vector3.new(0, height, 0)
		local cameraPos = rootPos + Vector3.new(0, 0, depth)

		camera.CFrame = CFrame.lookAt(cameraPos, rootPos)
	end
end

However, I don’t know if this will fix the mouse issue, but I really hope that I could help you further!

2 Likes

If I will remove the while loop from the function the while loop won’t have purpose. And players usually don’t stay in one position so I need a while loop to constantly check their position and return a value so I can set the :BindToRenderStep event (correct me if it’s not an event I forgot how : keywords are named) to give them the final result. If I remove the while loop everything will run once and the player’s camera will be locked.

2 Likes

I want to make me clear once and for all. I only want the player’s camera to be positioned 20 studs away from it on the Z axis and when the player’s decides to maybe move the mouse so that he could change the angle of the screen nothing would happen, simply nothing, like, he could move the mouse around the screen but not to change the angle (the mouse will have no purpose on adjusting screen view). Maybe I need to disable something, if so please tell me.

1 Like

Hello again!

Ah, sorry, I should have explained further.

What RunService:BindToRenderStep does is connecting a function with the render loop, and is thereby very similar to the event RunService.RenderStepped. The render loop is responsible for “drawing” the objects in the game world onto your screen, so you can see what’s happening. In the best case, the loop goes as fast as 60 frames per second (60 FPS), and that is how often the event is called.

So, you see, by binding your function to it, your function will be called every sixtieth of a second, until unbound via RunService:UnbindFromRenderStep, and that’s good, because you’re using it together with the Camera, which is responsible for setting what is going to be rendered. You can test it yourself like this:

local function helloWorld()
	print("Hello World!")
end

game:GetService("RunService"):BindToRenderStep("test", Enum.RenderPriority.Last.Value, helloWorld)

In short, you don’t need an infinite while loop inside your function anymore, since RunService:BindToRenderStep already does the job for you, and leaving it inside can cause strange camera behavior and performance issues.

when the player’s decides to maybe move the mouse so that he could change the angle of the screen nothing would happen.

Ah, great! Then I didn’t misunderstand you, and I hope I could clarify what I meant in my last answer. Like always, I hope I could help further!

2 Likes

Here are the results with the while true do loop in the spawn.function():

robloxapp-20230527-1554589.wmv (1.2 MB)

That’s what I was trying to tell you, you see how the camera distance changes everytime. Also, in the video I’m trying to move the camera using the mouse but I can’t because of the localscript. But the task.spawn() function the while loop and the :BindToRenderStep loop turned out to be a dumpsterfire for my computer graphical performance, because of this my computer became really laggy.

Here is the result based on your specifications:

robloxapp-20230527-1559445.wmv (2.5 MB)

This happens because the updateCamera() function returns only one value. You can see that when I’m trying to go in the opposite direction it blocks me to do so. But when when I go the same direction I can go as further as I want.

I need specifications for the LocalScript to not be laggy like my script did, to have the mouse disabled when it comes to camera angle-changing and to have a function that will constantly return a value that the :BindToRenderStep loop will be based on.

2 Likes

Hmm, oh no, I think I understand what you mean (correct me if I’m wrong), but I think the second result comes to be because the camera position Z-Axis is not relative to the HumanoidRootPart Position.

So, to clear up everything, I will post the important parts of the script I meant above here:

camera.CameraType = Enum.CameraType.Scriptable  -- This will fix some Problems

local function updateCamera()
	local con = getRoot()

	if con then
		local rootPos = con.Position + Vector3.new(0, height, 0)
		local cameraPos = rootPos + Vector3.new(0, 0, depth)

		camera.CFrame = CFrame.lookAt(cameraPos, rootPos)
	end
end

RunService:BindToRenderStep("Focus", Enum.RenderPriority.Camera.Value + 1, updateCamera)

Here is the result with this code.
robloxapp-20230527-1545064.wmv (2.3 MB)

EDIT: You may also want to set CameraMinZoomDistance inside StarterPlayer to the same value as CameraMaxZoomDistance (default 128), because else your Player can become invisible when scrolled in too much.

This is what I believe you mean, but if I can still help you in any way, please write it in the replies.

2 Likes

Put this in StarterCharacterScripts

local camera = game.Workspace.CurrentCamera --First we need to get the camera

local player = game:GetService("Players").LocalPlayer --Next the player

local character = player.Character or player.Character:Added() --Make sure we got the character!

--Next we will create a function and name it cameraUpdate!

function cameraUpdate() --next we get the torso!

local torso = character:WaitForChild("Torso") --Here is the player's torso! we use WaitForChild just in case they die.

--Now we can set the camera coordinateframe to all torso cframes!

camera.CoordinateFrame = CFrame.new(torso.CFrame.X + 8 --[[This is the X position change this to what you want.]],torso.CFrame.Y + 2.4 --[[Change 2.4 which is the y value to whatever you like]] ,75 --[[This is the Z value or the distance change it to whatever you like]])

end

game:GetService("RunService").RenderStepped:connect(cameraUpdate) --and last we connect the runservice to the function!

You can just go into the starter player properties and change the min and max camera distance to 20

Oof, finally, turns out everything that was needed was that single-line-variable → camera.CameraType = Enum.CameraType.Scriptable that you’ve shown me. Thank you very much!

Ah, you’re welcome! I’m glad it worked!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.