Skip to main content
Ramblings of a Developer

Journal: A CRUD developer builds a game

What did I do today? #

Today I switched the movement to use the physics engine. I think it feels a bit better, and makes a bunch of things better. It allows me to easily add level elements such as blocks and barriers, and prevents the enemies and the player from overlapping.

What did I learn? #

One problem I ran into was that I wanted to add some knock back effects, so the player and enemies will get knocked back when they get hit. I solved this by tracking two different velocities (one movement velocity, and one velocity for "impulses"). Here is the code for the player that shows how I put it together:

func _process(delta):
	var nearest_mob = null
	var min_distance = INF
	for mob in get_tree().get_nodes_in_group("mobs"):
		var distance = mob.global_position.distance_to(position)
		if !nearest_mob || distance < min_distance:
			min_distance = distance
			nearest_mob = mob
	if nearest_mob:
		# change the angle of the turret to aim at the nearest enemy, with a maximum rotation speed
		$Turret.fire_rate = fire_rate
		$Turret.target_position = nearest_mob.global_position
	else:
		$Turret.fire_rate = 0
		$Turret.target_position = Vector2.INF

var target_position = Vector2.INF
var movement_velocity = Vector2()
var is_reversing = false
var impulse_velocity = Vector2()

func control(delta):
	if health <= 0:
		return

	var rotation_dir = 0
	if Input.is_action_pressed("turn_left"):
		rotation_dir = -1
		target_position = Vector2.INF
	if Input.is_action_pressed("turn_right"):
		rotation_dir = 1
		target_position = Vector2.INF
	rotation += rotation_dir * rotation_speed * delta

	if Input.is_action_pressed("move_forward"):
		is_reversing = false
		movement_velocity = Vector2(0, 1).rotated(rotation).normalized()
		target_position = Vector2.INF
	if Input.is_action_pressed("move_backward"):
		is_reversing = true
		target_position = Vector2.INF
		movement_velocity = Vector2(0, -1).rotated(rotation).normalized() / 2

	# if target_position is set, move towards it with a max rotation speed
	if target_position != Vector2.INF:
		is_reversing = false
		if (target_position - position).length() < 100:
			target_position = Vector2.INF
		else:
			# calculate the angle to the target position
			var angle_difference = get_angle_to(target_position) - PI / 2

			if angle_difference > PI:
				angle_difference -= 2 * PI
			elif angle_difference < -PI:
				angle_difference += 2 * PI

			rotation_dir = 1
			if angle_difference < 0:
				rotation_dir = -1

			var rotation_amount = min(abs(angle_difference), rotation_speed * delta)
			rotation += rotation_dir * rotation_amount
			movement_velocity = Vector2(0, 1).rotated(rotation).normalized()

func _physics_process(delta):
	position.x = wrapf(position.x, 0, screen_size.x)
	position.y = wrapf(position.y, 0, screen_size.y)
	control(delta)
	move_and_slide(movement_velocity * speed + impulse_velocity)
	movement_velocity = Vector2(0, -1 if is_reversing else 1).rotated(rotation).normalized() * movement_velocity.linear_interpolate(Vector2(), 0.1).length()
	impulse_velocity = impulse_velocity.linear_interpolate(Vector2(), 0.1)

func apply_impulse(direction, force):
	impulse_velocity += direction.normalized() * force

func _on_Player_hit(damage, location, velocity):
	apply_impulse(velocity, damage * 10)
	decrement_health(damage)
	if health <= 0:
		rotation = 0
		movement_velocity = Vector2()
		impulse_velocity = Vector2()
		$Body.animation = 'die'
		$Body.scale = Vector2(2, 2)
		$Body.play()
		$Body.connect("animation_finished", self, "_on_Body_animation_finished")
		$Turret.hide()
		$CollisionShape2D.set_deferred("disabled", true)

Things to note:

What's next? #

Demo #

Click here to play.

WASD or arrows to move, or click/touch to move.