Hey everyone! This is a bit of a niche problem, and I figure half of the issue is that I’m thinking about the wrong solutions, but this is what I’ve got right now - essentially I’m working on a spawn selection menu that uses CFrame’s LookAt to point the Camera towards the spawn that has been selected.
(the little coloured parts are the spawns for reference)
This ostensibly works as intended, as the Camera does in-fact place the correct spawn at the center of the screen, but its not centre relative to the actually exposed portion of the screen. Is there any way to change the behaviour of the Camera such that the spawn will end up in the middle of the unobstructed part of the screen?
Code I’m using for good measure below:
local function ChangeSpawn()
local CameraPosition = Vector3.new(SelectedSpawn.Position.X + 50, SelectedSpawn.Position.Y + 100, SelectedSpawn.Position.Z + 50)
Camera.CFrame = CFrame.new(CameraPosition, SelectedSpawn.Position)
-- camera stuff ^^
-- ||
-- GUI stuff vv
SelectedSpawn = Spawns[count]
script.Parent.Parent.Selected.Text = SelectedSpawn.Name
end
If it’s just for spawn, aka a one time use thing, I’d just make a static offset e.g. +20 studs in whatever direction and try that. If you need it alot then you’d probably want to calculate said offset each time, which ai might be able to find some formula for you.
Basically I think there’s alot of maths to actually find the relative center so you might want to avoid it
You’d have to do some CFrame wizardry and maybe even use ViewportToRay / WorldToViewportPosition mathematics. The way I’d do this is the following:
Insert an invisible Frame with size 0,0,0,0 into your UI and position it to the new ‘centre’ of where you want to focus your target object.
Position your camera where you need it
Get the absolute position of the UI point, and get the screenpoint of the target object using cam:WorldToScreenPoint.
Convert the screen points into world CFrames (the camera screen has a physical ‘size’ and the distance from the top-left of the screen to the bottom-right is dependant on the screen ratio but usually 0.2-0.4 units)
Get the offset of the two points by using CFrame:ToObjectSpace()
Multiply the camera’s CFrame by this offset
This was a really interesting problem to solve, I had fun working it out! This is my working code that’s placed within a ScreenGui.
local cam = game.Workspace.CurrentCamera
local target = game.Workspace.StarterCharacter.Head
local camPart = game.Workspace:WaitForChild("CamPart")
local UICentreRef: Frame = script.Parent.Frame
cam.CameraType = "Scriptable"
function RayToCF(ray: Ray)
return CFrame.new(ray.Origin, ray.Origin + ray.Direction)
end
function AbsolutePosToCamCF(absolutePos: Vector2)
return RayToCF( cam:ScreenPointToRay(absolutePos.X, absolutePos.Y, 0) )
end
game:GetService("RunService").RenderStepped:Connect(function()
cam.CFrame = CFrame.new(camPart.Position, target.Position)
local absolutePosition_UI = UICentreRef.AbsolutePosition
local vpp_TargetObject = cam:WorldToScreenPoint(target.Position)
local absolutePosition_TargetObject = Vector2.new(vpp_TargetObject.X, vpp_TargetObject.Y)
-- now convert the screen points into world positions so I can accurately translate the camera CFrame
local screenpointWorldCF_UI = AbsolutePosToCamCF(absolutePosition_UI)
local screenpointWorldCF_TargetObject = AbsolutePosToCamCF(absolutePosition_TargetObject)
-- this converts the screenpointWorldCFrame of the target object into the relative CFrame space of the UI CFrame.
-- so it should let us know how we should translate the camera in to make the target object centred the the UI point we specified.
local screenpointWorldDifference = screenpointWorldCF_UI:ToObjectSpace(screenpointWorldCF_TargetObject)
cam.CFrame *= screenpointWorldDifference
end)
Here’s it working:
I added a UIDragDetector to my UI point for fun. It’s not 100% perfect, as you can see when getting very close to the corners of the screen the camera rolls a little, but it’s very close.
Edit: I later discovered that this method struggles when the target object is directly above/below the camera - the camera will rotate around the Y axis of the world position of the centre of the screen, so your camera’s rotation will not cooperate how you want it. Something to do with how CFrame calculates orientation when using CFrame.new(fromPoint, lookAtPoint).
This was actually done by manipulating the CFrame matrices of the camera to offset it to the right.
This is fairly simple to pull off by using this module.
Code used (in conjunction with the module linked above):
local distortion = require(PATH_HERE)
distortion.Position = UDim2.fromScale(0.25, 0)
distortion.Start()
I believe your solution is the solution. I was about to recommend a simple ViewportFrame solution, but the use of that distort module seems better.
Since I tried out a solution before you wrote that, here’s what I’d do with ViewportFrames, @SomeAustralianMate:
-- New stuff
local ViewportFrame = script.Parent
-- Set up the Camera instance here because descendant Cameras under ViewportFrames don't load for some reason (see https://devforum.roblox.com/t/camera-instance-and-viewport-frames-problem/3802616)
local Camera = Instance.new("Camera")
ViewportFrame.CurrentCamera = Camera
-- Change this to however you set your variables
local Spawns = ViewportFrame.Spawns:GetChildren()
local count = 1
local SelectedSpawn = Spawns[count]
-- Same function you used
local function ChangeSpawn()
local CameraPosition = Vector3.new(SelectedSpawn.Position.X + 50, SelectedSpawn.Position.Y + 100, SelectedSpawn.Position.Z + 50)
Camera.CFrame = CFrame.new(CameraPosition, SelectedSpawn.Position)
-- camera stuff ^^
-- ||
-- GUI stuff vv
SelectedSpawn = Spawns[count]
script.Parent.Parent.Selected.Text = SelectedSpawn.Name
end
-- I used this for testing
while true do
ChangeSpawn()
count = (count)%(#Spawns) + 1
task.wait(0.5)
end
You can change the center of the spawn preview window by adjusting the properties of the ViewportFrame. Do be mindful of the limitations of ViewportFrames if you decide to use this solution with more instances under the VF:
I think @thom463s has the best solution for this problem–I haven’t used the module described, but it seems like it works the best here due to the limits of ViewportFrames.
Just wanted to send this for learning purposes.. and also bump this feature request fr
Oh wow this is crazy! Thank you and everyone else that replied with solutions. I really ought to use this forum more often - I’ll try everything out next time I open studio and return with the results!