aboutsummaryrefslogtreecommitdiffstats
path: root/Scripts/car_behaviour.gd
blob: a881363c7c6ed494f730f5ae156ac6cc18402306 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
extends CharacterBody2D

# editor variables
@export var max_speed = 800
@export var steer_force = 20
@export var acceleration = 8
@export var mass = 1.0
@export var look_ahead = 75
@export var num_rays = 32
@export var driving = false

# context array
var ray_directions = []
var interest = []
var danger = []

var chosen_dir = Vector2.ZERO
var current_speed = 0


# Called when the node enters the scene tree for the first time.
func _ready():
	interest.resize(num_rays)
	danger.resize(num_rays)
	ray_directions.resize(num_rays)
	for i in num_rays:
		var angle = i * 2 * PI / num_rays
		ray_directions[i] = Vector2.RIGHT.rotated(angle)

func _physics_process(delta):
	if not driving:
		$Sprite2D.hide()
		return
		
	$Sprite2D.show()
	
	# Code for getting the next desired direction based on the environment
	set_interest()
	set_danger()
	choose_direction()
	
#	example_movement(delta)
#	own_movement(delta)
	own_movement_improved(delta)


func set_interest():
	# Set interest in each slot based on world direction
#	if owner and owner.has_method("get_path_direction"):
	if owner and owner.has_method("get_path_next_position"):
#		var path_direction = owner.get_path_direction(position)
		var next_pos = owner.get_path_next_position(position)
		var path_direction = (next_pos - position).normalized()

		for i in num_rays:
			var d = ray_directions[i].rotated(rotation).dot(path_direction)
			interest[i] = max(0, d)
	# If no world path, use default interest
	else:
		set_default_interest()

func set_default_interest():
	# Default to moving forward
	for i in num_rays:
		var d = ray_directions[i].rotated(rotation).dot(transform.x)
		interest[i] = max(0, d)

func set_danger():
	# Cast rays to find danger directions
	var space_state = get_world_2d().direct_space_state
	
	var params = PhysicsRayQueryParameters2D.new()
	params.from = position
	params.exclude = [self]
	
	for i in num_rays:
		params.to = position + ray_directions[i].rotated(rotation) * look_ahead
		var result = space_state.intersect_ray(params)
		danger[i] = 1.0 if result else 0.0

func choose_direction():
	# Eliminate interest in slots with danger
	for i in num_rays:
		if danger[i] > 0.0:
			interest[i] = 0.0
	# Choose direction based on remaining interest
	chosen_dir = Vector2.ZERO
	for i in num_rays:
		chosen_dir += ray_directions[i] * interest[i]
	chosen_dir = chosen_dir.normalized()


func example_movement(delta):
	var desired_velocity = chosen_dir.rotated(rotation) * max_speed
	velocity = velocity.lerp(desired_velocity, steer_force)
	rotation = velocity.angle()
	move_and_collide(velocity * delta)


func own_movement(delta):
	var desired_velocity = chosen_dir.rotated(rotation) * max_speed

	var new_speed = lerpf(velocity.length(), max_speed, acceleration)
	var new_angle = lerp_angle(velocity.angle(), desired_velocity.angle(), steer_force)
	
	velocity = Vector2.from_angle(new_angle) * new_speed
	rotation = new_angle
	move_and_collide(velocity * delta)
	

func own_movement_improved(delta):
	var desired_velocity = chosen_dir.rotated(rotation) * max_speed
#	print('==========')
#	print('current speed: ', velocity.length(), ' | current angle: ', velocity.angle())
#	print('desired speed: ', desired_velocity.length(), ' | desired angle: ', desired_velocity.angle())
	
#	var possible_radius = mass * velocity.length_squared() / steer_force
#	var possible_steer_angle = velocity.length() / possible_radius
	var possible_steer_angle = steer_force / (mass * velocity.length())
	var angle_change = clampf(wrapf(desired_velocity.angle() - velocity.angle(), -PI, PI), -possible_steer_angle, possible_steer_angle)
	var new_angle = wrapf(velocity.angle() + angle_change, -PI, PI)
#	print('possible_steer_angle: ', possible_steer_angle, ' | angle_change: ', angle_change, ' | new_angle: ', new_angle )
	
	var wanted_speed = steer_force / (mass * absf(desired_velocity.angle() - velocity.angle()))
	var new_max_speed = min(velocity.length() + (acceleration), max_speed)
	var new_min_speed = max(velocity.length() - (acceleration), 0)
	var new_speed = clampf(wanted_speed, new_min_speed, new_max_speed)
#	print('wanted: ', wanted_speed, ' | min: ', new_min_speed, ' | max: ', new_max_speed, ' | new: ', new_speed)
	
	velocity = Vector2.from_angle(new_angle) * new_speed
	rotation = new_angle
	move_and_collide(velocity * delta)