Advertisement
CirilXD

Godot 3D First Person Movement + Head bobbing

Jan 27th, 2024
1,340
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
GDScript 11.73 KB | None | 0 0
  1. extends CharacterBody3D
  2.  
  3. enum WalkState{
  4.     NORMAL,
  5.     SPRINT,
  6.     CROUCH,
  7.     PRONE,
  8.     SLIDE
  9. }
  10.  
  11. #movement attribute values
  12. const TARGET_LERP = .8
  13.  
  14. const SPRINT_SPEED = 18.0
  15. const SPRINT_LERP_ACC = 1
  16. const SPRINT_LERP_DEC = 8
  17.  
  18. const WALK_SPEED = 6.0
  19. const WALK_LERP_ACC = 3.5
  20. const WALK_LERP_DEC = 10
  21.  
  22. const CROUCH_SPEED = 3.0
  23. const CROUCH_LERP_ACC = 8
  24. const CROUCH_LERP_DEC = 14
  25.  
  26. const PRONE_SPEED = 1.5
  27. const PRONE_LERP_ACC = 12
  28. const PRONE_LERP_DEC = 22
  29.  
  30. const SLIDE_SPEED = 30
  31. const SLIDE_TIME_MAX = .7
  32. const SLIDE_DAMPEN_RATE = .05
  33. const SLIDE_FLAT_DAMPEN_RATE = .001
  34. const SLOPE_SLIDE_THRESHOLD = .1
  35. var current_slide_time = 0
  36. var current_slide_vector : Vector3 = Vector3.ZERO
  37.  
  38. var SPRINT_CD_MAX = .2
  39. var current_sprint_cd = 0
  40.  
  41. const FALL_SPEED_MAX = 30
  42. const JUMP_VELOCITY = 6
  43. const JUMP_HANG_TIME_THRESHOLD = .3
  44. const JUMP_HANG_TIME_SPEED_MULT = 1.05
  45. const JUMP_HANG_TIME_ACC_MULT = 3
  46.  
  47. const COYOTE_TIME_MAX = .1
  48. var current_coyote_time = 0
  49. const JUMP_CD_MAX = .25
  50. var current_jump_cd = 0
  51.  
  52.  
  53.  
  54. # Get the gravity from the project settings to be synced with RigidBody nodes.
  55. var gravity_default = ProjectSettings.get_setting("physics/3d/default_gravity")
  56. var gravity_falling = 3.3 * gravity_default
  57. var gravity_hang_time = .5 * gravity_default
  58. var current_gravity = gravity_default
  59.  
  60. @export var camera_sensitivity = .25
  61.  
  62. var input_dir = Vector2.ZERO
  63. var direction = Vector3.ZERO
  64.  
  65. @onready var collider = $Collider
  66. const COLLIDER_HEIGHT_NORMAL = 2
  67. const COLLIDER_HEIGHT_CROUCH = 1.1
  68. const COLLIDER_HEIGHT_PRONE = .5
  69.  
  70. const CAMERA_HEIGHT_NORMAL = 1.8
  71. const CAMERA_HEIGHT_CROUCH = .9
  72. const CAMERA_HEIGHT_PRONE = .3
  73.  
  74. const HEAD_BOB_INTENSITY_SPRINT = .15
  75. const HEAD_BOB_INTENSITY_NORMAL = .12
  76. const HEAD_BOB_INTENSITY_CROUCH = .08
  77. const HEAD_BOB_INTENSITY_PRONE = .04
  78.  
  79. const HEAD_BOB_FREQUENCY_SPRINT = 18
  80. const HEAD_BOB_FREQUENCY_NORMAL = 14
  81. const HEAD_BOB_FREQUENCY_CROUCH = 10
  82. const HEAD_BOB_FREQUENCY_PRONE = 8
  83.  
  84. const HEAD_BOB_LERP_RATE = 50
  85.  
  86. var head_bob_var = 0
  87. var is_head_bob_active = true
  88. var current_head_bob_intensity = 0
  89. var current_head_bob_frequency = 0
  90.  
  91. const CAMERA_LERP = 10
  92. const CAMERA_FOV_NORMAL = 70.0
  93. const CAMERA_FOV_MAX_SPEED = 100.0
  94.  
  95. @onready var camera_pivot = $CameraPivot
  96. @onready var debug_label = $DebugLabel
  97. @onready var height_raycast = $HeightRaycast
  98. @onready var camera_3d = $CameraPivot/HeadBobPivot/Camera3D
  99. @onready var head_bob_pivot = $CameraPivot/HeadBobPivot
  100.  
  101. var current_max_speed : float = 0
  102. var current_lerp_acc : float = 0
  103. var current_lerp_dec : float = 0
  104. var current_camera_height : float = 0
  105.  
  106.  
  107. var current_walk_state : WalkState = WalkState.NORMAL
  108.  
  109.  
  110. var floor_angle
  111.  
  112. var debug = null
  113. var debug1 = null
  114. var debug2 = null
  115. var debug3 = null
  116.  
  117. func _ready():
  118.     Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
  119.     _UpdateCollider()
  120.     debug =  get_tree().root.get_node("ROOT/raycasts")
  121.     debug1 = debug.get_node("DebugRaycast")
  122.     debug2 = debug.get_node("DebugRaycast2")
  123.     debug3 = debug.get_node("DebugRaycast3")
  124.    
  125. func _input(event):
  126.     if event is InputEventMouseMotion:
  127.         rotate_y(deg_to_rad(camera_sensitivity * -event.relative.x))
  128.         camera_pivot.rotate_x(deg_to_rad(camera_sensitivity * -event.relative.y))
  129.         camera_pivot.rotation.x = clamp(camera_pivot.rotation.x, deg_to_rad(-89), deg_to_rad(89))
  130.  
  131.  
  132. func _process(delta):
  133.     input_dir = Input.get_vector("left", "right", "forward", "back")
  134.  
  135.     direction = transform.basis * Vector3(input_dir.x, 0, input_dir.y).normalized()
  136.  
  137.     if WalkState.SLIDE != current_walk_state:
  138.         if Input.is_action_pressed("prone") && is_on_floor():
  139.             if current_walk_state != WalkState.PRONE:
  140.                 current_walk_state = WalkState.PRONE
  141.                 _UpdateCollider()
  142.         elif Input.is_action_pressed("crouch") and !height_raycast.is_colliding() && is_on_floor():
  143.             if current_walk_state != WalkState.CROUCH:
  144.                 if current_sprint_cd > 0:
  145.                     current_walk_state = WalkState.SLIDE
  146.                     current_slide_time = SLIDE_TIME_MAX
  147.                     current_slide_vector = abs(velocity) * direction
  148.                     current_slide_vector.y = 0
  149.                     _UpdateCollider()
  150.                 else:
  151.                     current_walk_state = WalkState.CROUCH
  152.                     _UpdateCollider()
  153.         elif !height_raycast.is_colliding():
  154.             if current_walk_state == WalkState.PRONE:
  155.                 current_walk_state = WalkState.CROUCH
  156.                 _UpdateCollider()
  157.             elif Input.is_action_pressed("sprint"):
  158.                 current_sprint_cd = SPRINT_CD_MAX
  159.                 if current_walk_state != WalkState.SPRINT:
  160.                     current_walk_state = WalkState.SPRINT
  161.                     _UpdateCollider()
  162.             elif current_walk_state != WalkState.NORMAL:
  163.                 current_walk_state = WalkState.NORMAL
  164.                 _UpdateCollider()
  165.                
  166.     floor_angle = get_floor_angle()
  167.  
  168.  
  169. func _physics_process(delta):
  170.     if WalkState.SLIDE == current_walk_state:
  171.         if current_slide_time > 0:
  172.             if floor_angle < SLOPE_SLIDE_THRESHOLD || velocity.y > 0:
  173.                 current_slide_time -= delta
  174.                 current_slide_time = clamp(current_slide_time, 0, SLIDE_TIME_MAX)
  175.            
  176.         else:
  177.             current_walk_state = WalkState.CROUCH
  178.             _UpdateCollider()
  179.    
  180.     if current_sprint_cd > 0:
  181.         current_sprint_cd -= delta
  182.  
  183.     #calc target speed based on current input
  184.     var target_speed : Vector3 = direction * current_max_speed
  185.  
  186.     var current_acc_rate : Vector3 = Vector3.ZERO
  187.     if input_dir:
  188.         current_acc_rate = Vector3(current_lerp_acc,
  189.                 0,
  190.                 current_lerp_acc)
  191.     else:
  192.         current_acc_rate = Vector3(current_lerp_dec,
  193.             0,
  194.             current_lerp_dec)
  195.    
  196.    
  197.     #lerp the target speed for smoother change
  198.     #if the movement is in the same direction make the target closer to the current velocity
  199.     #otherwise, since direction shift is required make the target closer to actual target
  200.     #if player is faster than top speed don't slow down on X and Z axis
  201.     if (target_speed.x != 0 &&
  202.         abs(velocity.x) >= abs(target_speed.x) &&
  203.         sign(velocity.x) == sign(target_speed.x)):
  204.            
  205.         target_speed.x = lerp(velocity.x, target_speed.x, 1-TARGET_LERP)
  206.     else:
  207.         target_speed.x = lerp(velocity.x, target_speed.x, TARGET_LERP)
  208.        
  209.     if (target_speed.z != 0 &&
  210.         abs(velocity.z) >= abs(target_speed.z) &&
  211.         sign(velocity.z) == sign(target_speed.z)):
  212.  
  213.         target_speed.z = lerp(velocity.z, target_speed.z, 1-TARGET_LERP)
  214.     else:
  215.         target_speed.z = lerp(velocity.z, target_speed.z, TARGET_LERP)
  216.    
  217.     # Handle Jump.
  218.     if Input.is_action_just_released("jump") and velocity.y > 0:
  219.         current_jump_cd = 0
  220.         velocity.y = velocity.y / 2.0
  221.     elif current_coyote_time > 0 && (
  222.         Input.is_action_just_pressed("jump") || current_jump_cd > 0):
  223.         current_jump_cd = JUMP_CD_MAX
  224.         velocity.y = JUMP_VELOCITY
  225.         if Input.is_action_pressed("sprint"): current_walk_state = WalkState.SPRINT
  226.         else: current_walk_state = WalkState.NORMAL
  227.         _UpdateCollider()
  228.        
  229.     if abs(velocity.y) < JUMP_HANG_TIME_THRESHOLD && !is_on_floor():
  230.         #make the gravity weaker around apex of jump
  231.         current_gravity = gravity_hang_time
  232.        
  233.         #increase responsiveness
  234.         target_speed *= JUMP_HANG_TIME_SPEED_MULT
  235.         current_acc_rate *= JUMP_HANG_TIME_ACC_MULT
  236.     else:
  237.         #if falling make gravity stronger
  238.         if velocity.y < 0:
  239.             current_gravity = gravity_falling
  240.         else:
  241.             current_gravity = gravity_default
  242.    
  243.     # Add the gravity.
  244.     if not is_on_floor():
  245.         current_coyote_time -= delta
  246.         if current_coyote_time < 0: current_coyote_time = 0
  247.         velocity.y -= current_gravity * delta
  248.     else:
  249.         current_coyote_time = COYOTE_TIME_MAX
  250.  
  251.    
  252.     #calculate dif between max and current speed
  253.     #ignore y axis
  254.     var speed_difference : Vector3 = target_speed - velocity
  255.     speed_difference.y = 0
  256.  
  257.     #final force that will be applied to character
  258.     var movement = speed_difference * current_acc_rate
  259.  
  260.     if is_on_floor() && floor_angle > SLOPE_SLIDE_THRESHOLD:
  261.         var plane = Plane(get_floor_normal())
  262.         movement = plane.project(movement)
  263.         current_slide_vector = plane.project(current_slide_vector)
  264.  
  265.     if WalkState.SLIDE == current_walk_state:
  266.         velocity = velocity + (movement) * delta * SLIDE_DAMPEN_RATE
  267.         velocity = velocity + current_slide_vector * delta * (current_slide_time) * (-(current_slide_vector.y) + .01)
  268.     else:
  269.         velocity = velocity + (movement) * delta
  270.  
  271.     if velocity.y < -FALL_SPEED_MAX:
  272.         velocity.y = -FALL_SPEED_MAX
  273.        
  274.     current_jump_cd -= delta
  275.     if current_jump_cd < 0: current_jump_cd = 0
  276.    
  277.     debug_label.text = str(get_floor_angle()) + " rad\n" + str(velocity.length()) + "\n" + (
  278.         "Vertical:" + str(velocity.y))
  279.    
  280.     _UpdateCameraPosition(delta, inverse_lerp(0, abs(SPRINT_SPEED), velocity.length()))
  281.     move_and_slide()
  282.  
  283. func _UpdateCameraPosition(delta, speed_t):
  284.     var t = CAMERA_LERP * delta
  285.  
  286.     if WalkState.SLIDE == current_walk_state:
  287.         camera_3d.rotation.z = lerp(camera_3d.rotation.z, deg_to_rad(15.0), t)
  288.     else:
  289.         camera_3d.rotation.z = lerp(camera_3d.rotation.z, 0.0, t)
  290.        
  291.     if is_head_bob_active && WalkState.SLIDE != current_walk_state && is_on_floor() && direction != Vector3.ZERO:
  292.         head_bob_var += current_head_bob_frequency * delta
  293.         head_bob_pivot.position.y = lerp(
  294.             head_bob_pivot.position.y,
  295.             sin(head_bob_var) * current_head_bob_intensity / 2.0,
  296.             t)
  297.         head_bob_pivot.position.x = lerp(
  298.             head_bob_pivot.position.x,
  299.             cos(head_bob_var / 2.0) * current_head_bob_intensity,
  300.             t)
  301.     else:
  302.         head_bob_pivot.position.y = lerp(
  303.             head_bob_pivot.position.y,
  304.             0.0,
  305.             t)
  306.         head_bob_pivot.position.x = lerp(
  307.             head_bob_pivot.position.x,
  308.             0.0,
  309.             t)
  310.        
  311.     var tmp = lerp(CAMERA_FOV_NORMAL, CAMERA_FOV_MAX_SPEED, speed_t)
  312.     camera_3d.fov = lerp(camera_3d.fov, min(tmp, CAMERA_FOV_MAX_SPEED), t)
  313.     camera_pivot.position.y = lerp(camera_pivot.position.y, current_camera_height, t)
  314.  
  315. func _UpdateCollider():
  316.     match current_walk_state:
  317.         WalkState.NORMAL:
  318.             collider.shape.height = COLLIDER_HEIGHT_NORMAL
  319.             collider.position.y = COLLIDER_HEIGHT_NORMAL / 2.0
  320.             height_raycast.target_position.y = COLLIDER_HEIGHT_NORMAL
  321.             current_camera_height = CAMERA_HEIGHT_NORMAL
  322.             current_max_speed = WALK_SPEED
  323.             current_head_bob_frequency = HEAD_BOB_FREQUENCY_NORMAL
  324.             current_head_bob_intensity = HEAD_BOB_INTENSITY_NORMAL
  325.             current_lerp_acc = WALK_LERP_ACC
  326.             current_lerp_dec = WALK_LERP_DEC
  327.         WalkState.CROUCH:  
  328.             collider.shape.height = COLLIDER_HEIGHT_CROUCH
  329.             collider.position.y = COLLIDER_HEIGHT_CROUCH / 2.0
  330.             height_raycast.target_position.y = COLLIDER_HEIGHT_NORMAL
  331.             current_camera_height = CAMERA_HEIGHT_CROUCH
  332.             current_max_speed = CROUCH_SPEED
  333.             current_head_bob_frequency = HEAD_BOB_FREQUENCY_CROUCH
  334.             current_head_bob_intensity = HEAD_BOB_INTENSITY_CROUCH
  335.             current_lerp_acc = CROUCH_LERP_ACC
  336.             current_lerp_dec = CROUCH_LERP_DEC
  337.         WalkState.SLIDE:   
  338.             collider.shape.height = COLLIDER_HEIGHT_CROUCH
  339.             collider.position.y = COLLIDER_HEIGHT_CROUCH / 2.0
  340.             height_raycast.target_position.y = COLLIDER_HEIGHT_NORMAL
  341.             current_camera_height = CAMERA_HEIGHT_CROUCH
  342.             current_max_speed = WALK_SPEED
  343.             current_head_bob_frequency = HEAD_BOB_FREQUENCY_CROUCH
  344.             current_head_bob_intensity = HEAD_BOB_INTENSITY_CROUCH
  345.             current_lerp_acc = CROUCH_LERP_ACC
  346.             current_lerp_dec = CROUCH_LERP_DEC
  347.         WalkState.PRONE:   
  348.             collider.shape.height = COLLIDER_HEIGHT_PRONE
  349.             collider.position.y = COLLIDER_HEIGHT_PRONE / 2.0
  350.             height_raycast.target_position.y = COLLIDER_HEIGHT_CROUCH
  351.             current_camera_height = CAMERA_HEIGHT_PRONE
  352.             current_max_speed = PRONE_SPEED
  353.             current_head_bob_frequency = HEAD_BOB_FREQUENCY_PRONE
  354.             current_head_bob_intensity = HEAD_BOB_INTENSITY_PRONE
  355.             current_lerp_acc = PRONE_LERP_ACC
  356.             current_lerp_dec = PRONE_LERP_DEC
  357.         WalkState.SPRINT:  
  358.             collider.shape.height = COLLIDER_HEIGHT_NORMAL
  359.             collider.position.y = COLLIDER_HEIGHT_NORMAL / 2.0
  360.             height_raycast.target_position.y = COLLIDER_HEIGHT_NORMAL
  361.             current_camera_height = CAMERA_HEIGHT_NORMAL
  362.             current_max_speed = SPRINT_SPEED
  363.             current_head_bob_frequency = HEAD_BOB_FREQUENCY_SPRINT
  364.             current_head_bob_intensity = HEAD_BOB_INTENSITY_SPRINT
  365.             current_lerp_acc = SPRINT_LERP_ACC
  366.             current_lerp_dec = SPRINT_LERP_DEC
  367.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement