player.gd 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. class_name Player
  2. extends RigidBody2D
  3. const WALK_ACCEL = 1000.0
  4. const WALK_DEACCEL = 1000.0
  5. const WALK_MAX_VELOCITY = 200.0
  6. const AIR_ACCEL = 250.0
  7. const AIR_DEACCEL = 250.0
  8. const JUMP_VELOCITY = 380.0
  9. const STOP_JUMP_FORCE = 450.0
  10. const MAX_SHOOT_POSE_TIME = 0.3
  11. const MAX_FLOOR_AIRBORNE_TIME = 0.15
  12. const BULLET_SCENE = preload("res://player/bullet.tscn")
  13. const ENEMY_SCENE = preload("res://enemy/enemy.tscn")
  14. var anim := ""
  15. var siding_left := false
  16. var jumping := false
  17. var stopping_jump := false
  18. var shooting := false
  19. var floor_h_velocity: float = 0.0
  20. var airborne_time: float = 1e20
  21. var shoot_time: float = 1e20
  22. @onready var sound_jump := $SoundJump as AudioStreamPlayer2D
  23. @onready var sound_shoot := $SoundShoot as AudioStreamPlayer2D
  24. @onready var sprite := $Sprite2D as Sprite2D
  25. @onready var sprite_smoke := sprite.get_node(^"Smoke") as CPUParticles2D
  26. @onready var animation_player := $AnimationPlayer as AnimationPlayer
  27. @onready var bullet_shoot := $BulletShoot as Marker2D
  28. func _integrate_forces(state: PhysicsDirectBodyState2D) -> void:
  29. var velocity := state.get_linear_velocity()
  30. var step := state.get_step()
  31. var new_anim := anim
  32. var new_siding_left := siding_left
  33. # Get player input.
  34. var move_left := Input.is_action_pressed(&"move_left")
  35. var move_right := Input.is_action_pressed(&"move_right")
  36. var jump := Input.is_action_pressed(&"jump")
  37. var shoot := Input.is_action_pressed(&"shoot")
  38. var spawn := Input.is_action_just_pressed(&"spawn")
  39. if spawn:
  40. _spawn_enemy_above.call_deferred()
  41. # Deapply previous floor velocity.
  42. velocity.x -= floor_h_velocity
  43. floor_h_velocity = 0.0
  44. # Find the floor (a contact with upwards facing collision normal).
  45. var found_floor := false
  46. var floor_index := -1
  47. for contact_index in state.get_contact_count():
  48. var collision_normal := state.get_contact_local_normal(contact_index)
  49. if collision_normal.dot(Vector2(0, -1)) > 0.6:
  50. found_floor = true
  51. floor_index = contact_index
  52. # A good idea when implementing characters of all kinds,
  53. # compensates for physics imprecision, as well as human reaction delay.
  54. if shoot and not shooting:
  55. _shot_bullet.call_deferred()
  56. else:
  57. shoot_time += step
  58. if found_floor:
  59. airborne_time = 0.0
  60. else:
  61. airborne_time += step # Time it spent in the air.
  62. var on_floor := airborne_time < MAX_FLOOR_AIRBORNE_TIME
  63. # Process jump.
  64. if jumping:
  65. if velocity.y > 0:
  66. # Set off the jumping flag if going down.
  67. jumping = false
  68. elif not jump:
  69. stopping_jump = true
  70. if stopping_jump:
  71. velocity.y += STOP_JUMP_FORCE * step
  72. if on_floor:
  73. # Process logic when character is on floor.
  74. if move_left and not move_right:
  75. if velocity.x > -WALK_MAX_VELOCITY:
  76. velocity.x -= WALK_ACCEL * step
  77. elif move_right and not move_left:
  78. if velocity.x < WALK_MAX_VELOCITY:
  79. velocity.x += WALK_ACCEL * step
  80. else:
  81. var xv := absf(velocity.x)
  82. xv -= WALK_DEACCEL * step
  83. if xv < 0:
  84. xv = 0
  85. velocity.x = signf(velocity.x) * xv
  86. # Check jump.
  87. if not jumping and jump:
  88. velocity.y = -JUMP_VELOCITY
  89. jumping = true
  90. stopping_jump = false
  91. sound_jump.play()
  92. # Check siding.
  93. if velocity.x < 0 and move_left:
  94. new_siding_left = true
  95. elif velocity.x > 0 and move_right:
  96. new_siding_left = false
  97. if jumping:
  98. new_anim = "jumping"
  99. elif absf(velocity.x) < 0.1:
  100. if shoot_time < MAX_SHOOT_POSE_TIME:
  101. new_anim = "idle_weapon"
  102. else:
  103. new_anim = "idle"
  104. else:
  105. if shoot_time < MAX_SHOOT_POSE_TIME:
  106. new_anim = "run_weapon"
  107. else:
  108. new_anim = "run"
  109. else:
  110. # Process logic when the character is in the air.
  111. if move_left and not move_right:
  112. if velocity.x > -WALK_MAX_VELOCITY:
  113. velocity.x -= AIR_ACCEL * step
  114. elif move_right and not move_left:
  115. if velocity.x < WALK_MAX_VELOCITY:
  116. velocity.x += AIR_ACCEL * step
  117. else:
  118. var xv := absf(velocity.x)
  119. xv -= AIR_DEACCEL * step
  120. if xv < 0:
  121. xv = 0
  122. velocity.x = signf(velocity.x) * xv
  123. if velocity.y < 0:
  124. if shoot_time < MAX_SHOOT_POSE_TIME:
  125. new_anim = "jumping_weapon"
  126. else:
  127. new_anim = "jumping"
  128. else:
  129. if shoot_time < MAX_SHOOT_POSE_TIME:
  130. new_anim = "falling_weapon"
  131. else:
  132. new_anim = "falling"
  133. # Update siding.
  134. if new_siding_left != siding_left:
  135. if new_siding_left:
  136. sprite.scale.x = -1
  137. else:
  138. sprite.scale.x = 1
  139. siding_left = new_siding_left
  140. # Change animation.
  141. if new_anim != anim:
  142. anim = new_anim
  143. animation_player.play(anim)
  144. shooting = shoot
  145. # Apply floor velocity.
  146. if found_floor:
  147. floor_h_velocity = state.get_contact_collider_velocity_at_position(floor_index).x
  148. velocity.x += floor_h_velocity
  149. # Finally, apply gravity and set back the linear velocity.
  150. velocity += state.get_total_gravity() * step
  151. state.set_linear_velocity(velocity)
  152. func _shot_bullet() -> void:
  153. shoot_time = 0
  154. var bullet := BULLET_SCENE.instantiate() as RigidBody2D
  155. var speed_scale: float
  156. if siding_left:
  157. speed_scale = -1.0
  158. else:
  159. speed_scale = 1.0
  160. bullet.position = position + bullet_shoot.position * Vector2(speed_scale, 1.0)
  161. get_parent().add_child(bullet)
  162. bullet.linear_velocity = Vector2(400.0 * speed_scale, -40)
  163. sprite_smoke.restart()
  164. sound_shoot.play()
  165. add_collision_exception_with(bullet) # Make bullet and this not collide.
  166. func _spawn_enemy_above() -> void:
  167. var enemy := ENEMY_SCENE.instantiate() as RigidBody2D
  168. enemy.position = position + 50 * Vector2.UP
  169. get_parent().add_child(enemy)