player.gd 5.3 KB

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