I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -0,0 +1,48 @@
[gd_scene load_steps=5 format=3 uid="uid://ddeq7mn1ealyc"]
[ext_resource type="Script" path="res://addons/godot_rl_agents/sensors/sensors_2d/RaycastSensor2D.gd" id="1"]
[sub_resource type="GDScript" id="2"]
script/source = "extends Node2D
func _physics_process(delta: float) -> void:
print(\"step start\")
"
[sub_resource type="GDScript" id="1"]
script/source = "extends RayCast2D
var steps = 1
func _physics_process(delta: float) -> void:
print(\"processing raycast\")
steps += 1
if steps % 2:
force_raycast_update()
print(is_colliding())
"
[sub_resource type="CircleShape2D" id="3"]
[node name="ExampleRaycastSensor2D" type="Node2D"]
script = SubResource("2")
[node name="ExampleAgent" type="Node2D" parent="."]
position = Vector2(573, 314)
rotation = 0.286234
[node name="RaycastSensor2D" type="Node2D" parent="ExampleAgent"]
script = ExtResource("1")
[node name="TestRayCast2D" type="RayCast2D" parent="."]
script = SubResource("1")
[node name="StaticBody2D" type="StaticBody2D" parent="."]
position = Vector2(1, 52)
[node name="CollisionShape2D" type="CollisionShape2D" parent="StaticBody2D"]
shape = SubResource("3")

View File

@ -0,0 +1,235 @@
@tool
extends ISensor2D
class_name GridSensor2D
@export var debug_view := false:
get:
return debug_view
set(value):
debug_view = value
_update()
@export_flags_2d_physics var detection_mask := 0:
get:
return detection_mask
set(value):
detection_mask = value
_update()
@export var collide_with_areas := false:
get:
return collide_with_areas
set(value):
collide_with_areas = value
_update()
@export var collide_with_bodies := true:
get:
return collide_with_bodies
set(value):
collide_with_bodies = value
_update()
@export_range(1, 200, 0.1) var cell_width := 20.0:
get:
return cell_width
set(value):
cell_width = value
_update()
@export_range(1, 200, 0.1) var cell_height := 20.0:
get:
return cell_height
set(value):
cell_height = value
_update()
@export_range(1, 21, 2, "or_greater") var grid_size_x := 3:
get:
return grid_size_x
set(value):
grid_size_x = value
_update()
@export_range(1, 21, 2, "or_greater") var grid_size_y := 3:
get:
return grid_size_y
set(value):
grid_size_y = value
_update()
var _obs_buffer: PackedFloat64Array
var _rectangle_shape: RectangleShape2D
var _collision_mapping: Dictionary
var _n_layers_per_cell: int
var _highlighted_cell_color: Color
var _standard_cell_color: Color
func get_observation():
return _obs_buffer
func _update():
if Engine.is_editor_hint():
if is_node_ready():
_spawn_nodes()
func _ready() -> void:
_set_colors()
if Engine.is_editor_hint():
if get_child_count() == 0:
_spawn_nodes()
else:
_spawn_nodes()
func _set_colors() -> void:
_standard_cell_color = Color(100.0 / 255.0, 100.0 / 255.0, 100.0 / 255.0, 100.0 / 255.0)
_highlighted_cell_color = Color(255.0 / 255.0, 100.0 / 255.0, 100.0 / 255.0, 100.0 / 255.0)
func _get_collision_mapping() -> Dictionary:
# defines which layer is mapped to which cell obs index
var total_bits = 0
var collision_mapping = {}
for i in 32:
var bit_mask = 2 ** i
if (detection_mask & bit_mask) > 0:
collision_mapping[i] = total_bits
total_bits += 1
return collision_mapping
func _spawn_nodes():
for cell in get_children():
cell.name = "_%s" % cell.name # Otherwise naming below will fail
cell.queue_free()
_collision_mapping = _get_collision_mapping()
#prints("collision_mapping", _collision_mapping, len(_collision_mapping))
# allocate memory for the observations
_n_layers_per_cell = len(_collision_mapping)
_obs_buffer = PackedFloat64Array()
_obs_buffer.resize(grid_size_x * grid_size_y * _n_layers_per_cell)
_obs_buffer.fill(0)
#prints(len(_obs_buffer), _obs_buffer )
_rectangle_shape = RectangleShape2D.new()
_rectangle_shape.set_size(Vector2(cell_width, cell_height))
var shift := Vector2(
-(grid_size_x / 2) * cell_width,
-(grid_size_y / 2) * cell_height,
)
for i in grid_size_x:
for j in grid_size_y:
var cell_position = Vector2(i * cell_width, j * cell_height) + shift
_create_cell(i, j, cell_position)
func _create_cell(i: int, j: int, position: Vector2):
var cell := Area2D.new()
cell.position = position
cell.name = "GridCell %s %s" % [i, j]
cell.modulate = _standard_cell_color
if collide_with_areas:
cell.area_entered.connect(_on_cell_area_entered.bind(i, j))
cell.area_exited.connect(_on_cell_area_exited.bind(i, j))
if collide_with_bodies:
cell.body_entered.connect(_on_cell_body_entered.bind(i, j))
cell.body_exited.connect(_on_cell_body_exited.bind(i, j))
cell.collision_layer = 0
cell.collision_mask = detection_mask
cell.monitorable = true
add_child(cell)
cell.set_owner(get_tree().edited_scene_root)
var col_shape := CollisionShape2D.new()
col_shape.shape = _rectangle_shape
col_shape.name = "CollisionShape2D"
cell.add_child(col_shape)
col_shape.set_owner(get_tree().edited_scene_root)
if debug_view:
var quad = MeshInstance2D.new()
quad.name = "MeshInstance2D"
var quad_mesh = QuadMesh.new()
quad_mesh.set_size(Vector2(cell_width, cell_height))
quad.mesh = quad_mesh
cell.add_child(quad)
quad.set_owner(get_tree().edited_scene_root)
func _update_obs(cell_i: int, cell_j: int, collision_layer: int, entered: bool):
for key in _collision_mapping:
var bit_mask = 2 ** key
if (collision_layer & bit_mask) > 0:
var collison_map_index = _collision_mapping[key]
var obs_index = (
(cell_i * grid_size_x * _n_layers_per_cell)
+ (cell_j * _n_layers_per_cell)
+ collison_map_index
)
#prints(obs_index, cell_i, cell_j)
if entered:
_obs_buffer[obs_index] += 1
else:
_obs_buffer[obs_index] -= 1
func _toggle_cell(cell_i: int, cell_j: int):
var cell = get_node_or_null("GridCell %s %s" % [cell_i, cell_j])
if cell == null:
print("cell not found, returning")
var n_hits = 0
var start_index = (cell_i * grid_size_x * _n_layers_per_cell) + (cell_j * _n_layers_per_cell)
for i in _n_layers_per_cell:
n_hits += _obs_buffer[start_index + i]
if n_hits > 0:
cell.modulate = _highlighted_cell_color
else:
cell.modulate = _standard_cell_color
func _on_cell_area_entered(area: Area2D, cell_i: int, cell_j: int):
#prints("_on_cell_area_entered", cell_i, cell_j)
_update_obs(cell_i, cell_j, area.collision_layer, true)
if debug_view:
_toggle_cell(cell_i, cell_j)
#print(_obs_buffer)
func _on_cell_area_exited(area: Area2D, cell_i: int, cell_j: int):
#prints("_on_cell_area_exited", cell_i, cell_j)
_update_obs(cell_i, cell_j, area.collision_layer, false)
if debug_view:
_toggle_cell(cell_i, cell_j)
func _on_cell_body_entered(body: Node2D, cell_i: int, cell_j: int):
#prints("_on_cell_body_entered", cell_i, cell_j)
_update_obs(cell_i, cell_j, body.collision_layer, true)
if debug_view:
_toggle_cell(cell_i, cell_j)
func _on_cell_body_exited(body: Node2D, cell_i: int, cell_j: int):
#prints("_on_cell_body_exited", cell_i, cell_j)
_update_obs(cell_i, cell_j, body.collision_layer, false)
if debug_view:
_toggle_cell(cell_i, cell_j)

View File

@ -0,0 +1,25 @@
extends Node2D
class_name ISensor2D
var _obs: Array = []
var _active := false
func get_observation():
pass
func activate():
_active = true
func deactivate():
_active = false
func _update_observation():
pass
func reset():
pass

View File

@ -0,0 +1,118 @@
@tool
extends ISensor2D
class_name RaycastSensor2D
@export_flags_2d_physics var collision_mask := 1:
get:
return collision_mask
set(value):
collision_mask = value
_update()
@export var collide_with_areas := false:
get:
return collide_with_areas
set(value):
collide_with_areas = value
_update()
@export var collide_with_bodies := true:
get:
return collide_with_bodies
set(value):
collide_with_bodies = value
_update()
@export var n_rays := 16.0:
get:
return n_rays
set(value):
n_rays = value
_update()
@export_range(5, 3000, 5.0) var ray_length := 200:
get:
return ray_length
set(value):
ray_length = value
_update()
@export_range(5, 360, 5.0) var cone_width := 360.0:
get:
return cone_width
set(value):
cone_width = value
_update()
@export var debug_draw := true:
get:
return debug_draw
set(value):
debug_draw = value
_update()
var _angles = []
var rays := []
func _update():
if Engine.is_editor_hint():
if debug_draw:
_spawn_nodes()
else:
for ray in get_children():
if ray is RayCast2D:
remove_child(ray)
func _ready() -> void:
_spawn_nodes()
func _spawn_nodes():
for ray in rays:
ray.queue_free()
rays = []
_angles = []
var step = cone_width / (n_rays)
var start = step / 2 - cone_width / 2
for i in n_rays:
var angle = start + i * step
var ray = RayCast2D.new()
ray.set_target_position(
Vector2(ray_length * cos(deg_to_rad(angle)), ray_length * sin(deg_to_rad(angle)))
)
ray.set_name("node_" + str(i))
ray.enabled = false
ray.collide_with_areas = collide_with_areas
ray.collide_with_bodies = collide_with_bodies
ray.collision_mask = collision_mask
add_child(ray)
rays.append(ray)
_angles.append(start + i * step)
func get_observation() -> Array:
return self.calculate_raycasts()
func calculate_raycasts() -> Array:
var result = []
for ray in rays:
ray.enabled = true
ray.force_raycast_update()
var distance = _get_raycast_distance(ray)
result.append(distance)
ray.enabled = false
return result
func _get_raycast_distance(ray: RayCast2D) -> float:
if !ray.is_colliding():
return 0.0
var distance = (global_position - ray.get_collision_point()).length()
distance = clamp(distance, 0.0, ray_length)
return (ray_length - distance) / ray_length

View File

@ -0,0 +1,7 @@
[gd_scene load_steps=2 format=3 uid="uid://drvfihk5esgmv"]
[ext_resource type="Script" path="res://addons/godot_rl_agents/sensors/sensors_2d/RaycastSensor2D.gd" id="1"]
[node name="RaycastSensor2D" type="Node2D"]
script = ExtResource("1")
n_rays = 17.0

View File

@ -0,0 +1,6 @@
[gd_scene format=3 uid="uid://biu787qh4woik"]
[node name="ExampleRaycastSensor3D" type="Node3D"]
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.804183, 0, 2.70146)

View File

@ -0,0 +1,258 @@
@tool
extends ISensor3D
class_name GridSensor3D
@export var debug_view := false:
get:
return debug_view
set(value):
debug_view = value
_update()
@export_flags_3d_physics var detection_mask := 0:
get:
return detection_mask
set(value):
detection_mask = value
_update()
@export var collide_with_areas := false:
get:
return collide_with_areas
set(value):
collide_with_areas = value
_update()
@export var collide_with_bodies := false:
# NOTE! The sensor will not detect StaticBody3D, add an area to static bodies to detect them
get:
return collide_with_bodies
set(value):
collide_with_bodies = value
_update()
@export_range(0.1, 2, 0.1) var cell_width := 1.0:
get:
return cell_width
set(value):
cell_width = value
_update()
@export_range(0.1, 2, 0.1) var cell_height := 1.0:
get:
return cell_height
set(value):
cell_height = value
_update()
@export_range(1, 21, 1, "or_greater") var grid_size_x := 3:
get:
return grid_size_x
set(value):
grid_size_x = value
_update()
@export_range(1, 21, 1, "or_greater") var grid_size_z := 3:
get:
return grid_size_z
set(value):
grid_size_z = value
_update()
var _obs_buffer: PackedFloat64Array
var _box_shape: BoxShape3D
var _collision_mapping: Dictionary
var _n_layers_per_cell: int
var _highlighted_box_material: StandardMaterial3D
var _standard_box_material: StandardMaterial3D
func get_observation():
return _obs_buffer
func reset():
_obs_buffer.fill(0)
func _update():
if Engine.is_editor_hint():
if is_node_ready():
_spawn_nodes()
func _ready() -> void:
_make_materials()
if Engine.is_editor_hint():
if get_child_count() == 0:
_spawn_nodes()
else:
_spawn_nodes()
func _make_materials() -> void:
if _highlighted_box_material != null and _standard_box_material != null:
return
_standard_box_material = StandardMaterial3D.new()
_standard_box_material.set_transparency(1) # ALPHA
_standard_box_material.albedo_color = Color(
100.0 / 255.0, 100.0 / 255.0, 100.0 / 255.0, 100.0 / 255.0
)
_highlighted_box_material = StandardMaterial3D.new()
_highlighted_box_material.set_transparency(1) # ALPHA
_highlighted_box_material.albedo_color = Color(
255.0 / 255.0, 100.0 / 255.0, 100.0 / 255.0, 100.0 / 255.0
)
func _get_collision_mapping() -> Dictionary:
# defines which layer is mapped to which cell obs index
var total_bits = 0
var collision_mapping = {}
for i in 32:
var bit_mask = 2 ** i
if (detection_mask & bit_mask) > 0:
collision_mapping[i] = total_bits
total_bits += 1
return collision_mapping
func _spawn_nodes():
for cell in get_children():
cell.name = "_%s" % cell.name # Otherwise naming below will fail
cell.queue_free()
_collision_mapping = _get_collision_mapping()
#prints("collision_mapping", _collision_mapping, len(_collision_mapping))
# allocate memory for the observations
_n_layers_per_cell = len(_collision_mapping)
_obs_buffer = PackedFloat64Array()
_obs_buffer.resize(grid_size_x * grid_size_z * _n_layers_per_cell)
_obs_buffer.fill(0)
#prints(len(_obs_buffer), _obs_buffer )
_box_shape = BoxShape3D.new()
_box_shape.set_size(Vector3(cell_width, cell_height, cell_width))
var shift := Vector3(
-(grid_size_x / 2) * cell_width,
0,
-(grid_size_z / 2) * cell_width,
)
for i in grid_size_x:
for j in grid_size_z:
var cell_position = Vector3(i * cell_width, 0.0, j * cell_width) + shift
_create_cell(i, j, cell_position)
func _create_cell(i: int, j: int, position: Vector3):
var cell := Area3D.new()
cell.position = position
cell.name = "GridCell %s %s" % [i, j]
if collide_with_areas:
cell.area_entered.connect(_on_cell_area_entered.bind(i, j))
cell.area_exited.connect(_on_cell_area_exited.bind(i, j))
if collide_with_bodies:
cell.body_entered.connect(_on_cell_body_entered.bind(i, j))
cell.body_exited.connect(_on_cell_body_exited.bind(i, j))
# cell.body_shape_entered.connect(_on_cell_body_shape_entered.bind(i, j))
# cell.body_shape_exited.connect(_on_cell_body_shape_exited.bind(i, j))
cell.collision_layer = 0
cell.collision_mask = detection_mask
cell.monitorable = true
cell.input_ray_pickable = false
add_child(cell)
cell.set_owner(get_tree().edited_scene_root)
var col_shape := CollisionShape3D.new()
col_shape.shape = _box_shape
col_shape.name = "CollisionShape3D"
cell.add_child(col_shape)
col_shape.set_owner(get_tree().edited_scene_root)
if debug_view:
var box = MeshInstance3D.new()
box.name = "MeshInstance3D"
var box_mesh = BoxMesh.new()
box_mesh.set_size(Vector3(cell_width, cell_height, cell_width))
box_mesh.material = _standard_box_material
box.mesh = box_mesh
cell.add_child(box)
box.set_owner(get_tree().edited_scene_root)
func _update_obs(cell_i: int, cell_j: int, collision_layer: int, entered: bool):
for key in _collision_mapping:
var bit_mask = 2 ** key
if (collision_layer & bit_mask) > 0:
var collison_map_index = _collision_mapping[key]
var obs_index = (
(cell_i * grid_size_z * _n_layers_per_cell)
+ (cell_j * _n_layers_per_cell)
+ collison_map_index
)
#prints(obs_index, cell_i, cell_j)
if entered:
_obs_buffer[obs_index] += 1
else:
_obs_buffer[obs_index] -= 1
func _toggle_cell(cell_i: int, cell_j: int):
var cell = get_node_or_null("GridCell %s %s" % [cell_i, cell_j])
if cell == null:
print("cell not found, returning")
var n_hits = 0
var start_index = (cell_i * grid_size_z * _n_layers_per_cell) + (cell_j * _n_layers_per_cell)
for i in _n_layers_per_cell:
n_hits += _obs_buffer[start_index + i]
var cell_mesh = cell.get_node_or_null("MeshInstance3D")
if n_hits > 0:
cell_mesh.mesh.material = _highlighted_box_material
else:
cell_mesh.mesh.material = _standard_box_material
func _on_cell_area_entered(area: Area3D, cell_i: int, cell_j: int):
#prints("_on_cell_area_entered", cell_i, cell_j)
_update_obs(cell_i, cell_j, area.collision_layer, true)
if debug_view:
_toggle_cell(cell_i, cell_j)
#print(_obs_buffer)
func _on_cell_area_exited(area: Area3D, cell_i: int, cell_j: int):
#prints("_on_cell_area_exited", cell_i, cell_j)
_update_obs(cell_i, cell_j, area.collision_layer, false)
if debug_view:
_toggle_cell(cell_i, cell_j)
func _on_cell_body_entered(body: Node3D, cell_i: int, cell_j: int):
#prints("_on_cell_body_entered", cell_i, cell_j)
_update_obs(cell_i, cell_j, body.collision_layer, true)
if debug_view:
_toggle_cell(cell_i, cell_j)
func _on_cell_body_exited(body: Node3D, cell_i: int, cell_j: int):
#prints("_on_cell_body_exited", cell_i, cell_j)
_update_obs(cell_i, cell_j, body.collision_layer, false)
if debug_view:
_toggle_cell(cell_i, cell_j)

View File

@ -0,0 +1,25 @@
extends Node3D
class_name ISensor3D
var _obs: Array = []
var _active := false
func get_observation():
pass
func activate():
_active = true
func deactivate():
_active = false
func _update_observation():
pass
func reset():
pass

View File

@ -0,0 +1,63 @@
extends Node3D
class_name RGBCameraSensor3D
var camera_pixels = null
@onready var camera_texture := $Control/CameraTexture as Sprite2D
@onready var processed_texture := $Control/ProcessedTexture as Sprite2D
@onready var sub_viewport := $SubViewport as SubViewport
@onready var displayed_image: ImageTexture
@export var render_image_resolution := Vector2(36, 36)
## Display size does not affect rendered or sent image resolution.
## Scale is relative to either render image or downscale image resolution
## depending on which mode is set.
@export var displayed_image_scale_factor := Vector2(8, 8)
@export_group("Downscale image options")
## Enable to downscale the rendered image before sending the obs.
@export var downscale_image: bool = false
## If downscale_image is true, will display the downscaled image instead of rendered image.
@export var display_downscaled_image: bool = true
## This is the resolution of the image that will be sent after downscaling
@export var resized_image_resolution := Vector2(36, 36)
func _ready():
sub_viewport.size = render_image_resolution
camera_texture.scale = displayed_image_scale_factor
if downscale_image and display_downscaled_image:
camera_texture.visible = false
processed_texture.scale = displayed_image_scale_factor
else:
processed_texture.visible = false
func get_camera_pixel_encoding():
var image := camera_texture.get_texture().get_image() as Image
if downscale_image:
image.resize(
resized_image_resolution.x, resized_image_resolution.y, Image.INTERPOLATE_NEAREST
)
if display_downscaled_image:
if not processed_texture.texture:
displayed_image = ImageTexture.create_from_image(image)
processed_texture.texture = displayed_image
else:
displayed_image.update(image)
return image.get_data().hex_encode()
func get_camera_shape() -> Array:
var size = resized_image_resolution if downscale_image else render_image_resolution
assert(
size.x >= 36 and size.y >= 36,
"Camera sensor sent image resolution must be 36x36 or larger."
)
if sub_viewport.transparent_bg:
return [4, size.y, size.x]
else:
return [3, size.y, size.x]

View File

@ -0,0 +1,35 @@
[gd_scene load_steps=3 format=3 uid="uid://baaywi3arsl2m"]
[ext_resource type="Script" path="res://addons/godot_rl_agents/sensors/sensors_3d/RGBCameraSensor3D.gd" id="1"]
[sub_resource type="ViewportTexture" id="ViewportTexture_y72s3"]
viewport_path = NodePath("SubViewport")
[node name="RGBCameraSensor3D" type="Node3D"]
script = ExtResource("1")
[node name="RemoteTransform" type="RemoteTransform3D" parent="."]
remote_path = NodePath("../SubViewport/Camera")
[node name="SubViewport" type="SubViewport" parent="."]
size = Vector2i(36, 36)
render_target_update_mode = 3
[node name="Camera" type="Camera3D" parent="SubViewport"]
near = 0.5
[node name="Control" type="Control" parent="."]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
metadata/_edit_use_anchors_ = true
[node name="CameraTexture" type="Sprite2D" parent="Control"]
texture = SubResource("ViewportTexture_y72s3")
centered = false
[node name="ProcessedTexture" type="Sprite2D" parent="Control"]
centered = false

View File

@ -0,0 +1,185 @@
@tool
extends ISensor3D
class_name RayCastSensor3D
@export_flags_3d_physics var collision_mask = 1:
get:
return collision_mask
set(value):
collision_mask = value
_update()
@export_flags_3d_physics var boolean_class_mask = 1:
get:
return boolean_class_mask
set(value):
boolean_class_mask = value
_update()
@export var n_rays_width := 6.0:
get:
return n_rays_width
set(value):
n_rays_width = value
_update()
@export var n_rays_height := 6.0:
get:
return n_rays_height
set(value):
n_rays_height = value
_update()
@export var ray_length := 10.0:
get:
return ray_length
set(value):
ray_length = value
_update()
@export var cone_width := 60.0:
get:
return cone_width
set(value):
cone_width = value
_update()
@export var cone_height := 60.0:
get:
return cone_height
set(value):
cone_height = value
_update()
@export var collide_with_areas := false:
get:
return collide_with_areas
set(value):
collide_with_areas = value
_update()
@export var collide_with_bodies := true:
get:
return collide_with_bodies
set(value):
collide_with_bodies = value
_update()
@export var class_sensor := false
var rays := []
var geo = null
func _update():
if Engine.is_editor_hint():
if is_node_ready():
_spawn_nodes()
func _ready() -> void:
if Engine.is_editor_hint():
if get_child_count() == 0:
_spawn_nodes()
else:
_spawn_nodes()
func _spawn_nodes():
print("spawning nodes")
for ray in get_children():
ray.queue_free()
if geo:
geo.clear()
#$Lines.remove_points()
rays = []
var horizontal_step = cone_width / (n_rays_width)
var vertical_step = cone_height / (n_rays_height)
var horizontal_start = horizontal_step / 2 - cone_width / 2
var vertical_start = vertical_step / 2 - cone_height / 2
var points = []
for i in n_rays_width:
for j in n_rays_height:
var angle_w = horizontal_start + i * horizontal_step
var angle_h = vertical_start + j * vertical_step
#angle_h = 0.0
var ray = RayCast3D.new()
var cast_to = to_spherical_coords(ray_length, angle_w, angle_h)
ray.set_target_position(cast_to)
points.append(cast_to)
ray.set_name("node_" + str(i) + " " + str(j))
ray.enabled = true
ray.collide_with_bodies = collide_with_bodies
ray.collide_with_areas = collide_with_areas
ray.collision_mask = collision_mask
add_child(ray)
ray.set_owner(get_tree().edited_scene_root)
rays.append(ray)
ray.force_raycast_update()
# if Engine.editor_hint:
# _create_debug_lines(points)
func _create_debug_lines(points):
if not geo:
geo = ImmediateMesh.new()
add_child(geo)
geo.clear()
geo.begin(Mesh.PRIMITIVE_LINES)
for point in points:
geo.set_color(Color.AQUA)
geo.add_vertex(Vector3.ZERO)
geo.add_vertex(point)
geo.end()
func display():
if geo:
geo.display()
func to_spherical_coords(r, inc, azimuth) -> Vector3:
return Vector3(
r * sin(deg_to_rad(inc)) * cos(deg_to_rad(azimuth)),
r * sin(deg_to_rad(azimuth)),
r * cos(deg_to_rad(inc)) * cos(deg_to_rad(azimuth))
)
func get_observation() -> Array:
return self.calculate_raycasts()
func calculate_raycasts() -> Array:
var result = []
for ray in rays:
ray.set_enabled(true)
ray.force_raycast_update()
var distance = _get_raycast_distance(ray)
result.append(distance)
if class_sensor:
var hit_class: float = 0
if ray.get_collider():
var hit_collision_layer = ray.get_collider().collision_layer
hit_collision_layer = hit_collision_layer & collision_mask
hit_class = (hit_collision_layer & boolean_class_mask) > 0
result.append(float(hit_class))
ray.set_enabled(false)
return result
func _get_raycast_distance(ray: RayCast3D) -> float:
if !ray.is_colliding():
return 0.0
var distance = (global_transform.origin - ray.get_collision_point()).length()
distance = clamp(distance, 0.0, ray_length)
return (ray_length - distance) / ray_length

View File

@ -0,0 +1,27 @@
[gd_scene load_steps=2 format=3 uid="uid://b803cbh1fmy66"]
[ext_resource type="Script" path="res://addons/godot_rl_agents/sensors/sensors_3d/RaycastSensor3D.gd" id="1"]
[node name="RaycastSensor3D" type="Node3D"]
script = ExtResource("1")
n_rays_width = 4.0
n_rays_height = 2.0
ray_length = 11.0
[node name="node_1 0" type="RayCast3D" parent="."]
target_position = Vector3(-1.38686, -2.84701, 10.5343)
[node name="node_1 1" type="RayCast3D" parent="."]
target_position = Vector3(-1.38686, 2.84701, 10.5343)
[node name="node_2 0" type="RayCast3D" parent="."]
target_position = Vector3(1.38686, -2.84701, 10.5343)
[node name="node_2 1" type="RayCast3D" parent="."]
target_position = Vector3(1.38686, 2.84701, 10.5343)
[node name="node_3 0" type="RayCast3D" parent="."]
target_position = Vector3(4.06608, -2.84701, 9.81639)
[node name="node_3 1" type="RayCast3D" parent="."]
target_position = Vector3(4.06608, 2.84701, 9.81639)