player.gd 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. class_name Player
  2. extends RigidBody2D
  3. # Character Demo, written by Juan Linietsky.
  4. #
  5. # Implementation of a 2D Character controller.
  6. # This implementation uses the physics engine for
  7. # controlling a character, in a very similar way
  8. # than a 3D character controller would be implemented.
  9. #
  10. # Using the physics engine for this has the main advantages:
  11. # - Easy to write.
  12. # - Interaction with other physics-based objects is free
  13. # - Only have to deal with the object linear velocity, not position
  14. # - All collision/area framework available
  15. #
  16. # But also has the following disadvantages:
  17. # - Objects may bounce a little bit sometimes
  18. # - Going up ramps sends the chracter flying up, small hack is needed.
  19. # - A ray collider is needed to avoid sliding down on ramps and
  20. # undesiderd bumps, small steps and rare numerical precision errors.
  21. # (another alternative may be to turn on friction when the character is not moving).
  22. # - Friction cant be used, so floor velocity must be considered
  23. # for moving platforms.
  24. const WALK_ACCEL = 500.0
  25. const WALK_DEACCEL = 500.0
  26. const WALK_MAX_VELOCITY = 140.0
  27. const AIR_ACCEL = 100.0
  28. const AIR_DEACCEL = 100.0
  29. const JUMP_VELOCITY = 380
  30. const STOP_JUMP_FORCE = 450.0
  31. const MAX_SHOOT_POSE_TIME = 0.3
  32. const MAX_FLOOR_AIRBORNE_TIME = 0.15
  33. var anim = ""
  34. var siding_left = false
  35. var jumping = false
  36. var stopping_jump = false
  37. var shooting = false
  38. var floor_h_velocity = 0.0
  39. var airborne_time = 1e20
  40. var shoot_time = 1e20
  41. var Bullet = preload("res://player/Bullet.tscn")
  42. var Enemy = preload("res://enemy/Enemy.tscn")
  43. onready var sound_jump = $SoundJump
  44. onready var sound_shoot = $SoundShoot
  45. onready var sprite = $Sprite
  46. onready var sprite_smoke = sprite.get_node(@"Smoke")
  47. onready var animation_player = $AnimationPlayer
  48. onready var bullet_shoot = $BulletShoot
  49. func _integrate_forces(s):
  50. var lv = s.get_linear_velocity()
  51. var step = s.get_step()
  52. var new_anim = anim
  53. var new_siding_left = siding_left
  54. # Get player input.
  55. var move_left = Input.is_action_pressed("move_left")
  56. var move_right = Input.is_action_pressed("move_right")
  57. var jump = Input.is_action_pressed("jump")
  58. var shoot = Input.is_action_pressed("shoot")
  59. var spawn = Input.is_action_pressed("spawn")
  60. if spawn:
  61. call_deferred("_spawn_enemy_above")
  62. # Deapply prev floor velocity.
  63. lv.x -= floor_h_velocity
  64. floor_h_velocity = 0.0
  65. # Find the floor (a contact with upwards facing collision normal).
  66. var found_floor = false
  67. var floor_index = -1
  68. for x in range(s.get_contact_count()):
  69. var ci = s.get_contact_local_normal(x)
  70. if ci.dot(Vector2(0, -1)) > 0.6:
  71. found_floor = true
  72. floor_index = x
  73. # A good idea when implementing characters of all kinds,
  74. # compensates for physics imprecision, as well as human reaction delay.
  75. if shoot and not shooting:
  76. call_deferred("_shot_bullet")
  77. else:
  78. shoot_time += step
  79. if found_floor:
  80. airborne_time = 0.0
  81. else:
  82. airborne_time += step # Time it spent in the air.
  83. var on_floor = airborne_time < MAX_FLOOR_AIRBORNE_TIME
  84. # Process jump.
  85. if jumping:
  86. if lv.y > 0:
  87. # Set off the jumping flag if going down.
  88. jumping = false
  89. elif not jump:
  90. stopping_jump = true
  91. if stopping_jump:
  92. lv.y += STOP_JUMP_FORCE * step
  93. if on_floor:
  94. # Process logic when character is on floor.
  95. if move_left and not move_right:
  96. if lv.x > -WALK_MAX_VELOCITY:
  97. lv.x -= WALK_ACCEL * step
  98. elif move_right and not move_left:
  99. if lv.x < WALK_MAX_VELOCITY:
  100. lv.x += WALK_ACCEL * step
  101. else:
  102. var xv = abs(lv.x)
  103. xv -= WALK_DEACCEL * step
  104. if xv < 0:
  105. xv = 0
  106. lv.x = sign(lv.x) * xv
  107. # Check jump.
  108. if not jumping and jump:
  109. lv.y = -JUMP_VELOCITY
  110. jumping = true
  111. stopping_jump = false
  112. sound_jump.play()
  113. # Check siding.
  114. if lv.x < 0 and move_left:
  115. new_siding_left = true
  116. elif lv.x > 0 and move_right:
  117. new_siding_left = false
  118. if jumping:
  119. new_anim = "jumping"
  120. elif abs(lv.x) < 0.1:
  121. if shoot_time < MAX_SHOOT_POSE_TIME:
  122. new_anim = "idle_weapon"
  123. else:
  124. new_anim = "idle"
  125. else:
  126. if shoot_time < MAX_SHOOT_POSE_TIME:
  127. new_anim = "run_weapon"
  128. else:
  129. new_anim = "run"
  130. else:
  131. # Process logic when the character is in the air.
  132. if move_left and not move_right:
  133. if lv.x > -WALK_MAX_VELOCITY:
  134. lv.x -= AIR_ACCEL * step
  135. elif move_right and not move_left:
  136. if lv.x < WALK_MAX_VELOCITY:
  137. lv.x += AIR_ACCEL * step
  138. else:
  139. var xv = abs(lv.x)
  140. xv -= AIR_DEACCEL * step
  141. if xv < 0:
  142. xv = 0
  143. lv.x = sign(lv.x) * xv
  144. if lv.y < 0:
  145. if shoot_time < MAX_SHOOT_POSE_TIME:
  146. new_anim = "jumping_weapon"
  147. else:
  148. new_anim = "jumping"
  149. else:
  150. if shoot_time < MAX_SHOOT_POSE_TIME:
  151. new_anim = "falling_weapon"
  152. else:
  153. new_anim = "falling"
  154. # Update siding.
  155. if new_siding_left != siding_left:
  156. if new_siding_left:
  157. sprite.scale.x = -1
  158. else:
  159. sprite.scale.x = 1
  160. siding_left = new_siding_left
  161. # Change animation.
  162. if new_anim != anim:
  163. anim = new_anim
  164. animation_player.play(anim)
  165. shooting = shoot
  166. # Apply floor velocity.
  167. if found_floor:
  168. floor_h_velocity = s.get_contact_collider_velocity_at_position(floor_index).x
  169. lv.x += floor_h_velocity
  170. # Finally, apply gravity and set back the linear velocity.
  171. lv += s.get_total_gravity() * step
  172. s.set_linear_velocity(lv)
  173. func _shot_bullet():
  174. shoot_time = 0
  175. var bi = Bullet.instance()
  176. var ss
  177. if siding_left:
  178. ss = -1.0
  179. else:
  180. ss = 1.0
  181. var pos = position + bullet_shoot.position * Vector2(ss, 1.0)
  182. bi.position = pos
  183. get_parent().add_child(bi)
  184. bi.linear_velocity = Vector2(400.0 * ss, -40)
  185. sprite_smoke.restart()
  186. sound_shoot.play()
  187. add_collision_exception_with(bi) # Make bullet and this not collide.
  188. func _spawn_enemy_above():
  189. var e = Enemy.instance()
  190. e.position = position + 50 * Vector2.UP
  191. get_parent().add_child(e)