How do I simulate "weighty" gun reloads by manipulating the camera?

Videoed here, in Blood Engine, is exactly what I’m looking to achieve:

Take a peek at how the camera orientates itself relative to the gun when equipping and reloading, and simultaneously how the crosshair does the inverse to maintain its position. I can likely figure out one from the other, but I’m not sure how to work out the math to even start. I thought it’d maybe be something like:

  1. Get the position of the camera
  2. Get the position of the gun’s Handle
  3. Calculate a halfway position point between the camera and the gun
  4. Smoothly lerp the camera’s CFrame angles toward said point
  5. Rinse and repeat each frame with RenderStepped

Just after writing the above down, I gained some confidence to try on my own and got this so far (a lot of the code is purged of course):

local function camReload()
	local halfwayPoint = Camera.CFrame.Position:Lerp(VMTool.Handle.Position, 0.2)
	
	local targetCF = CFrame.lookAt(Camera.CFrame.p, halfwayPoint)
	
	Camera.CFrame = Camera.CFrame:Lerp(targetCF, 0.1)
end

RunService.RenderStepped:Connect(function()
	if reloading then
		camReload()
	end
end)

I think this code has a recursive effect, leading to nauseating camera spinning during testing. I’m assuming its because I’m not manipulating off the base camera CFrame each time, however I’m not even sure how to get this. Whereas you can predict easily the position something like a crosshair GUI will be, the player’s camera CFrame is ever-changing as they look around. It’s not pictured in this code, but I had tried a solution where I get the camera CFrame, subtract whatever changes I made to it the previous frame in hopes of getting back what the base CFrame should be and then going from there, though it didn’t seem to work and ended up giving my camera a seizure.

I’ll likely work more on this, but in the meantime I’d love for someone to lend a hand, particularly towards a solution to avoid the recursiveness. Thanks!


Old edit, irrelevant now

Edit 1:

Little bit of a realization. While the above code still has a recursive effect and causes its own issue, much of the camera spinning was caused by a different recursive issue. Since I’m using a view-model, I have to set the CFrame of the view-model to the camera every frame. This is an issue when reloading because when the camera moves to follow the gun, the view-model (including the gun) will follow it, and then the camera will follow the gun again next frame, and it ends up in a kind of chase.

I disabled this temporarily when reloading, and I’ll have to come back around to fix it, but it also highlights the issue with the other camera manipulation code:

Again, I think this can be fixed if I find out a way to get the “base” CFrame of the camera, but until then the code basically restricts your camera to look at the gun until the reloading is finished.

3 Likes

Why don’t you change the method? Other than moving both the camera and the crosshair to match that effect, just move the viewmodel. Give it a specific rotation and position offset and it’ll work. I actually think that’s how it was done as I paused the video before and after the rotation and took pictures to analyze, they were pretty much the same, obviously with some offset because of the camera bobbing but you get my idea.

1 Like

I was able to replicate the same effects by using Camera:WorldToScreenPoint().

It does require a custom first person engine though where you have control of the camera cframe and the offset/impulse used to pan the camera for gun reloads.

Demo place is here:

4 Likes

If I’m not misunderstanding you, you’re suggesting I apply this effect to only the view-model, but that alone won’t achieve the specific artistic effect I’m looking for. By manipulating the camera, I’m hoping the guns will feel more “weighty” and responsive to reload, almost as if your body is moving from the momentum and weight during the process.

1 Like

WorldToScreenPoint is new to me, I think that may help with the crosshair positioning later.

Though it’s not out of the question, I’m not entirely sure I’d like to delve into the process of forking ROBLOX’s camera for this one specific-use case. I’d like to roll with it for now, and so I came up with a kind of solution by scouring other posts and giving another shot based on what I had gathered thus far.

With the code below, I think I’ve eliminated all the recursiveness (including the issue with the view-model CFrame-setting).

local OldAngle = CFrame.new()
local NewAngle = CFrame.new()

local function camReload2()
	local CamCFrame = Camera.CFrame*OldAngle:Inverse()
	
	local CamPos = CamCFrame.Position
	local HandlePos = VMTool.Handle.Position
	
	local lerpedPoint = CamPos:Lerp(HandlePos, 0.1)
	local targetCF = CFrame.lookAt(Camera.CFrame.p, lerpedPoint)
	
	local x, y, z = targetCF:ToEulerAnglesXYZ()
	NewAngle = CFrame.Angles(x, y, z)
	OldAngle = NewAngle

	Camera.CFrame = Camera.CFrame:Lerp(CamCFrame*NewAngle, 0.15)
