diff --git a/scene/attack.tscn b/scene/attack.tscn new file mode 100644 index 0000000..d6ef3c8 --- /dev/null +++ b/scene/attack.tscn @@ -0,0 +1,32 @@ +[gd_scene load_steps=3 format=3 uid="uid://cq2qq7q4u7mx1"] + +[ext_resource type="Script" path="res://scripts/attack.gd" id="1_ag1vs"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_0cret"] +radius = 16.0 + +[node name="Attack" type="Node2D"] +script = ExtResource("1_ag1vs") + +[node name="CPUParticles2D" type="CPUParticles2D" parent="."] +emitting = false +amount = 120 +lifetime = 0.25 +one_shot = true +explosiveness = 1.0 +randomness = 0.05 +emission_shape = 1 +emission_sphere_radius = 4.0 +spread = 180.0 +gravity = Vector2(0, 0) +initial_velocity_min = 56.0 +initial_velocity_max = 60.0 + +[node name="Area2D" type="Area2D" parent="."] +collision_layer = 0 +collision_mask = 2 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"] +shape = SubResource("CircleShape2D_0cret") + +[connection signal="finished" from="CPUParticles2D" to="." method="_on_cpu_particles_2d_finished"] diff --git a/scene/chest.tscn b/scene/chest.tscn index a56989c..9465cc2 100644 --- a/scene/chest.tscn +++ b/scene/chest.tscn @@ -14,3 +14,6 @@ texture = ExtResource("1_dmyxo") [node name="CollisionShape2D" type="CollisionShape2D" parent="."] shape = SubResource("RectangleShape2D_miucj") + +[node name="NavigationObstacle2D" type="NavigationObstacle2D" parent="."] +vertices = PackedVector2Array(5, 5, -5, 5, -5, -5, 5, -5) diff --git a/scene/enemy.tscn b/scene/enemy.tscn index 3751314..aea825c 100644 --- a/scene/enemy.tscn +++ b/scene/enemy.tscn @@ -7,6 +7,7 @@ radius = 6.0 [node name="Enemy" type="CharacterBody2D"] +collision_layer = 2 script = ExtResource("1_a6qhu") [node name="Sprite2D" type="Sprite2D" parent="."] @@ -14,3 +15,9 @@ texture = ExtResource("2_jkluv") [node name="CollisionShape2D" type="CollisionShape2D" parent="."] shape = SubResource("CircleShape2D_c7wcn") + +[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."] +target_desired_distance = 16.0 +path_postprocessing = 1 +avoidance_enabled = true +radius = 8.0 diff --git a/scene/hud.tscn b/scene/hud.tscn index 4b2cc70..29fbbb7 100644 --- a/scene/hud.tscn +++ b/scene/hud.tscn @@ -89,3 +89,20 @@ step = 0.1 value = 1.0 fill_mode = 3 show_percentage = false + +[node name="EntityCountContainer" type="PanelContainer" parent="."] +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -40.0 +offset_bottom = 40.0 +grow_horizontal = 0 + +[node name="MarginContainer" type="MarginContainer" parent="EntityCountContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_right = 8 + +[node name="EntityCountLabel" type="Label" parent="EntityCountContainer/MarginContainer"] +layout_mode = 2 +text = "Entity Count: 0" diff --git a/scene/maze_1.tscn b/scene/maze_1.tscn index f47f35f..b50e80d 100644 --- a/scene/maze_1.tscn +++ b/scene/maze_1.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=11 format=3 uid="uid://b2amoasy4e08i"] +[gd_scene load_steps=24 format=3 uid="uid://b2amoasy4e08i"] [ext_resource type="Texture2D" uid="uid://bsrmtons1ov7x" path="res://assets/maze.png" id="1_jp5wf"] [ext_resource type="Script" path="res://scripts/maze.gd" id="1_u2iw0"] @@ -8,63 +8,168 @@ [ext_resource type="Resource" uid="uid://bo7k3kmsq57xy" path="res://data/buffs/green_buff.tres" id="5_ho55u"] [ext_resource type="Resource" uid="uid://tiv2o1xk5g7e" path="res://data/buffs/red_buff.tres" id="6_ll7oj"] +[sub_resource type="NavigationPolygon" id="NavigationPolygon_6s0ud"] +vertices = PackedVector2Array(8, 8, 2, 8, 2, 2, 8, 2) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(2, 2, 8, 2, 8, 8, 2, 8)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_em7y3"] +vertices = PackedVector2Array(8, 8, 2, 8, 2, -8, 8, -8) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(2, -8, 8, -8, 8, 8, 2, 8)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_106v7"] +vertices = PackedVector2Array(8, -2, 2, -2, 2, -8, 8, -8) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(2, -8, 8, -8, 8, -2, 2, -2)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_ywxsi"] +vertices = PackedVector2Array(8, 8, -8, 8, -8, 2, 8, 2) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-8, 2, 8, 2, 8, 8, -8, 8)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_xtsht"] +vertices = PackedVector2Array(8, 8, -8, 8, -8, -8, 8, -8) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-8, -8, 8, -8, 8, 8, -8, 8)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_l7kt7"] +vertices = PackedVector2Array(8, -2, -8, -2, -8, -8, 8, -8) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-8, -8, 8, -8, 8, -2, -8, -2)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_sbwbv"] +vertices = PackedVector2Array(-2, 8, -8, 8, -8, 2, -2, 2) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-8, 2, -2, 2, -2, 8, -8, 8)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_amdei"] +vertices = PackedVector2Array(-2, 8, -8, 8, -8, -8, -2, -8) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-8, -8, -2, -8, -2, 8, -8, 8)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_fuoof"] +vertices = PackedVector2Array(-2, -2, -8, -2, -8, -8, -2, -8) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-8, -8, -2, -8, -2, -2, -8, -2)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_tco5v"] +vertices = PackedVector2Array(8, -8, 8, -2, -2, -2, -8, -8, -8, 8, -2, 8) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 3, 2, 5)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(8, -2, -2, -2, -2, 8, -8, 8, -8, -8, 8, -8)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_tvjsp"] +vertices = PackedVector2Array(-2, 2, 8, 2, 8, 8, -8, 8, -8, -8, -2, -8) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(0, 3, 4, 5)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-2, -8, -2, 2, 8, 2, 8, 8, -8, 8, -8, -8)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_pqq6a"] +vertices = PackedVector2Array(8, -8, 8, 8, 2, 8, 2, -2, -8, -8, -8, -2) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 0, 3, 5)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(2, 8, 2, -2, -8, -2, -8, -8, 8, -8, 8, 8)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_mlmli"] +vertices = PackedVector2Array(8, 8, -8, 8, -8, 2, 2, 2, 8, -8, 2, -8) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 0, 3, 5)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-8, 2, 2, 2, 2, -8, 8, -8, 8, 8, -8, 8)]) +source_geometry_group_name = &"navigation_polygon_source_group" +agent_radius = 0.0 + [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_e47fw"] texture = ExtResource("1_jp5wf") 0:0/0 = 0 0:0/0/physics_layer_0/linear_velocity = Vector2(0, 0) 0:0/0/physics_layer_0/angular_velocity = 0.0 0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(8, -8, 8, -6, -6, -6, -6, 8, -8, 8, -8, -8) +0:0/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_6s0ud") 1:0/0 = 0 1:0/0/physics_layer_0/linear_velocity = Vector2(0, 0) 1:0/0/physics_layer_0/angular_velocity = 0.0 1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(8, -8, 8, -6, -8, -6, -8, -8) +1:0/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_ywxsi") 2:0/0 = 0 2:0/0/physics_layer_0/linear_velocity = Vector2(0, 0) 2:0/0/physics_layer_0/angular_velocity = 0.0 2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(8, 8, 6, 8, 6, -6, -8, -6, -8, -8, 8, -8) +2:0/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_sbwbv") 0:1/0 = 0 0:1/0/physics_layer_0/linear_velocity = Vector2(0, 0) 0:1/0/physics_layer_0/angular_velocity = 0.0 0:1/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, -6, -8, -6, 8, -8, 8) +0:1/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_em7y3") 1:1/0 = 0 1:1/0/physics_layer_0/linear_velocity = Vector2(0, 0) 1:1/0/physics_layer_0/angular_velocity = 0.0 +1:1/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_xtsht") 2:1/0 = 0 2:1/0/physics_layer_0/linear_velocity = Vector2(0, 0) 2:1/0/physics_layer_0/angular_velocity = 0.0 2:1/0/physics_layer_0/polygon_0/points = PackedVector2Array(8, -8, 6, -8, 6, 8, 8, 8) +2:1/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_amdei") 0:2/0 = 0 0:2/0/physics_layer_0/linear_velocity = Vector2(0, 0) 0:2/0/physics_layer_0/angular_velocity = 0.0 0:2/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, -6, -8, -6, 6, 8, 6, 8, 8, -8, 8) +0:2/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_106v7") 1:2/0 = 0 1:2/0/physics_layer_0/linear_velocity = Vector2(0, 0) 1:2/0/physics_layer_0/angular_velocity = 0.0 1:2/0/physics_layer_0/polygon_0/points = PackedVector2Array(8, 8, 8, 6, -8, 6, -8, 8) +1:2/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_l7kt7") 2:2/0 = 0 2:2/0/physics_layer_0/linear_velocity = Vector2(0, 0) 2:2/0/physics_layer_0/angular_velocity = 0.0 2:2/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, 8, -8, 6, 6, 6, 6, -8, 8, -8, 8, 8) +2:2/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_fuoof") 3:0/0 = 0 3:0/0/physics_layer_0/linear_velocity = Vector2(0, 0) 3:0/0/physics_layer_0/angular_velocity = 0.0 3:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(6, 6, 6, 8, 8, 8, 8, 6) +3:0/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_tco5v") 4:1/0 = 0 4:1/0/physics_layer_0/linear_velocity = Vector2(0, 0) 4:1/0/physics_layer_0/angular_velocity = 0.0 4:1/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -8, -8, -6, -6, -6, -6, -8) +4:1/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_mlmli") 4:0/0 = 0 4:0/0/physics_layer_0/linear_velocity = Vector2(0, 0) 4:0/0/physics_layer_0/angular_velocity = 0.0 4:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, 6, -8, 8, -6, 8, -6, 6) +4:0/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_pqq6a") 3:1/0 = 0 3:1/0/physics_layer_0/linear_velocity = Vector2(0, 0) 3:1/0/physics_layer_0/angular_velocity = 0.0 3:1/0/physics_layer_0/polygon_0/points = PackedVector2Array(6, -8, 6, -6, 8, -6, 8, -8) +3:1/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_tvjsp") [sub_resource type="TileSet" id="TileSet_dy2x8"] physics_layer_0/collision_layer = 1 physics_layer_0/collision_mask = 0 +navigation_layer_0/layers = 1 sources/0 = SubResource("TileSetAtlasSource_e47fw") [sub_resource type="RectangleShape2D" id="RectangleShape2D_hodhv"] @@ -155,3 +260,5 @@ position = Vector2(-8, 344) buff = ExtResource("6_ll7oj") [node name="HUD" parent="." instance=ExtResource("4_7uel4")] + +[node name="EntityContainer" type="Node" parent="."] diff --git a/scene/player.tscn b/scene/player.tscn index 7fc7412..60704af 100644 --- a/scene/player.tscn +++ b/scene/player.tscn @@ -7,6 +7,7 @@ radius = 6.0 [node name="Player" type="CharacterBody2D"] +collision_layer = 2 script = ExtResource("1_veifk") [node name="Sprite2D" type="Sprite2D" parent="."] diff --git a/scripts/attack.gd b/scripts/attack.gd new file mode 100644 index 0000000..d8cf070 --- /dev/null +++ b/scripts/attack.gd @@ -0,0 +1,23 @@ +extends Node2D +class_name Attack + +@onready var cpu_particles_2d: CPUParticles2D = $CPUParticles2D +@onready var area_2d: Area2D = $Area2D + +var initiator: Entity + +func set_initiator(entity: Entity) -> void: + position = entity.position + initiator = entity + +func _ready() -> void: + cpu_particles_2d.emitting = true + cpu_particles_2d.color = initiator.color + +func _on_cpu_particles_2d_finished() -> void: + var collisions := area_2d.get_overlapping_bodies() + for object in collisions: + if object is Entity and object != initiator: + object.hit() + print("attack hit %s" % collisions.size()) + queue_free() diff --git a/scripts/enemy.gd b/scripts/enemy.gd index bbf4348..23826d8 100644 --- a/scripts/enemy.gd +++ b/scripts/enemy.gd @@ -1,23 +1,20 @@ -extends CharacterBody2D +extends Entity class_name Enemy @onready var sprite_2d: Sprite2D = $Sprite2D +@onready var nav_agent: NavigationAgent2D = $NavigationAgent2D -var color := Color.WHITE -@export var speed = 100 -var lives: int = 1 +var vision: int = 100 func _physics_process(delta: float) -> void: - velocity = Vector2.ZERO - if Input.is_action_pressed("ui_down"): - velocity += Vector2.DOWN - if Input.is_action_pressed("ui_up"): - velocity += Vector2.UP - if Input.is_action_pressed("ui_left"): - velocity += Vector2.LEFT - if Input.is_action_pressed("ui_right"): - velocity += Vector2.RIGHT - velocity = velocity.normalized() * speed + if nav_agent.is_navigation_finished(): + call_deferred("new_target_point") + return + + var current_agent_position: Vector2 = global_position + var next_path_position: Vector2 = nav_agent.get_next_path_position() + + velocity = current_agent_position.direction_to(next_path_position) * speed move_and_slide() for i in range(get_slide_collision_count()): var collision := get_slide_collision(i) @@ -35,3 +32,19 @@ func _physics_process(delta: float) -> void: speed += 12 color.b -= 0.25 sprite_2d.modulate = color + +func new_target_point(): + # Wait for the first physics frame so the NavigationServer can sync. + await get_tree().physics_frame + + var target_point := Vector2.from_angle(randf_range(0.0, TAU)) * vision + var map := get_world_2d().navigation_map + var closest_point := NavigationServer2D.map_get_closest_point(map, global_position + target_point) + var point_delta := closest_point - target_point + var is_on_map = point_delta.is_zero_approx() # Answer to original question! + #if not is_on_map: + ## Wasn't on the map, so push in from edge. If you have thin sections on + ## your navmesh, this could push it back off the navmesh! + #point_delta = point_delta.normalized() + #closest_point += point_delta * 32 + nav_agent.target_position = closest_point diff --git a/scripts/entity.gd b/scripts/entity.gd new file mode 100644 index 0000000..0b9429a --- /dev/null +++ b/scripts/entity.gd @@ -0,0 +1,14 @@ +extends CharacterBody2D +class_name Entity + +signal death + +var color := Color.WHITE +@export var speed = 100 +var lives: int = 1 + +func hit() -> void: + lives -= 1 + if lives <= 0: + death.emit() + queue_free() diff --git a/scripts/hud.gd b/scripts/hud.gd index 05f32a2..3d1d0c3 100644 --- a/scripts/hud.gd +++ b/scripts/hud.gd @@ -6,6 +6,7 @@ class_name HUD @onready var progress_bar_blue: ProgressBar = $PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/PanelContainerBlue/ProgressBar @onready var texture_rect: TextureRect = $PanelContainer/MarginContainer/VBoxContainer/TextureRect @onready var lives_container: GridContainer = $PanelContainer/MarginContainer/VBoxContainer/LivesContainer +@onready var entity_count_label: Label = $EntityCountContainer/MarginContainer/EntityCountLabel const HEART = preload("res://assets/heart.png") func _on_player_color_changed(color: Color) -> void: @@ -23,3 +24,6 @@ func _on_player_lives_changed(lives: int) -> void: lives_container.add_child(life) elif heart_containers.size() > lives: heart_containers.pop_back().queue_free() + +func update_entity_count(count: int): + entity_count_label.text = "Entity Count: %s" % count diff --git a/scripts/maze.gd b/scripts/maze.gd index 2c62d98..60003bb 100644 --- a/scripts/maze.gd +++ b/scripts/maze.gd @@ -8,6 +8,7 @@ var spawn_locations: Array[Vector2] = [] @onready var hud: HUD = $HUD @onready var spawn_zones: Node2D = $SpawnZones +@onready var entity_container: Node = $EntityContainer func _ready() -> void: var spawn_zone_array := spawn_zones.get_children() @@ -17,6 +18,7 @@ func _ready() -> void: for x in range(zone.shape.size.x / 16): for y in range(zone.shape.size.y / 16): spawn_locations.append(top_left + Vector2(x * 16, y * 16)) + hud.update_entity_count(spawn_locations.size()) var player_loc: Vector2 = spawn_locations.pick_random() for spawn in spawn_locations: var entity @@ -27,4 +29,9 @@ func _ready() -> void: else: entity = ENEMY.instantiate() entity.position = spawn - add_child(entity) + entity.death.connect(_on_entity_death) + entity_container.add_child(entity) + +func _on_entity_death(): + var entities = entity_container.get_child_count() + hud.update_entity_count(entities) diff --git a/scripts/player.gd b/scripts/player.gd index 0a70a3e..75c3dc2 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -1,14 +1,12 @@ -extends CharacterBody2D +extends Entity class_name Player signal color_changed(color: Color) signal lives_changed(lives: int) @onready var sprite_2d: Sprite2D = $Sprite2D +const ATTACK := preload("res://scene/attack.tscn") -var color := Color.WHITE -@export var speed = 100 -var lives: int = 1 func _physics_process(delta: float) -> void: var input_direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") @@ -33,3 +31,8 @@ func _physics_process(delta: float) -> void: color.b -= 0.25 sprite_2d.modulate = color color_changed.emit(color) + + if Input.is_action_just_pressed("ui_accept"): + var attack: Attack = ATTACK.instantiate() + attack.set_initiator(self) + get_parent().add_sibling(attack)