mirror of
https://github.com/godotengine/godot-demo-projects.git
synced 2026-01-04 15:00:09 +01:00
Update 2D physics platformer for 4.0
This commit is contained in:
@@ -1,27 +1,5 @@
|
||||
class_name Player
|
||||
extends RigidBody2D
|
||||
class_name Player extends RigidBody2D
|
||||
|
||||
# Character Demo, written by Juan Linietsky.
|
||||
#
|
||||
# Implementation of a 2D Character controller.
|
||||
# This implementation uses the physics engine for
|
||||
# controlling a character, in a very similar way
|
||||
# than a 3D character controller would be implemented.
|
||||
#
|
||||
# Using the physics engine for this has the main advantages:
|
||||
# - Easy to write.
|
||||
# - Interaction with other physics-based objects is free
|
||||
# - Only have to deal with the object linear velocity, not position
|
||||
# - All collision/area framework available
|
||||
#
|
||||
# But also has the following disadvantages:
|
||||
# - Objects may bounce a little bit sometimes
|
||||
# - Going up ramps sends the chracter flying up, small hack is needed.
|
||||
# - A ray collider is needed to avoid sliding down on ramps and
|
||||
# undesiderd bumps, small steps and rare numerical precision errors.
|
||||
# (another alternative may be to turn on friction when the character is not moving).
|
||||
# - Friction cant be used, so floor velocity must be considered
|
||||
# for moving platforms.
|
||||
|
||||
const WALK_ACCEL = 500.0
|
||||
const WALK_DEACCEL = 500.0
|
||||
@@ -33,63 +11,64 @@ const STOP_JUMP_FORCE = 450.0
|
||||
const MAX_SHOOT_POSE_TIME = 0.3
|
||||
const MAX_FLOOR_AIRBORNE_TIME = 0.15
|
||||
|
||||
var anim = ""
|
||||
var siding_left = false
|
||||
var jumping = false
|
||||
var stopping_jump = false
|
||||
var shooting = false
|
||||
var anim := ""
|
||||
var siding_left := false
|
||||
var jumping := false
|
||||
var stopping_jump := false
|
||||
var shooting := false
|
||||
|
||||
var floor_h_velocity = 0.0
|
||||
var floor_h_velocity: float = 0.0
|
||||
|
||||
var airborne_time = 1e20
|
||||
var shoot_time = 1e20
|
||||
var airborne_time: float = 1e20
|
||||
var shoot_time: float = 1e20
|
||||
|
||||
var Bullet = preload("res://player/Bullet.tscn")
|
||||
var Enemy = preload("res://enemy/Enemy.tscn")
|
||||
var Bullet := preload("res://player/bullet.tscn")
|
||||
var Enemy := preload("res://enemy/enemy.tscn")
|
||||
|
||||
@onready var sound_jump = $SoundJump
|
||||
@onready var sound_shoot = $SoundShoot
|
||||
@onready var sprite = $Sprite2D
|
||||
@onready var sprite_smoke = sprite.get_node(^"Smoke")
|
||||
@onready var animation_player = $AnimationPlayer
|
||||
@onready var bullet_shoot = $BulletShoot
|
||||
@onready var sound_jump := $SoundJump as AudioStreamPlayer2D
|
||||
@onready var sound_shoot := $SoundShoot as AudioStreamPlayer2D
|
||||
@onready var sprite := $Sprite2D as Sprite2D
|
||||
@onready var sprite_smoke := sprite.get_node(^"Smoke") as CPUParticles2D
|
||||
@onready var animation_player := $AnimationPlayer as AnimationPlayer
|
||||
@onready var bullet_shoot := $BulletShoot as Marker2D
|
||||
|
||||
func _integrate_forces(s):
|
||||
var lv = s.get_linear_velocity()
|
||||
var step = s.get_step()
|
||||
|
||||
var new_anim = anim
|
||||
var new_siding_left = siding_left
|
||||
func _integrate_forces(state: PhysicsDirectBodyState2D) -> void:
|
||||
var velocity := state.get_linear_velocity()
|
||||
var step := state.get_step()
|
||||
|
||||
var new_anim := anim
|
||||
var new_siding_left := siding_left
|
||||
|
||||
# Get player input.
|
||||
var move_left = Input.is_action_pressed(&"move_left")
|
||||
var move_right = Input.is_action_pressed(&"move_right")
|
||||
var jump = Input.is_action_pressed(&"jump")
|
||||
var shoot = Input.is_action_pressed(&"shoot")
|
||||
var spawn = Input.is_action_pressed(&"spawn")
|
||||
var move_left := Input.is_action_pressed(&"move_left")
|
||||
var move_right := Input.is_action_pressed(&"move_right")
|
||||
var jump := Input.is_action_pressed(&"jump")
|
||||
var shoot := Input.is_action_pressed(&"shoot")
|
||||
var spawn := Input.is_action_pressed(&"spawn")
|
||||
|
||||
if spawn:
|
||||
call_deferred("_spawn_enemy_above")
|
||||
_spawn_enemy_above.call_deferred()
|
||||
|
||||
# Deapply prev floor velocity.
|
||||
lv.x -= floor_h_velocity
|
||||
velocity.x -= floor_h_velocity
|
||||
floor_h_velocity = 0.0
|
||||
|
||||
# Find the floor (a contact with upwards facing collision normal).
|
||||
var found_floor = false
|
||||
var floor_index = -1
|
||||
var found_floor := false
|
||||
var floor_index := -1
|
||||
|
||||
for x in range(s.get_contact_count()):
|
||||
var ci = s.get_contact_local_normal(x)
|
||||
for contact_index in state.get_contact_count():
|
||||
var collision_normal = state.get_contact_local_normal(contact_index)
|
||||
|
||||
if ci.dot(Vector2(0, -1)) > 0.6:
|
||||
if collision_normal.dot(Vector2(0, -1)) > 0.6:
|
||||
found_floor = true
|
||||
floor_index = x
|
||||
floor_index = contact_index
|
||||
|
||||
# A good idea when implementing characters of all kinds,
|
||||
# compensates for physics imprecision, as well as human reaction delay.
|
||||
if shoot and not shooting:
|
||||
call_deferred("_shot_bullet")
|
||||
_shot_bullet.call_deferred()
|
||||
else:
|
||||
shoot_time += step
|
||||
|
||||
@@ -98,49 +77,49 @@ func _integrate_forces(s):
|
||||
else:
|
||||
airborne_time += step # Time it spent in the air.
|
||||
|
||||
var on_floor = airborne_time < MAX_FLOOR_AIRBORNE_TIME
|
||||
var on_floor := airborne_time < MAX_FLOOR_AIRBORNE_TIME
|
||||
|
||||
# Process jump.
|
||||
if jumping:
|
||||
if lv.y > 0:
|
||||
if velocity.y > 0:
|
||||
# Set off the jumping flag if going down.
|
||||
jumping = false
|
||||
elif not jump:
|
||||
stopping_jump = true
|
||||
|
||||
if stopping_jump:
|
||||
lv.y += STOP_JUMP_FORCE * step
|
||||
velocity.y += STOP_JUMP_FORCE * step
|
||||
|
||||
if on_floor:
|
||||
# Process logic when character is on floor.
|
||||
if move_left and not move_right:
|
||||
if lv.x > -WALK_MAX_VELOCITY:
|
||||
lv.x -= WALK_ACCEL * step
|
||||
if velocity.x > -WALK_MAX_VELOCITY:
|
||||
velocity.x -= WALK_ACCEL * step
|
||||
elif move_right and not move_left:
|
||||
if lv.x < WALK_MAX_VELOCITY:
|
||||
lv.x += WALK_ACCEL * step
|
||||
if velocity.x < WALK_MAX_VELOCITY:
|
||||
velocity.x += WALK_ACCEL * step
|
||||
else:
|
||||
var xv = abs(lv.x)
|
||||
var xv := absf(velocity.x)
|
||||
xv -= WALK_DEACCEL * step
|
||||
if xv < 0:
|
||||
xv = 0
|
||||
lv.x = sign(lv.x) * xv
|
||||
velocity.x = signf(velocity.x) * xv
|
||||
|
||||
# Check jump.
|
||||
if not jumping and jump:
|
||||
lv.y = -JUMP_VELOCITY
|
||||
velocity.y = -JUMP_VELOCITY
|
||||
jumping = true
|
||||
stopping_jump = false
|
||||
sound_jump.play()
|
||||
|
||||
# Check siding.
|
||||
if lv.x < 0 and move_left:
|
||||
if velocity.x < 0 and move_left:
|
||||
new_siding_left = true
|
||||
elif lv.x > 0 and move_right:
|
||||
elif velocity.x > 0 and move_right:
|
||||
new_siding_left = false
|
||||
if jumping:
|
||||
new_anim = "jumping"
|
||||
elif abs(lv.x) < 0.1:
|
||||
elif absf(velocity.x) < 0.1:
|
||||
if shoot_time < MAX_SHOOT_POSE_TIME:
|
||||
new_anim = "idle_weapon"
|
||||
else:
|
||||
@@ -153,20 +132,20 @@ func _integrate_forces(s):
|
||||
else:
|
||||
# Process logic when the character is in the air.
|
||||
if move_left and not move_right:
|
||||
if lv.x > -WALK_MAX_VELOCITY:
|
||||
lv.x -= AIR_ACCEL * step
|
||||
if velocity.x > -WALK_MAX_VELOCITY:
|
||||
velocity.x -= AIR_ACCEL * step
|
||||
elif move_right and not move_left:
|
||||
if lv.x < WALK_MAX_VELOCITY:
|
||||
lv.x += AIR_ACCEL * step
|
||||
if velocity.x < WALK_MAX_VELOCITY:
|
||||
velocity.x += AIR_ACCEL * step
|
||||
else:
|
||||
var xv = abs(lv.x)
|
||||
var xv := absf(velocity.x)
|
||||
xv -= AIR_DEACCEL * step
|
||||
|
||||
if xv < 0:
|
||||
xv = 0
|
||||
lv.x = sign(lv.x) * xv
|
||||
velocity.x = signf(velocity.x) * xv
|
||||
|
||||
if lv.y < 0:
|
||||
if velocity.y < 0:
|
||||
if shoot_time < MAX_SHOOT_POSE_TIME:
|
||||
new_anim = "jumping_weapon"
|
||||
else:
|
||||
@@ -195,36 +174,35 @@ func _integrate_forces(s):
|
||||
|
||||
# Apply floor velocity.
|
||||
if found_floor:
|
||||
floor_h_velocity = s.get_contact_collider_velocity_at_position(floor_index).x
|
||||
lv.x += floor_h_velocity
|
||||
floor_h_velocity = state.get_contact_collider_velocity_at_position(floor_index).x
|
||||
velocity.x += floor_h_velocity
|
||||
|
||||
# Finally, apply gravity and set back the linear velocity.
|
||||
lv += s.get_total_gravity() * step
|
||||
s.set_linear_velocity(lv)
|
||||
velocity += state.get_total_gravity() * step
|
||||
state.set_linear_velocity(velocity)
|
||||
|
||||
|
||||
func _shot_bullet():
|
||||
func _shot_bullet() -> void:
|
||||
shoot_time = 0
|
||||
var bi = Bullet.instantiate()
|
||||
var ss
|
||||
var bullet := Bullet.instantiate() as RigidBody2D
|
||||
var speed_scale: float
|
||||
if siding_left:
|
||||
ss = -1.0
|
||||
speed_scale = -1.0
|
||||
else:
|
||||
ss = 1.0
|
||||
var pos = position + bullet_shoot.position * Vector2(ss, 1.0)
|
||||
speed_scale = 1.0
|
||||
|
||||
bi.position = pos
|
||||
get_parent().add_child(bi)
|
||||
bullet.position = self.position + bullet_shoot.position * Vector2(speed_scale, 1.0)
|
||||
get_parent().add_child(bullet)
|
||||
|
||||
bi.linear_velocity = Vector2(400.0 * ss, -40)
|
||||
bullet.linear_velocity = Vector2(400.0 * speed_scale, -40)
|
||||
|
||||
sprite_smoke.restart()
|
||||
sound_shoot.play()
|
||||
|
||||
add_collision_exception_with(bi) # Make bullet and this not collide.
|
||||
add_collision_exception_with(bullet) # Make bullet and this not collide.
|
||||
|
||||
|
||||
func _spawn_enemy_above():
|
||||
var e = Enemy.instantiate()
|
||||
e.position = position + 50 * Vector2.UP
|
||||
get_parent().add_child(e)
|
||||
func _spawn_enemy_above() -> void:
|
||||
var enemy := Enemy.instantiate() as RigidBody2D
|
||||
enemy.position = self.position + 50 * Vector2.UP
|
||||
get_parent().add_child(enemy)
|
||||
|
||||
Reference in New Issue
Block a user