end

I only see two more issues: On the start of the reload, the camera snaps downward from what I assume to be the first line on the second RenderStepped running of the script rather than smoothly originating from the camera’s original CFrame, and at the end of the reload left my camera in a spot further than where I started, which is behavior I’d like to avoid:

local CamCFrame = Camera.CFrame*OldAngle:Inverse()

I tried to lerp it, but the jut downward was still too harsh. Admittedly this probably falls more on me to tweak and fix but I thought I’d share it for clarity.

The other disconcerting issue is the jumpy camera effect on the second reload in the video shared where I reload while moving (full-screen to see it easier). This unfortunately seems a lot less straightforward to fix.

1 Like

Moving the gun will actually give that same effect exact effect, along with bobbing it’ll pretty much be the same as the video you provided.

function cursor.updatePositions(dt, lockModel)
	local mousePosition = UserInputService:GetMouseLocation()
	
	if (lockModel and not cursor.inGUI(mousePosition)) then
		mousePosition = camera:WorldToViewportPoint(lockModel.PrimaryPart and (lockModel.PrimaryPart.Position) or (lockModel:GetPivot().Position))
	end
	
	local position = coreUI.cursorParty.Position:Lerp(UDim2.fromScale(mousePosition.X/camera.ViewportSize.X, mousePosition.Y/camera.ViewportSize.Y), .2 * dt * 60)
	coreUI.darkCursor.Position = position
	coreUI.cursorParty.Position = position
end

this is what i do in my game to make it so that the cursor always stays on an interactable object

what you want to do is put your cursor at a fixed point in front of the player
i.e.

mousePosition = camera:WorldToViewportPoint(lockModel.PrimaryPart and (lockModel.PrimaryPart.Position) or (lockModel:GetPivot().Position))

to

mousePosition = camera:WorldToViewportPoint((camera.CFrame * CFrame.New(0, 0, -10)).Position)

and then i hope you already know to make the camera an animatable object in your game, so you can move it around.

in the above line you can raycast in front of the camera a certain depth and get the cframe position that way.


‘cursor’ must have an anchor point of (0.5, 0.5)

RunService.Heartbeat:Connect(function(dt)
    local origin, direction = camera.CFrame.Position, camera.CFrame.LookVector * 10
	local cast = workspace:Raycast(origin, direction, params)
	local pos;
	if (cast) then
		pos = cast.Position
	else
		pos = origin + direction
	end
	
	local mousePosition = camera:WorldToViewportPoint(pos)
	cursor.Position = cursor.Position:Lerp(UDim2.fromScale(mousePosition.X/camera.ViewportSize.X, mousePosition.Y/camera.ViewportSize.Y), .2 * dt * 60)
end)

this may work

2 Likes

i did it wrong. you need to calculate the lookvector when the camera’s unaffected by the animations.


new idea:

have 3 cameras. one is the animations camera cframe (B), one is where workspace.currentCamera should be (A), and the last is A * B camera cframe, and this is the one players will see.

do the stuff in my last post, except raycast from A with the lookvector of A * 10

1 Like

Caved in and I’m taking a crack at making my own first-person camera. Started a new thread here:

Going to keep this thread open for the time being but I hope these two threads can compliment each other somehow. I’ll close them both when I hopefully reach a solution, thanks for the help thus far everyone :slight_smile:

i think you can raycast from the nozzle of the gun to whats in front of it, and put the ui cursors position where the rays hit is

try this first
it will be more original than copying blood engine and it will make sense unlike in blood engine

1 Like

I’ll likely get around to the crosshair/cursor scripting after I flesh out the custom first-person camera and later the camera manipulation when reloading. Appreciate the help though

Has anyone found a solution to this problem?? because I also need something like this and havent found anything useful.

1 Like

I’ve come to the conclusion that a lot of these games bake the camera movement into their animations in Blender rather than doing something procedural like I had unsuccessfully tried. If you’re good with animation I’d suggest doing it that way.

3 Likes

oh. I’ll try that.

(char limiiiitit)

1 Like

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