'How to make FPS crawl?
I'm using Unity New Input System for my FPS game and I have figured out the movement and the mouselook. However, I'm trying to add a crawling feature to my moving script. I don't want the player to be holding down a button for crawling, it would be better to click a button to change the status from walking to crawling and then just move accordingly. and of course, the mouselook will differ in every state. How would I achieve that with the minimal code changes?
Movement class:
public class Movement : MonoBehaviour
{
[SerializeField] CharacterController controller;
[SerializeField] float walkSpeed = 10f;
[SerializeField] float runSpeed = 35f;
float moveSpeed;
Vector2 horizontalInput;
[SerializeField] float jumpHeight = 3.5f;
bool jump, running;
[SerializeField] float gravity = -30f; //-9.81
Vector3 verticalVelocity = Vector3.zero;
[SerializeField] LayerMask groundMask;
bool isGrounded;
Vector3 horizontalVelocity;
private void Update()
{
isGrounded = Physics.CheckSphere(transform.position, 0.1f, groundMask);
if (isGrounded)
{
verticalVelocity.y = 0;
}
// Jump: v = sqrt(-2 * jumpheight* gravity)
if (jump)
{
if (isGrounded)
{
verticalVelocity.y = Mathf.Sqrt(-2f * jumpHeight * gravity);
}
jump = false;
}
verticalVelocity.y += gravity * Time.deltaTime;
controller.Move(verticalVelocity * Time.deltaTime);
if (running)
{
moveSpeed = runSpeed;
horizontalVelocity = (transform.right * horizontalInput.x + transform.forward * horizontalInput.y) * moveSpeed;
controller.Move(horizontalVelocity * Time.deltaTime);
}
else
{
moveSpeed = walkSpeed;
horizontalVelocity = (transform.right * horizontalInput.x + transform.forward * horizontalInput.y) * moveSpeed;
controller.Move(horizontalVelocity * Time.deltaTime);
}
}
public void ReceiveInput(Vector2 _horizontalInput)
{
horizontalInput = _horizontalInput;
}
public void OnJumpPressed()
{
jump = true;
}
public void OnRunningPressed() {
running = true;
}
}
MouseLook class:
public class MouseLook : MonoBehaviour
{
[SerializeField] float sensitivityX = 8f;
[SerializeField] float sensitivityY = 0.5f;
float mouseX, mouseY;
[SerializeField] Transform playerCamera;
[SerializeField] float xClamp = 75f;
float xRotataion = 0f;
private void Update()
{
transform.Rotate(Vector3.up, mouseX * Time.fixedDeltaTime);
xRotataion -= mouseY;
xRotataion = Mathf.Clamp(xRotataion, -xClamp, xClamp);
Vector3 targetRotation = transform.eulerAngles;
targetRotation.x = xRotataion;
playerCamera.eulerAngles = targetRotation;
}
public void ReceiveInput(Vector2 mouseInput)
{
mouseX = mouseInput.x * sensitivityX;
mouseY = mouseInput.y * sensitivityY;
}
}
InputManager class:
public class InputManager : MonoBehaviour
{
[SerializeField] Movement movement;
[SerializeField] PlayerInteractions playerInteractions;
[SerializeField] MouseLook mouseLook;
PlayerControls controls;
PlayerControls.GroundMovementActions groundMovement;
Vector2 horizontalInput;
Vector2 mouseInput;
private void Awake()
{
controls = new PlayerControls();
groundMovement = controls.GroundMovement;
// groundMovement.[action].performed += context => do something
groundMovement.HorizontalMovement.performed += ctx => horizontalInput = ctx.ReadValue<Vector2>();
groundMovement.Jump.performed += _ => movement.OnJumpPressed();
groundMovement.Running.performed += _ => movement.OnRunningPressed();
groundMovement.PickingUp.performed += _ => playerInteractions.OnPickingUp();
groundMovement.MouseX.performed += ctx => mouseInput.x = ctx.ReadValue<float>();
groundMovement.MouseY.performed += ctx => mouseInput.y = ctx.ReadValue<float>();
groundMovement.Open.performed += _ => playerInteractions.OnOpenPressed();
}
private void Update()
{
movement.ReceiveInput(horizontalInput);
mouseLook.ReceiveInput(mouseInput);
}
private void OnEnable()
{
controls.Enable();
}
private void OnDestroy()
{
controls.Disable();
}
}
I failed to find anything like this online and I don't know how to achieve it using the new input system but here is my attempt:
public class crawlScript : MonoBehaviour
{
CapsuleCollider playerCol;
float originalHeight;
public float reducedHeight;
bool crawl = false;
void Start()
{
playerCol= GetComponent<CapsuleCollider>();
originalHeight= playerCol.height;
}
// Update is called once per frame
void Update()
{
if (crawl)
{
OnCrouch();
}
else if (!crawl)
{
GoUp();
}
}
public void OnCrouch()
{
playerCol.height= reducedHeight;
}
void GoUp()
{
playerCol.height = originalHeight;
}
public void OnCrawl()
{
crawl= true;
}
And added this to the InputManager class:
groundMovement.Crawl.performed += _ => crawlscript.OnCrawl();
Solution 1:[1]
You will need to
- Create a public boolean variable for whether the player is crawling or not, because this will almost certainly be needed in the future by this script or other scripts.
- Create a crawling animation
- Create two functions for enabling and disabling crawl, that will toggle the animation, change the players walk speed, and do other things that you may want to add (like toggle the visibility of any objects in the player's hands until they get back up).
The code should look something like this:
public String crawlkey = "left shift";
public bool iscrawling = false;
// put in Update()
if(!crawling){
if(Input.GetKeyDown(crawlkey)){
iscrawling = true;
handleCrawlStart();
}
} else if(Input.GetKeyDown(crawlkey)){
iscrawling = false;
handleCrawlEnd;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Lee Taylor |

