"Advanced" Lock-on Logic

  1. What do you want to achieve?
    If you look closely in most souls games, the lock-on gives a little bit of lax in either direction before it starts following the player again. I’m trying to mimic that as closely as possible. For example, if I were 5 studs to the left of the enemy, the camera would still be in the same position if I were perfectly aligned. Anything greater than 5 or less than -5 would begin following me again, still with that bit of leeway.

Here’s my beautiful artistry to show what I mean:

  1. What is the issue?
    I’ve tried a few things but it either results in extremely jittery madness or just wacky stuff like looking up through the floor.

  2. What solutions have you tried so far?
    Moving both currentCamera and the cameraPart itself.

This code works as expected, but has no “leeway” or anything implemented.

Action.Pressed:Connect(function()
	if connection then
		disconnect()
	else
		local mouseTarget = mouse.Target
		if mouseTarget and mouseTarget.Parent ~= Player.Character then
			local character = Player.Character
			local root = character:WaitForChild("HumanoidRootPart")
			local humanoid = character:WaitForChild("Humanoid")
			
			local eCharacter = mouseTarget.Parent
			local eRoot = eCharacter:FindFirstChild("HumanoidRootPart")
			local eHumanoid = eCharacter:FindFirstChild("Humanoid")
			
			target = eCharacter
			local distance = (root.Position - mouseTarget.Position).Magnitude
			
			if eRoot and distance < maxDistance then
				camera.CameraType = Enum.CameraType.Scriptable
				UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
				UserInputService.MouseIconEnabled = false
				
				local markerClone = marker:Clone()
				markerClone.Parent = eRoot
				local clonedCamera = cameraPart:Clone()
				clonedCamera.Parent = root
				clonedCamera.CFrame = root.CFrame * offset
				local weld = Instance.new("WeldConstraint", root)
				weld.Part0 = clonedCamera
				weld.Part1 = root
				
				local previousERootPosition = Vector3.zero
				local previousRootPosition = Vector3.zero

				connection = RunService.PreRender:Connect(function()
					local currentRootPosition = root.Position
					local currentERootPosition = eRoot.Position
					
					if currentERootPosition ~= previousERootPosition or currentRootPosition ~= previousRootPosition then
						camera.CFrame = clonedCamera.CFrame

						root.CFrame = CFrame.lookAt(
							currentRootPosition,
							Vector3.new(currentERootPosition.X, currentRootPosition.Y, currentERootPosition.Z)
						)

						camera.CFrame = CFrame.lookAt(
							clonedCamera.Position, 
							currentERootPosition
						)				

						previousERootPosition = currentERootPosition
						previousRootPosition = currentRootPosition
					end

					local distance = (currentERootPosition - currentRootPosition).Magnitude
					if eHumanoid.Health <= 0 or humanoid.Health <= 0 or distance > maxDistance or not eCharacter or not character then
						disconnect()
					end
				end)
			end
		end
	end
end)

Thank you :>

Hello! I found a solution. Janky? Probably. Works? Good enough.


local difference = currentERootPosition - currentRootPosition
local distance = difference.Magnitude
local direction = difference.Unit

root.CFrame = CFrame.lookAt(
	currentRootPosition,
	Vector3.new(currentERootPosition.X, currentRootPosition.Y, currentERootPosition.Z)
)

local angle = math.clamp(math.deg(math.atan2(direction.Z, direction.X)), minimumAngle, maxAngle)
local horizontalOffset = (90 - angle) / angleDividend

local baseCFrame = root.CFrame * offset
local modifiedCFrame = baseCFrame * CFrame.new(horizontalOffset, 0, math.abs(horizontalOffset / (angleDividend * 2)))

clonedCamera.CFrame = modifiedCFrame
camera.CFrame = clonedCamera.CFrame

camera.CFrame = CFrame.lookAt(
	clonedCamera.Position, 
	currentERootPosition
)