What would be the best way to force a First-Person perspective alongside Motor6 modification?

TL;DR - I am trying to make the player’s camera match their head’s position, while still maintaining mouse-controls for looking around. The Head and UpperTorso’s Motor6s also rotate with the camera.

I do not know exactly what to search for help, and the few articles I have found did not prove to answer my problem. My game is a first-person shooter of sorts, and I have created a crouch/sneak system which plays animations where the player is closer to the ground. Being that the camera stays where the head normally is on a character, it becomes disjointed to the actual body and looks very strange. I have read the thread here about FPS games, but at this point I am too inexperienced to really understand what I’ve read there. Separating what the player sees with viewmodels and worldmodels is way beyond me, so what I do, is use normal Roblox Tool instances, force first person, and change the LocalTransparencyModifiers of the player’s body so that they can see themself. This causes issues with the sneaking though, as the camera stays still when the character drops to the ground.

One attempt to fix this was setting the CurrentCamera’s CameraSubject to the player’s head. This works for the most part, until you turn around; your body does not turn with you, and will always face whichever way you started at.
Another way I tried was to set the Camera to Scripted and, each frame, set it to the Head’s CFrame. I think this would have worked, if it were not for the one thing that I did get out of that FPS thread. That would be the modification of the character’s Motor6s. I have another script change the Motor6’s in the player’s UpperTorso and Head to rotate alongside the camera. That way, other players can visibly see when someone is looking up and down, rather than always pointing straight ahead. The thing is though, if I set the Camera’s CFrame to the Head, that Motor6 script will constantly push the camera up or down, whichever one is closer. I think this would work fine if I disabled the Motor6-changing-script, but then it would no longer be clear when player looks up or down.

What kind of alternatives to making viewmodels are there for an FPS-style game, that a relatively new developer like me could reasonably accomplish?

5 Likes

The quick (and very easy) fix to this is going into the character’s humanoid and tweening its CameraOffset property while the animation is playing.
e.g, in your Animate script (or something like that):

local TweenService = game:GetService("TweenService")

local CrouchTween = TweenService:Create(
	--[[ player's humanoid ]],
	TweenInfo.new(
		1, -- time
		Enum.EasingStyle.Quad,
		Enum.EasingDirection.InOut,
		0, -- repeats, negative = infinity
		false, -- reverses
		0 -- delay
	),
	{CameraOffset = Vector3.new(0,-3,-2)}
)

local UncrouchTween = TweenService:Create(
	--[[ player's humanoid ]],
	TweenInfo.new(
		1,
		Enum.EasingStyle.Quad,
		Enum.EasingDirection.InOut,
		0,
		false,
		0
	),
	{CameraOffset = Vector3.new(0,0,0)}
)

-- whenever CrouchAnimation:Play is called
CrouchTween:Play()

-- whenever UncrouchAnimation:Play is called
UncrouchTween:Play()

The only thing you would have to watch out for is making sure you redefine the humanoid tweens every time the player’s character respawns, or else the tween will be operating on a non-existent humanoid from the previous character.

4 Likes

Alrighty, thanks! There’s now a much smaller issue that I will have to research a bit, but the big glaring issue of the disjointed camera should be fixed.

2 Likes

Did a bit of experimenting with that CameraOffset property, and I think I’ve made an improvement that I will post here incase any future googlers want another method. Each frame, a localscript gets the current position of the head and takes its difference from the original position, and sets the CameraOffset to this. Here is the code for anyone looking for another method of doing this.

local RenderService = game:GetService("RunService")
local Character = game.Players.LocalPlayer.Character
RenderService.RenderStepped:Connect(function()
    --Each frame
	local NormalPos = Character:WaitForChild("HumanoidRootPart").Position + Vector3.new(0, 1.4630003, 0)
    --Get the head's original position. Since the HRP does not move at all, I use the distance between a normal, unmoving animation rig.
	local NewPos = Character:WaitForChild("Head").Position - NormalPos
    --Find the new, disjointed position of the head
	local NewY = NewPos.Y
	local NewX = NewPos.X
	if NewY > 0 then NewY = NewY * -1 end
	if NewX > 0 then NewX = NewX * -1 end
	NewPos = Vector3.new(0, NewY, NewX)
    --Fiddle with the different axis	
	
	Character:WaitForChild("Humanoid").CameraOffset = NewPos
    --Set it
end)

Edit - Fixed a minor issue with the code

6 Likes

Its baffling how similar our experiences were. Believe me when I say this, I went through everything that was said on this post. Went as far as trying to tween the HRP to the CFrame every frame and failed. I even made a post on it and no one pointed at the right way. I think every beginner would go through the same as well. To think this post was just sitting here! Very surreal. I found the post by typing the one obvious thing I didn’t think of typing. “Roblox First Person Crouch”. I apologize if I revived this topic, but I just wanted to let people know that I am extremely grateful to everyone in this thread! I hope you are successful with your endeavors. I wish someone pointed me to this post earlier. And finally I wish like-minded beginner scripters find this post in their time of need in the future! Good day!

2 Likes