diff --git a/2d/navigation_astar/Game.tscn b/2d/navigation_astar/Game.tscn new file mode 100644 index 00000000..63893b2e --- /dev/null +++ b/2d/navigation_astar/Game.tscn @@ -0,0 +1,31 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://tileset/tileset.tres" type="TileSet" id=1] +[ext_resource path="res://pathfind_astar.gd" type="Script" id=2] + +[node name="Game" type="Node" index="0"] + +[node name="TileMap" type="TileMap" parent="." index="0"] + +mode = 0 +tile_set = ExtResource( 1 ) +cell_size = Vector2( 64, 64 ) +cell_quadrant_size = 16 +cell_custom_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) +cell_half_offset = 2 +cell_tile_origin = 0 +cell_y_sort = false +cell_clip_uv = false +collision_use_kinematic = false +collision_friction = 1.0 +collision_bounce = 0.0 +collision_layer = 1 +collision_mask = 1 +occluder_light_mask = 1 +format = 1 +tile_data = PoolIntArray( 65537, 0, 0, 65541, 0, 0, 65545, 0, 0, 65550, 0, 0, 196614, 0, 0, 196615, 0, 0, 196616, 0, 0, 196617, 0, 0, 196618, 0, 0, 196619, 0, 0, 262145, 0, 0, 262146, 0, 0, 262147, 0, 0, 327683, 0, 0, 393219, 0, 0, 393220, 0, 0, 393221, 0, 0, 393222, 0, 0, 393226, 0, 0, 393227, 0, 0, 393228, 0, 0, 458761, 0, 0, 524290, 0, 0, 524291, 0, 0, 524292, 0, 0, 524293, 0, 0, 524294, 0, 0, 524295, 0, 0, 524296, 0, 0, 524297, 0, 0 ) +script = ExtResource( 2 ) +_sections_unfolded = [ "Cell" ] +map_size = Vector2( 16, 16 ) + + diff --git a/2d/navigation_astar/default_env.tres b/2d/navigation_astar/default_env.tres new file mode 100644 index 00000000..ad86b722 --- /dev/null +++ b/2d/navigation_astar/default_env.tres @@ -0,0 +1,101 @@ +[gd_resource type="Environment" load_steps=2 format=2] + +[sub_resource type="ProceduralSky" id=1] + +radiance_size = 4 +sky_top_color = Color( 0.0470588, 0.454902, 0.976471, 1 ) +sky_horizon_color = Color( 0.556863, 0.823529, 0.909804, 1 ) +sky_curve = 0.25 +sky_energy = 1.0 +ground_bottom_color = Color( 0.101961, 0.145098, 0.188235, 1 ) +ground_horizon_color = Color( 0.482353, 0.788235, 0.952941, 1 ) +ground_curve = 0.01 +ground_energy = 1.0 +sun_color = Color( 1, 1, 1, 1 ) +sun_latitude = 35.0 +sun_longitude = 0.0 +sun_angle_min = 1.0 +sun_angle_max = 100.0 +sun_curve = 0.05 +sun_energy = 16.0 +texture_size = 2 + +[resource] + +background_mode = 2 +background_sky = SubResource( 1 ) +background_sky_custom_fov = 0.0 +background_color = Color( 0, 0, 0, 1 ) +background_energy = 1.0 +background_canvas_max_layer = 0 +ambient_light_color = Color( 0, 0, 0, 1 ) +ambient_light_energy = 1.0 +ambient_light_sky_contribution = 1.0 +fog_enabled = false +fog_color = Color( 0.5, 0.6, 0.7, 1 ) +fog_sun_color = Color( 1, 0.9, 0.7, 1 ) +fog_sun_amount = 0.0 +fog_depth_enabled = true +fog_depth_begin = 10.0 +fog_depth_curve = 1.0 +fog_transmit_enabled = false +fog_transmit_curve = 1.0 +fog_height_enabled = false +fog_height_min = 0.0 +fog_height_max = 100.0 +fog_height_curve = 1.0 +tonemap_mode = 0 +tonemap_exposure = 1.0 +tonemap_white = 1.0 +auto_exposure_enabled = false +auto_exposure_scale = 0.4 +auto_exposure_min_luma = 0.05 +auto_exposure_max_luma = 8.0 +auto_exposure_speed = 0.5 +ss_reflections_enabled = false +ss_reflections_max_steps = 64 +ss_reflections_fade_in = 0.15 +ss_reflections_fade_out = 2.0 +ss_reflections_depth_tolerance = 0.2 +ss_reflections_roughness = true +ssao_enabled = false +ssao_radius = 1.0 +ssao_intensity = 1.0 +ssao_radius2 = 0.0 +ssao_intensity2 = 1.0 +ssao_bias = 0.01 +ssao_light_affect = 0.0 +ssao_color = Color( 0, 0, 0, 1 ) +ssao_quality = 0 +ssao_blur = 3 +ssao_edge_sharpness = 4.0 +dof_blur_far_enabled = false +dof_blur_far_distance = 10.0 +dof_blur_far_transition = 5.0 +dof_blur_far_amount = 0.1 +dof_blur_far_quality = 1 +dof_blur_near_enabled = false +dof_blur_near_distance = 2.0 +dof_blur_near_transition = 1.0 +dof_blur_near_amount = 0.1 +dof_blur_near_quality = 1 +glow_enabled = false +glow_levels/1 = false +glow_levels/2 = false +glow_levels/3 = true +glow_levels/4 = false +glow_levels/5 = true +glow_levels/6 = false +glow_levels/7 = false +glow_intensity = 0.8 +glow_strength = 1.0 +glow_bloom = 0.0 +glow_blend_mode = 2 +glow_hdr_threshold = 1.0 +glow_hdr_scale = 2.0 +glow_bicubic_upscale = false +adjustment_enabled = false +adjustment_brightness = 1.0 +adjustment_contrast = 1.0 +adjustment_saturation = 1.0 + diff --git a/2d/navigation_astar/icon.png b/2d/navigation_astar/icon.png new file mode 100644 index 00000000..b3602483 Binary files /dev/null and b/2d/navigation_astar/icon.png differ diff --git a/2d/navigation_astar/icon.png.import b/2d/navigation_astar/icon.png.import new file mode 100644 index 00000000..e4c4a984 --- /dev/null +++ b/2d/navigation_astar/icon.png.import @@ -0,0 +1,32 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" + +[deps] + +source_file="res://icon.png" +source_md5="0b0cb045ce690875fab066adeea76aba" + +dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] +dest_md5="58323f97585db7bd8d6e03320f9ebb33" + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/2d/navigation_astar/pathfind_astar.gd b/2d/navigation_astar/pathfind_astar.gd new file mode 100644 index 00000000..6d2f558b --- /dev/null +++ b/2d/navigation_astar/pathfind_astar.gd @@ -0,0 +1,174 @@ +extends TileMap + +# You can only create an AStar node from code, not from the Scene tab +onready var astar_node = AStar.new() +# The Tilemap node doesn't have clear bounds so we're defining the map's limits here +export(Vector2) var map_size = Vector2(16, 16) + +# The path start and end variables use setter methods +# You can find them at the bottom of the script +var path_start_position = Vector2() setget _set_path_start_position +var path_end_position = Vector2() setget _set_path_end_position + +var _point_path = [] + +const BASE_LINE_WIDTH = 3.0 +const DRAW_COLOR = Color('#fff') + +# get_used_cells_by_id is a method from the TileMap node +# here the id 0 corresponds to the grey tile, the obstacles +onready var obstacles = get_used_cells_by_id(0) +onready var _half_cell_size = cell_size / 2 + +func _ready(): + var walkable_cells_list = astar_add_walkable_cells(obstacles) + astar_connect_walkable_cells(walkable_cells_list) + + +# Click and Shift force the start and end position of the path to update +# and the node to redraw everything +func _input(event): + if event.is_action_pressed('click') and Input.is_key_pressed(KEY_SHIFT): + # To call the setter method from this script we have to use the explicit self. + self.path_start_position = world_to_map(get_global_mouse_position()) + elif event.is_action_pressed('click'): + self.path_end_position = world_to_map(get_global_mouse_position()) + + +# Loops through all cells within the map's bounds and +# adds all points to the astar_node, except the obstacles +func astar_add_walkable_cells(obstacles = []): + var points_array = [] + for y in range(map_size.y): + for x in range(map_size.x): + var point = Vector2(x, y) + if point in obstacles: + continue + + points_array.append(point) + # The AStar class references points with indices + # Using a function to calculate the index from a point's coordinates + # ensures we always get the same index with the same input point + var point_index = calculate_point_index(point) + # AStar works for both 2d and 3d, so we have to convert the point + # coordinates from and to Vector3s + astar_node.add_point(point_index, Vector3(point.x, point.y, 0.0)) + return points_array + + +# Once you added all points to the AStar node, you've got to connect them +# The points don't have to be on a grid: you can use this class +# to create walkable graphs however you'd like +# It's a little harder to code at first, but works for 2d, 3d, +# orthogonal grids, hex grids, tower defense games... +func astar_connect_walkable_cells(points_array): + for point in points_array: + var point_index = calculate_point_index(point) + # For every cell in the map, we check the one to the top, right. + # left and bottom of it. If it's in the map and not an obstalce, + # We connect the current point with it + var points_relative = PoolVector2Array([ + Vector2(point.x + 1, point.y), + Vector2(point.x - 1, point.y), + Vector2(point.x, point.y + 1), + Vector2(point.x, point.y - 1)]) + for point_relative in points_relative: + var point_relative_index = calculate_point_index(point_relative) + + if is_outside_map_bounds(point_relative): + continue + if not astar_node.has_point(point_relative_index): + continue + # Note the 3rd argument. It tells the astar_node that we want the + # connection to be bilateral: from point A to B and B to A + # If you set this value to false, it becomes a one-way path + astar_node.connect_points(point_index, point_relative_index, true) + + +# This is a variation of the method above +# It connects cells horizontally, vertically AND diagonally +func astar_connect_walkable_cells_diagonal(points_array): + for point in points_array: + var point_index = calculate_point_index(point) + for local_y in range(3): + for local_x in range(3): + var point_relative = Vector2(point.x + local_x - 1, point.y + local_y - 1) + var point_relative_index = calculate_point_index(point_relative) + + if point_relative == point or is_outside_map_bounds(point_relative): + continue + if not astar_node.has_point(point_relative_index): + continue + astar_node.connect_points(point_index, point_relative_index, true) + + +func is_outside_map_bounds(point): + return point.x < 0 or point.y < 0 or point.x >= map_size.x or point.y >= map_size.y + + +func calculate_point_index(point): + return point.x + map_size.x * point.y + + +func recalculate_path(): + clear_previous_path_drawing() + var start_point_index = calculate_point_index(path_start_position) + var end_point_index = calculate_point_index(path_end_position) + # This method gives us an array of points. Note you need the start and end + # points' indices as input + _point_path = astar_node.get_point_path(start_point_index, end_point_index) + # Redraw the lines and circles from the start to the end point + update() + + +func clear_previous_path_drawing(): + if not _point_path: + return + var point_start = _point_path[0] + var point_end = _point_path[len(_point_path) - 1] + set_cell(point_start.x, point_start.y, -1) + set_cell(point_end.x, point_end.y, -1) + + +func _draw(): + if not _point_path: + return + var point_start = _point_path[0] + var point_end = _point_path[len(_point_path) - 1] + + set_cell(point_start.x, point_start.y, 1) + set_cell(point_end.x, point_end.y, 2) + + var last_point = map_to_world(Vector2(point_start.x, point_start.y)) + _half_cell_size + for index in range(1, len(_point_path)): + var current_point = map_to_world(Vector2(_point_path[index].x, _point_path[index].y)) + _half_cell_size + draw_line(last_point, current_point, DRAW_COLOR, BASE_LINE_WIDTH, true) + draw_circle(current_point, BASE_LINE_WIDTH * 2.0, DRAW_COLOR) + last_point = current_point + + +# Setters for the start and end path values. +func _set_path_start_position(value): + if value in obstacles: + return + if is_outside_map_bounds(value): + return + + set_cell(path_start_position.x, path_start_position.y, -1) + set_cell(value.x, value.y, 1) + path_start_position = value + if path_end_position and path_end_position != path_start_position: + recalculate_path() + + +func _set_path_end_position(value): + if value in obstacles: + return + if is_outside_map_bounds(value): + return + + set_cell(path_start_position.x, path_start_position.y, -1) + set_cell(value.x, value.y, 2) + path_end_position = value + if path_start_position != value: + recalculate_path() \ No newline at end of file diff --git a/2d/navigation_astar/project.godot b/2d/navigation_astar/project.godot new file mode 100644 index 00000000..14a2c95b --- /dev/null +++ b/2d/navigation_astar/project.godot @@ -0,0 +1,24 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=3 + +[application] + +config/name="Grid-based pathfinding with Astar" +run/main_scene="res://Game.tscn" +config/icon="res://icon.png" + +[input] + +click=[ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":1,"pressed":false,"doubleclick":false,"script":null) + ] + +[rendering] + +environment/default_environment="res://default_env.tres" diff --git a/2d/navigation_astar/sprites/obstacle.png b/2d/navigation_astar/sprites/obstacle.png new file mode 100644 index 00000000..73580809 Binary files /dev/null and b/2d/navigation_astar/sprites/obstacle.png differ diff --git a/2d/navigation_astar/sprites/obstacle.png.import b/2d/navigation_astar/sprites/obstacle.png.import new file mode 100644 index 00000000..030d8116 --- /dev/null +++ b/2d/navigation_astar/sprites/obstacle.png.import @@ -0,0 +1,32 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/obstacle.png-0258c5f5ce65bfa0dd8610adeb784f54.stex" + +[deps] + +source_file="res://sprites/obstacle.png" +source_md5="f741dc1724960a029cba51c962d988be" + +dest_files=[ "res://.import/obstacle.png-0258c5f5ce65bfa0dd8610adeb784f54.stex" ] +dest_md5="432d869b849ac70d4f76935622b2e37b" + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/2d/navigation_astar/sprites/path_end.png b/2d/navigation_astar/sprites/path_end.png new file mode 100644 index 00000000..bb0e7573 Binary files /dev/null and b/2d/navigation_astar/sprites/path_end.png differ diff --git a/2d/navigation_astar/sprites/path_end.png.import b/2d/navigation_astar/sprites/path_end.png.import new file mode 100644 index 00000000..81d2d571 --- /dev/null +++ b/2d/navigation_astar/sprites/path_end.png.import @@ -0,0 +1,32 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/path_end.png-02b79e25892cd8d863bd44c3c5a1720e.stex" + +[deps] + +source_file="res://sprites/path_end.png" +source_md5="be9537ddfec33fdc35a33d88cad94d38" + +dest_files=[ "res://.import/path_end.png-02b79e25892cd8d863bd44c3c5a1720e.stex" ] +dest_md5="436b174ce46135e1d4d6b9b238889433" + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/2d/navigation_astar/sprites/path_start.png b/2d/navigation_astar/sprites/path_start.png new file mode 100644 index 00000000..0125bdaa Binary files /dev/null and b/2d/navigation_astar/sprites/path_start.png differ diff --git a/2d/navigation_astar/sprites/path_start.png.import b/2d/navigation_astar/sprites/path_start.png.import new file mode 100644 index 00000000..3c7e90fe --- /dev/null +++ b/2d/navigation_astar/sprites/path_start.png.import @@ -0,0 +1,32 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/path_start.png-475bd0b469629aa8779d506c5134e5c2.stex" + +[deps] + +source_file="res://sprites/path_start.png" +source_md5="6b4a1211e21f4fab937c7b309d802a44" + +dest_files=[ "res://.import/path_start.png-475bd0b469629aa8779d506c5134e5c2.stex" ] +dest_md5="5d90014ca7f70fa5f9494bd25fbe72d0" + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/2d/navigation_astar/tileset/tileset.tres b/2d/navigation_astar/tileset/tileset.tres new file mode 100644 index 00000000..54fb6b18 --- /dev/null +++ b/2d/navigation_astar/tileset/tileset.tres @@ -0,0 +1,37 @@ +[gd_resource type="TileSet" load_steps=4 format=2] + +[ext_resource path="res://sprites/obstacle.png" type="Texture" id=1] +[ext_resource path="res://sprites/path_start.png" type="Texture" id=2] +[ext_resource path="res://sprites/path_end.png" type="Texture" id=3] + +[resource] + +0/name = "Obstacle" +0/texture = ExtResource( 1 ) +0/tex_offset = Vector2( 0, 0 ) +0/modulate = Color( 1, 1, 1, 1 ) +0/region = Rect2( 0, 0, 64, 64 ) +0/is_autotile = false +0/occluder_offset = Vector2( 32, 32 ) +0/navigation_offset = Vector2( 32, 32 ) +0/shapes = [ ] +1/name = "PathStart" +1/texture = ExtResource( 2 ) +1/tex_offset = Vector2( 0, 0 ) +1/modulate = Color( 1, 1, 1, 1 ) +1/region = Rect2( 0, 0, 64, 64 ) +1/is_autotile = false +1/occluder_offset = Vector2( 32, 32 ) +1/navigation_offset = Vector2( 32, 32 ) +1/shapes = [ ] +2/name = "PathEnd" +2/texture = ExtResource( 3 ) +2/tex_offset = Vector2( 0, 0 ) +2/modulate = Color( 1, 1, 1, 1 ) +2/region = Rect2( 0, 0, 64, 64 ) +2/is_autotile = false +2/occluder_offset = Vector2( 32, 32 ) +2/navigation_offset = Vector2( 32, 32 ) +2/shapes = [ ] +_sections_unfolded = [ "2" ] + diff --git a/2d/navigation_astar/tileset/tileset_source.tscn b/2d/navigation_astar/tileset/tileset_source.tscn new file mode 100644 index 00000000..931ba7c4 --- /dev/null +++ b/2d/navigation_astar/tileset/tileset_source.tscn @@ -0,0 +1,24 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://sprites/obstacle.png" type="Texture" id=1] +[ext_resource path="res://sprites/path_start.png" type="Texture" id=2] +[ext_resource path="res://sprites/path_end.png" type="Texture" id=3] + +[node name="Node2D" type="Node2D" index="0"] + +[node name="Obstacle" type="Sprite" parent="." index="0"] + +position = Vector2( 32, 32 ) +texture = ExtResource( 1 ) + +[node name="PathStart" type="Sprite" parent="." index="1"] + +position = Vector2( 112, 32 ) +texture = ExtResource( 2 ) + +[node name="PathEnd" type="Sprite" parent="." index="2"] + +position = Vector2( 192, 32 ) +texture = ExtResource( 3 ) + +