Advertisement
Guest User

godot-simple-fps-player

a guest
Dec 4th, 2016
517
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.69 KB | None | 0 0
  1. extends KinematicBody
  2.  
  3. var _is_loaded = false
  4. var velocity=Vector3()
  5. var yaw = 0
  6. var pitch = 0
  7. var is_moving=false
  8. var on_floor=false
  9. var jump_timeout=0
  10. var step_timeout=0
  11. var fly_mode=false
  12. var alive=true
  13. var is_jumping=false
  14. var is_running=false
  15. var is_crouching=false
  16. var body_position="stand"
  17. var jump_strength=9
  18. var node_camera=null
  19. var node_leg=null
  20. var node_yaw=null
  21. var node_body=null
  22. var node_head_check=null
  23. var node_action_ray=null
  24. var node_ray = null
  25. var node_step_ray=null
  26. var aim_offset=Vector3(0,1.5,0)
  27.  
  28. const GRAVITY_FACTOR=3
  29.  
  30. # params
  31. export(bool) var active=true setget _set_active
  32. export(float) var walk_speed=15
  33. export(float) var run_factor=3
  34. export(float) var crouch_factor=0.2
  35.  
  36. ## dimensions
  37. export(float) var leg_length=0.4 setget _set_leg_length
  38. export(float) var body_radius=0.6 setget _set_body_radius
  39. export(float) var body_height=0.8 setget _set_body_height
  40. export(float) var camera_height=1.7 setget _set_camera_height
  41. export(float) var action_range=2 setget _set_action_range
  42.  
  43. ## actions
  44. export(String) var action_forward="ui_up"
  45. export(String) var action_backward="ui_down"
  46. export(String) var action_left="ui_left"
  47. export(String) var action_right="ui_right"
  48. export(String) var action_action1="ui_action1"
  49. export(String) var action_jump="ui_jump"
  50. export(String) var action_use="ui_select"
  51.  
  52. ## physics
  53. export(float) var ACCEL= 2
  54. export(float) var DEACCEL= 4
  55. export(float) var FLY_SPEED=100
  56. export(float) var FLY_ACCEL=4
  57. export(float) var GRAVITY=-9.8
  58. export(float) var MAX_JUMP_TIMEOUT=0.2
  59. export(float) var MAX_ATTACK_TIMEOUT=0.2
  60. export(int) var MAX_SLOPE_ANGLE = 40
  61. export(float) var STAIR_RAYCAST_HEIGHT=0.75
  62. export(float) var STAIR_RAYCAST_DISTANCE=0.58
  63. export(float) var STAIR_JUMP_SPEED=5
  64. export(float) var STAIR_JUMP_TIMEOUT=0.1
  65. export(float) var footstep_factor=0.004
  66. export(float) var view_sensitivity = 0.3
  67.  
  68. #################################################################################3
  69.  
  70. # initializer
  71. func _ready():
  72.     _build_structure()
  73.     _is_loaded=true
  74.     _update_params()
  75.     node_action_ray.add_exception(self)
  76.     set_fixed_process(true)
  77.     set_process_input(true)
  78.  
  79. func _build_structure():
  80.     # collision
  81.     var body=CollisionShape.new()
  82.     body.set_transform(Transform(Vector3(1,0,0),Vector3(0,0,-1),Vector3(0,1,0),Vector3(0,1.4,0)))
  83.     var body_shape=CapsuleShape.new()
  84.     body_shape.set_radius(0.6)
  85.     body_shape.set_height(0.8)
  86.     body.set_shape(body_shape)
  87.     add_child(body)
  88.    
  89.     var leg=CollisionShape.new()
  90.     leg.set_transform(Transform(Vector3(1,0,0),Vector3(0,1,0),Vector3(0,0,1),Vector3(0,0,1)))
  91.     var leg_shape=RayShape.new()
  92.     leg_shape.set_length(0.4)
  93.     leg.set_shape(leg_shape)
  94.     body.add_child(leg)
  95.    
  96.     # camera
  97.     var yaw=Spatial.new()
  98.     add_child(yaw)
  99.    
  100.     var camera=Camera.new()
  101.     camera.set_translation(Vector3(0,1.7,0))
  102.     yaw.add_child(camera)
  103.    
  104.     var actionRay=RayCast.new()
  105.     actionRay.set_enabled(true)
  106.     actionRay.set_cast_to(Vector3(0,0,-2))
  107.     camera.add_child(actionRay)
  108.    
  109.     # walking rays
  110.     var wallRay=RayCast.new()
  111.     wallRay.set_enabled(true)
  112.     wallRay.set_cast_to(Vector3(0,-0.8,0))
  113.     wallRay.set_translation(Vector3(0,0.4,0))
  114.     add_child(wallRay)
  115.    
  116.     var stepRay=RayCast.new()
  117.     stepRay.set_enabled(true)
  118.     stepRay.set_cast_to(Vector3(0,-0.5,0))
  119.     stepRay.set_translation(Vector3(0,0.75,-0.58))
  120.     add_child(stepRay)
  121.    
  122.     # head collision check (for crouching)
  123.     var head_area=Area.new()
  124.     head_area.set_enable_monitoring(false)
  125.     head_area.set_monitorable(false)
  126.     head_area.set_translation(Vector3(0,2.2186,0))
  127.     add_child(head_area)
  128.    
  129.     var head_col_shape=CollisionShape.new()
  130.     var head_shape=SphereShape.new()
  131.     head_shape.set_radius(0.2)
  132.     head_col_shape.set_shape(head_shape)
  133.     head_area.add_child(head_col_shape)
  134.    
  135.     # make links
  136.     node_camera=camera
  137.     node_leg=leg
  138.     node_yaw=yaw
  139.     node_body=body
  140.     node_head_check=head_area
  141.     node_action_ray=actionRay
  142.     node_ray=wallRay
  143.     node_step_ray=stepRay
  144.  
  145. func _update_params():
  146.     _set_active(active)
  147.     _set_leg_length(leg_length)
  148.     _set_body_radius(body_radius)
  149.     _set_body_height(body_height)
  150.     _set_camera_height(camera_height)
  151.     _set_action_range(action_range)
  152.  
  153. func _embed_children():
  154.     var children=self.get_children()
  155.     for child in children:
  156.         if child.get_name().left(4)=="eco_":
  157.             continue
  158.         else:
  159.             remove_child(child)
  160.             node_camera.add_child(child)
  161.  
  162. # Keys and mouse handler
  163. func _input(ie):
  164.     if not active:
  165.         return
  166.    
  167.     if ie.type == InputEvent.MOUSE_MOTION:
  168.         yaw = fmod(yaw - ie.relative_x * view_sensitivity, 360)
  169.         pitch = max(min(pitch - ie.relative_y * view_sensitivity, 90), -90)
  170.         node_yaw.set_rotation(Vector3(0, deg2rad(yaw), 0))
  171.         node_camera.set_rotation(Vector3(deg2rad(pitch), 0, 0))
  172.    
  173.     if ie.type == InputEvent.KEY:
  174.         if Input.is_action_pressed(action_use):
  175.             var ray=node_action_ray
  176.             if ray.is_colliding():
  177.                 var obj=ray.get_collider()
  178.                 if obj.has_method("use"):
  179.                     obj.use(self)
  180.         if Input.is_action_pressed("ui_toggle_crouch"):
  181.             if body_position=="crouch":
  182.                 stand()
  183.                 body_position="stand"
  184.             else:
  185.                 crouch()
  186.                 body_position="crouch"
  187.  
  188. # main loop
  189. func _fixed_process(delta):
  190.    
  191.     if fly_mode:
  192.         _fly(delta)
  193.     else:
  194.         _walk(delta)
  195.  
  196. func _enter_tree():
  197.     if active:
  198.         Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
  199.  
  200. func _exit_tree():
  201.     Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
  202.  
  203. func _fly(delta):
  204.     # read the rotation of the camera
  205.     var aim = node_camera.get_global_transform().basis
  206.     # calculate the direction where the player want to move
  207.     var direction = Vector3()
  208.     if Input.is_action_pressed(action_forward):
  209.         direction -= aim[2]
  210.     if Input.is_action_pressed(action_backward):
  211.         direction += aim[2]
  212.     if Input.is_action_pressed(action_left):
  213.         direction -= aim[0]
  214.     if Input.is_action_pressed(action_right):
  215.         direction += aim[0]
  216.    
  217.     direction = direction.normalized()
  218.        
  219.     # calculate the target where the player want to move
  220.     var target=direction*FLY_SPEED
  221.    
  222.     # calculate the velocity to move the player toward the target
  223.     velocity=Vector3().linear_interpolate(target,FLY_ACCEL*delta)
  224.    
  225.     # move the node
  226.     var motion=velocity*delta
  227.     motion=move(motion)
  228.    
  229.     # slide until it doesn't need to slide anymore, or after n times
  230.     var original_vel=velocity
  231.     var attempts=4 # number of attempts to slide the node
  232.    
  233.     while(attempts and is_colliding()):
  234.         var n=get_collision_normal()
  235.         motion=n.slide(motion)
  236.         velocity=n.slide(velocity)
  237.         # check that the resulting velocity is not opposite to the original velocity, which would mean moving backward.
  238.         if(original_vel.dot(velocity)>0):
  239.             motion=move(motion)
  240.             if (motion.length()<0.001):
  241.                 break
  242.         attempts-=1
  243.  
  244. func _walk(delta):
  245.    
  246.     # process timers
  247.     if jump_timeout>0:
  248.         jump_timeout-=delta
  249.    
  250.     # read the rotation of the camera
  251.     var aim = node_camera.get_global_transform().basis
  252.     # calculate the direction where the player want to move
  253.     var direction = Vector3()
  254.     if Input.is_action_pressed(action_forward):
  255.         direction -= aim[2]
  256.     if Input.is_action_pressed(action_backward):
  257.         direction += aim[2]
  258.     if Input.is_action_pressed(action_left):
  259.         direction -= aim[0]
  260.     if Input.is_action_pressed(action_right):
  261.         direction += aim[0]
  262.    
  263.     is_running=Input.is_action_pressed("ui_run")
  264.    
  265.     var crouch_mode=body_position=="crouch"
  266.     var is_crouch_pressed=Input.is_action_pressed("ui_crouch")
  267.  
  268.     if is_crouching and (crouch_mode==is_crouch_pressed):
  269.         stand()
  270.     elif not is_crouching and (crouch_mode!=is_crouch_pressed):
  271.         crouch()
  272.    
  273.     #reset the flag for actor's movement state
  274.     is_moving=(direction.length()>0)
  275.    
  276.     direction.y=0
  277.     direction = direction.normalized()
  278.    
  279.     # clamp to ground if not jumping. Check only the first time a collision is detected (landing from a fall)
  280.     var is_ray_colliding=node_ray.is_colliding()
  281.     if !on_floor and jump_timeout<=0 and is_ray_colliding:
  282.         set_translation(node_ray.get_collision_point())
  283.         on_floor=true
  284.     elif on_floor and not is_ray_colliding:
  285.         # check that flag on_floor still reflects the state of the ray.
  286.         on_floor=false
  287.    
  288.     if on_floor:
  289.         if step_timeout<=0:
  290.             step_timeout=1
  291.         else:
  292.             step_timeout-=velocity.length()*footstep_factor
  293.        
  294.         # if on floor move along the floor. To do so, we calculate the velocity perpendicular to the normal of the floor.
  295.         var n=node_ray.get_collision_normal()
  296.         velocity=velocity-velocity.dot(n)*n
  297.        
  298.         # if the character is in front of a stair, and if the step is flat enough, jump to the step.
  299.         if is_moving and node_step_ray.is_colliding():
  300.             var step_normal=node_step_ray.get_collision_normal()
  301.             if (rad2deg(acos(step_normal.dot(Vector3(0,1,0))))< MAX_SLOPE_ANGLE):
  302.                 velocity.y=STAIR_JUMP_SPEED
  303.                 jump_timeout=STAIR_JUMP_TIMEOUT
  304.        
  305.         # apply gravity if on a slope too steep
  306.         if (rad2deg(acos(n.dot(Vector3(0,1,0))))> MAX_SLOPE_ANGLE):
  307.             velocity.y+=delta*GRAVITY*GRAVITY_FACTOR
  308.     else:
  309.         # apply gravity if falling
  310.         velocity.y+=delta*GRAVITY*GRAVITY_FACTOR
  311.    
  312.     # calculate the target where the player want to move
  313.     var target=direction*get_walk_speed(is_crouching,is_running)
  314.     # if the character is moving, he must accelerate. Otherwise he deccelerates.
  315.     var accel=DEACCEL
  316.     if is_moving:
  317.         accel=ACCEL
  318.    
  319.     # calculate velocity's change
  320.     var hvel=velocity
  321.     hvel.y=0
  322.    
  323.     # calculate the velocity to move toward the target, but only on the horizontal plane XZ
  324.     hvel=hvel.linear_interpolate(target,accel*delta)
  325.     velocity.x=hvel.x
  326.     velocity.z=hvel.z
  327.    
  328.     # move the node
  329.     var motion=velocity*delta
  330.     motion=move(motion)
  331.    
  332.     # slide until it doesn't need to slide anymore, or after n times
  333.     var original_vel=velocity
  334.     if(motion.length()>0 and is_colliding()):
  335.         var n=get_collision_normal()
  336.         motion=n.slide(motion)
  337.         velocity=n.slide(velocity)
  338.         # check that the resulting velocity is not opposite to the original velocity, which would mean moving backward.
  339.         if(original_vel.dot(velocity)>0):
  340.             motion=move(motion)
  341.    
  342.     if on_floor:
  343.         # move with floor but don't change the velocity.
  344.         var floor_velocity=_get_floor_velocity(node_ray,delta)
  345.         if floor_velocity.length()!=0:
  346.             move(floor_velocity*delta)
  347.    
  348.         # jump
  349.         if Input.is_action_pressed(action_jump) and body_position=="stand":
  350.             velocity.y=jump_strength
  351.             jump_timeout=MAX_JUMP_TIMEOUT
  352.             on_floor=false
  353.    
  354.     # update the position of the raycast for stairs to where the character is trying to go, so it will cast the ray at the next loop.
  355.     if is_moving:
  356.         var sensor_position=Vector3(direction.z,0,-direction.x)*STAIR_RAYCAST_DISTANCE
  357.         sensor_position.y=STAIR_RAYCAST_HEIGHT
  358.         node_step_ray.set_translation(sensor_position)
  359.  
  360. func _get_floor_velocity(ray,delta):
  361.     var floor_velocity=Vector3()
  362.     # only static or rigid bodies are considered as floor. If the character is on top of another character, he can be ignored.
  363.     var object = ray.get_collider()
  364.     if object extends RigidBody or object extends StaticBody:
  365.         var floor_angular_vel = Vector3()
  366.         # get the floor velocity and rotation depending on the kind of floor
  367.         if object extends RigidBody:
  368.             floor_velocity = object.get_linear_velocity()
  369.             floor_angular_vel = object.get_angular_velocity()
  370.         elif object extends StaticBody:
  371.             floor_velocity = object.get_constant_linear_velocity()
  372.             floor_angular_vel = object.get_constant_angular_velocity()
  373.         # if there's an angular velocity, the floor velocity take it in account too.
  374.         if(floor_angular_vel.length()>0):
  375.             var transform = Matrix3(Vector3(1, 0, 0), floor_angular_vel.x)
  376.             transform = transform.rotated(Vector3(0, 1, 0), floor_angular_vel.y)
  377.             transform = transform.rotated(Vector3(0, 0, 1), floor_angular_vel.z)
  378.             var point = ray.get_collision_point() - object.get_translation()
  379.             floor_velocity += transform.xform_inv(point) - point
  380.            
  381.             # if the floor has an angular velocity (rotation force), the character must rotate too.
  382.             yaw = fmod(yaw + rad2deg(floor_angular_vel.y) * delta, 360)
  383.             node_yaw.set_rotation(Vector3(0, deg2rad(yaw), 0))
  384.     return floor_velocity
  385.  
  386. func _on_ladders_body_enter( body ):
  387.     if body==self:
  388.         fly_mode=true
  389.  
  390. func _on_ladders_body_exit( body ):
  391.     if body==self:
  392.         fly_mode=false
  393.  
  394. # crouch ########################################################3
  395. func crouch():
  396.     node_body.get_shape().set_height(0.1)
  397.     node_body.get_shape().set_radius(0.1)
  398.     node_body.set_translation(Vector3(0,0.55,0))
  399.     node_camera.set_translation(Vector3(0,0.4,0))
  400.     node_leg.set_translation(Vector3(0,0,0.1))
  401.     is_crouching=true
  402.     node_head_check.set_enable_monitoring(true)
  403.    
  404. func stand():
  405.     if node_head_check.get_overlapping_bodies().size()>0:
  406.         return
  407.    
  408.     node_body.get_shape().set_height(0.8)
  409.     node_body.get_shape().set_radius(0.6)
  410.     node_body.set_translation(Vector3(0,1.4,0))
  411.     node_camera.set_translation(Vector3(0,1.7,0))
  412.     node_leg.set_translation(Vector3(0,0,1))
  413.     is_crouching=false
  414.     node_head_check.set_enable_monitoring(false)
  415.  
  416. func get_walk_speed(is_crouching,is_running):
  417.     var speed=walk_speed
  418.     if is_crouching:
  419.         speed*=crouch_factor
  420.     if is_running:
  421.         speed*=run_factor
  422.     return speed
  423.  
  424. # Setter/Getter #################################################################
  425.  
  426. func _set_leg_length(value):
  427.     leg_length=value
  428.     if _is_loaded:
  429.         node_leg.get_shape().set_length(value)
  430.         node_ray.set_cast_to(Vector3(0,-value*2,0))
  431.  
  432. func _set_body_radius(value):
  433.     body_radius=value
  434.     if _is_loaded:
  435.         node_body.get_shape().set_radius(value)
  436.  
  437. func _set_body_height(value):
  438.     body_height=value
  439.     if _is_loaded:
  440.         node_body.get_shape().set_height(value)
  441.  
  442. func _set_camera_height(value):
  443.     camera_height=value
  444.     if _is_loaded:
  445.         node_camera.set_translation(Vector3(0,value,0))
  446.  
  447. func _set_action_range(value):
  448.     action_range=value
  449.     if _is_loaded:
  450.         node_action_ray.set_cast_to(Vector3(0,0,-value))
  451.  
  452. func _set_active(value):
  453.     if value==active:
  454.         return
  455.    
  456.     active=value
  457.     if active:
  458.         Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
  459.     else:
  460.         Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement