godot-shooter / commit
Merge branch 'navigation' into 'main'
actors/Enemy.gd | 76 +++++++++++++++++++++++++++--------------- actors/Enemy.tscn | 21 ++++++------ actors/Player.gd | 17 ++++++---- actors/Player.tscn | 4 +++ items/ConstantItemGenerator.gd | 4 ++- items/LevelItemGenerator.gd | 6 ++-- project.godot | 2 ++ scenes/HUD.gd | 9 +++-- scenes/World.gd | 69 ++++++++++++++++++++++++++++---------- scenes/World.tscn | 11 +++++- weapons/Weapons.gd | 13 +++++++- 11 files changed, 164 insertions(+), 68 deletions(-) diff --git a/actors/Enemy.gd b/actors/Enemy.gd index 8dfed65..d4aa41a 100644 --- a/actors/Enemy.gd +++ b/actors/Enemy.gd @@ -1,8 +1,8 @@ extends KinematicBody2D class_name Enemy -onready var ray_cast_2d: RayCast2D = $RayCast2D onready var damage_points = $DamagePoints +onready var navigation_agent: NavigationAgent2D = $NavigationAgent2D const Utils = preload("res://utils/Utils.gd") const Explosion = preload("res://effects/Explosion.tscn") const Smoke = preload("res://effects/Smoke.tscn") @@ -25,6 +25,11 @@ var health_ratio var health_percentage func _ready() -> void: + navigation_agent.max_speed = move_speed + var _err = navigation_agent.connect("path_changed", self, "path_changed") + _err = navigation_agent.connect("velocity_computed", self, "velocity_computed") + _err = navigation_agent.connect("target_reached", self, "target_reached") + $AnimationPlayer.play("fade_in") if health < 0 or health > max_health: health = max_health @@ -34,6 +39,24 @@ func _ready() -> void: for i in hurt_points: hurt() +func path_changed() -> void: + pass + +func velocity_computed(new_vel: Vector2) -> void: + velocity = move_and_slide(new_vel) + global_rotation = velocity.angle() + PI/2.0 + pass + +func target_reached() -> void: + var players = get_tree().get_nodes_in_group("player") + var player = players[0] + player.hurt(25) + var explosion = Explosion.instance() + explosion.transform = transform + get_parent().add_child(explosion) + kill() + pass + func _process(_delta: float) -> void: var pb: ProgressBar = $Graphics/Alive/Container/ProgressBar health_ratio = float(health) / float(max_health) @@ -50,35 +73,34 @@ func _process(_delta: float) -> void: $Graphics/Alive/Sprite/Target.global_rotation = 0 $Graphics/Alive/Sprite/Trails.add_position(global_position) -func _physics_process(_delta: float) -> void: +func _physics_process(delta: float) -> void: if dead: return - - velocity = move_and_slide(velocity) - global_rotation = velocity.angle() + PI/2.0 - if self.follow_target: - var to_dir = (target - global_position).normalized() - var curr_dir = velocity.normalized() - var dir_diff = to_dir - curr_dir - var change = dir_diff * turn_rate * move_speed - velocity = (velocity + change).normalized() * move_speed - - var ray_length = ray_cast_2d.cast_to.length() - if ray_cast_2d.is_colliding(): - var players = get_tree().get_nodes_in_group("player") - var player = players[0] - var dist_to_player = global_position.distance_to(player.global_position) - if ray_cast_2d.get_collider() == player and dist_to_player < (ray_length + 5): - player.hurt(25) - var explosion = Explosion.instance() - explosion.transform = transform - get_parent().add_child(explosion) - kill() - -func turn_towards(pos: Vector2): - self.target = pos - self.follow_target = true + # navigate to the next position + var current_pos = global_position + var next_pos = navigation_agent.get_next_location() + var diff: Vector2 = next_pos - current_pos + var dir = diff.normalized() + var desired_vel = dir * navigation_agent.max_speed + var steering = (desired_vel - velocity) * delta * 4 + var new_vel = velocity + steering + velocity = new_vel + + if navigation_agent.avoidance_enabled: + navigation_agent.set_velocity(velocity) + else: + velocity_computed(velocity) + +func set_target(pos: Vector2): +# var to_dir = (pos - global_position).normalized() +# var curr_dir = velocity.normalized() +# var dir_diff = to_dir - curr_dir +# var change = dir_diff * turn_rate * move_speed +# velocity = (velocity + change).normalized() * move_speed +# var nav_map = navigation_agent.get_navigation_map() +# Utils.log("setting target location", pos, "map", nav_map) + navigation_agent.set_target_location(pos) func _ensure_smoke_exists() -> void: var max_smoke_count = $DamagePoints.get_child_count() diff --git a/actors/Enemy.tscn b/actors/Enemy.tscn index 6246ee2..affedab 100644 --- a/actors/Enemy.tscn +++ b/actors/Enemy.tscn @@ -89,7 +89,7 @@ tracks/2/keys = { [node name="Enemy" type="KinematicBody2D"] z_index = 10 collision_layer = 2 -collision_mask = 9 +collision_mask = 11 script = ExtResource( 3 ) [node name="Graphics" type="Node2D" parent="."] @@ -101,12 +101,10 @@ modulate = Color( 1, 0.470588, 0, 1 ) texture = ExtResource( 4 ) [node name="Target" type="Sprite" parent="Graphics/Alive/Sprite"] -visible = false -modulate = Color( 1, 0, 0, 1 ) +modulate = Color( 1, 0, 0, 0.643137 ) texture = ExtResource( 5 ) [node name="Trails" parent="Graphics/Alive/Sprite" instance=ExtResource( 2 )] -visible = false gradient = SubResource( 4 ) [node name="Container" type="Node2D" parent="Graphics/Alive"] @@ -139,13 +137,16 @@ position = Vector2( 0, -13 ) [node name="Position2D2" type="Position2D" parent="DamagePoints"] position = Vector2( 16, 16 ) -[node name="RayCast2D" type="RayCast2D" parent="."] -visible = false -enabled = true -cast_to = Vector2( 0, -50 ) - [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="."] -visible = false polygon = PoolVector2Array( 0, -23, -7, 5, -12, 9, -19, 9, -23, 14, -23, 19, -19, 23, -14, 23, -9, 19, -9, 14, -10, 12, -6, 9, 0, 15, 6, 9, 10, 12, 9, 14, 9, 19, 13, 23, 18, 23, 23, 18, 23, 14, 19, 10, 13, 10, 7, 5 ) [node name="DamageObjects" type="Node2D" parent="."] + +[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."] +path_desired_distance = 35.0 +target_desired_distance = 35.0 +navigation_layers = 3 +avoidance_enabled = true +radius = 35.0 +neighbor_dist = 100.0 +max_speed = 50.0 diff --git a/actors/Player.gd b/actors/Player.gd index edb8921..b3aa23b 100644 --- a/actors/Player.gd +++ b/actors/Player.gd @@ -5,11 +5,11 @@ const Damage = preload("res://effects/Damage.tscn") const Weapons = preload("res://weapons/Weapons.gd") const Constants = preload("res://utils/Constants.gd") +export var is_invincible = true export var score = 0 export var move_speed = Constants.SPEED export var health = 50 export var max_health = 100 -export var is_invincible = false export var turn_rate = 0.03 export var accel_rate = 1 export var radar_min_radius = 350 @@ -39,20 +39,18 @@ onready var player_to_target = $Graphics/Alive/PlayerToTarget const Utils = preload("res://utils/Utils.gd") const PopupControl = preload("res://utils/Popup.tscn") -var weapon: Weapon = create_default_weapon() +var weapon: Weapon var dead = false var velocity: Vector2 = Vector2.UP * move_speed var settings: Settings -func filter_invalid_enemies(enemy: Enemy) -> bool: - if enemy.dead: - return false - return true - func create_default_weapon() -> Weapon: return Weapons.create_basic_gun() func _ready() -> void: + velocity= Vector2.UP * move_speed + weapon = Weapons.create_non_gun() + # Utils.remove_user_settings() settings = Utils.load_user_settings() settings.save_user_settings() @@ -104,6 +102,11 @@ func _process(_delta: float) -> void: player_to_target.points[0] = self.global_position player_to_target.points[1] = closest_enemy.global_position +func filter_invalid_enemies(enemy: Enemy) -> bool: + if enemy.dead: + return false + return true + func sort_enemies_ascending_distance(a: Enemy,b: Enemy): var distA = self.global_position.distance_to(a.global_position) var distB = self.global_position.distance_to(b.global_position) diff --git a/actors/Player.tscn b/actors/Player.tscn index 0652a01..e84a2e9 100644 --- a/actors/Player.tscn +++ b/actors/Player.tscn @@ -372,6 +372,10 @@ text = "Replay!" [node name="Camera2D" type="Camera2D" parent="."] current = true +[node name="NavigationObstacle2D" type="NavigationObstacle2D" parent="."] +estimate_radius = false +radius = 35.0 + [connection signal="debug_signal" from="UI/HUD" to="." method="_on_HUD_debug_signal"] [connection signal="paused" from="UI/HUD" to="." method="_on_HUD_paused"] [connection signal="position_set" from="UI/HUD" to="." method="_on_HUD_position_set"] diff --git a/items/ConstantItemGenerator.gd b/items/ConstantItemGenerator.gd index 24790a6..09bd10a 100644 --- a/items/ConstantItemGenerator.gd +++ b/items/ConstantItemGenerator.gd @@ -14,6 +14,7 @@ func _init(creator, instance, group, sim_count = 1) -> void: group_name = group simultaneous_count = sim_count +# runs the generator, returns is_done, a boolean func run(node: Node) -> bool: var items = node.get_tree().get_nodes_in_group(group_name) var should_add = items.size() < simultaneous_count @@ -22,4 +23,5 @@ func run(node: Node) -> bool: item.add_to_group(group_name) node.add_child(item) generated_count += 1 - return false + var is_done = not should_add + return is_done diff --git a/items/LevelItemGenerator.gd b/items/LevelItemGenerator.gd index f0b8782..94639de 100644 --- a/items/LevelItemGenerator.gd +++ b/items/LevelItemGenerator.gd @@ -16,6 +16,7 @@ func _init(count, creator, instance, group, sim_count = 1) -> void: instance_object = instance simultaneous_count = sim_count +# runs the generator, returns is_done, a boolean func run(node: Node) -> bool: var items = node.get_tree().get_nodes_in_group(group_name) var generated_enough = generated_count == item_count @@ -23,11 +24,12 @@ func run(node: Node) -> bool: if finished: return true if generated_enough: - return false + return true var should_add = items.size() < simultaneous_count if should_add: var item: Node = funcref(instance_object, creator_name).call_func() item.add_to_group(group_name) node.add_child(item) generated_count += 1 - return false + var is_done = not should_add + return is_done diff --git a/project.godot b/project.godot index 23b7065..a27c543 100644 --- a/project.godot +++ b/project.godot @@ -132,6 +132,8 @@ debug_action={ 2d_physics/layer_2="enemies" 2d_physics/layer_3="power_ups" 2d_physics/layer_4="bullets" +2d_navigation/layer_1="player" +2d_navigation/layer_2="enemies" [network] diff --git a/scenes/HUD.gd b/scenes/HUD.gd index f4cb76f..108cc55 100644 --- a/scenes/HUD.gd +++ b/scenes/HUD.gd @@ -86,5 +86,10 @@ func _on_PauseButton_pressed() -> void: emit_signal("paused") func set_current_weapon(weapon: Weapon) -> void: - weapon_label.text = "%s:" % weapon.name - ammo_label.text = "%s" % weapon.ammo + var is_non_weapon = weapon.name == "" + if is_non_weapon: + weapon_label.text = "" + ammo_label.text = "" + else: + weapon_label.text = "%s:" % weapon.name + ammo_label.text = "%s" % weapon.ammo diff --git a/scenes/World.gd b/scenes/World.gd index 6b10aac..6c837a2 100644 --- a/scenes/World.gd +++ b/scenes/World.gd @@ -9,9 +9,10 @@ const PowerUp = preload("res://items/PowerUp.tscn") const Settings = preload("res://utils/Settings.gd") onready var player: Player = $Player onready var update_timer = $UpdateTimer +onready var navigation_polygon_instance: NavigationPolygonInstance = $NavigationPolygonInstance var generators = [ - ConstantItemGenerator.new("create_enemy", self, "enemies", 4), + ConstantItemGenerator.new("create_enemy", self, "enemies", 5), ConstantItemGenerator.new("create_health", self, "health", 10), ConstantItemGenerator.new("create_weapon", self, "weapon", 10), ConstantItemGenerator.new("create_bomb", self, "bombs", 10), @@ -47,7 +48,7 @@ func bomb_consumed(data): func create_enemy() -> Enemy: var enemy = Enemy.instance() - enemy.position = get_new_environment_position() + enemy.position = get_new_environment_position(1) enemy.connect("enemy_killed", self, "enemy_killed") return enemy func create_health() -> PowerUp: @@ -83,7 +84,7 @@ func create_weapon() -> PowerUp: weapon.label = "ROC" weapon.color = Color.red elif r < .9: - weapon.data = Weapons.create_gattling_gun() + weapon.data = Weapons.create_smg_gun() weapon.label = "SMG" weapon.color = Color.orange else: @@ -120,12 +121,49 @@ func get_new_environment_position(dist_ratio: float = 1.0) -> Vector2: break return potential_pos +func _ready() -> void: + run_generators() +# # position first two enemies +# var enemies = get_tree().get_nodes_in_group("enemies") +# var enemy1: Enemy = enemies[0] +# enemy1.position = Vector2(-300, 200) +# var enemy2: Enemy = enemies[1] +# enemy2.position = Vector2(300, 200) + pass + +func run_generators() -> void: + for g in generators: + var generator = g + while true: + var is_done = generator.run($Environment) + if is_done: + break + pass + func _input(_event: InputEvent) -> void: if player.dead: return func _process(_delta: float) -> void: + # move navigation polygon around the player, navigation won't work outside of this polygon + var nav_size = Vector2(item_replacement_distance * 2 * 5, item_replacement_distance * 2 * 5) + var nav_pos = player.global_position - (nav_size * 0.5) + var vertices = PoolVector2Array([ + nav_pos, + nav_pos + Vector2(nav_size.x, 0), + nav_pos + nav_size, + nav_pos + Vector2(0, nav_size.y) + ]) + var polygon = NavigationPolygon.new() + polygon.set_vertices(vertices) + var indices = PoolIntArray([0, 1, 2, 3]) + polygon.add_polygon(indices) + navigation_polygon_instance.navpoly = polygon + + # set the objective text player.set_objective("%s / %s" % [completed_objectives, max_objectives]) + + # if all objectives are complete, show the game finished menu var all_done = completed_objectives == max_objectives if all_done: get_tree().paused = true @@ -137,25 +175,23 @@ func _process(_delta: float) -> void: $Player/UI/GameFinishedMenu.show() return - for g in generators: - var generator = g - var _is_done = generator.run($Environment) - + run_generators() + + # position the destination $Destination.visible = player.is_destination_set $Destination.global_position = player.destination - player.clear_radar() - + # clear radar and populate with all items + var enemies = get_tree().get_nodes_in_group("enemies") + var filteredEnemies = Utils.filter(funcref(self,"filter_invalid_enemies"), enemies) + player.clear_radar() + player.update_radar(get_tree().get_nodes_in_group("gold"), Color.gold) + player.update_radar(filteredEnemies, Color.red) if player.settings.show_items: player.update_radar(get_tree().get_nodes_in_group("health"), Color.gray) player.update_radar(get_tree().get_nodes_in_group("weapons"), Color.white) player.update_radar(get_tree().get_nodes_in_group("bombs"), Color.purple) - player.update_radar(get_tree().get_nodes_in_group("gold"), Color.gold) - var enemies = get_tree().get_nodes_in_group("enemies") - var filteredEnemies = Utils.filter(funcref(self,"filter_invalid_enemies"), enemies) - player.update_radar(filteredEnemies, Color.red) - func _on_Destination_body_entered(body: Node) -> void: if body == player: player.clear_destination() @@ -186,8 +222,7 @@ func _on_Player_debug() -> void: # player.kill() # completed_objectives = max_objectives # player.debug() -# weapon_consumed(Weapons.create_rocket_gun()) -# weapon_consumed(Weapons.create_gattling_gun()) + weapon_consumed(Weapons.create_smg_gun()) pass # Replace with function body. func _on_Player_destination_set() -> void: @@ -200,7 +235,7 @@ func _on_UpdateTimer_timeout() -> void: var filteredEnemies = Utils.filter(funcref(self,"filter_invalid_enemies"), enemies) for e in filteredEnemies: var enemy: Enemy = e - enemy.turn_towards($Player.position) + enemy.set_target($Player.position) var diff = enemy.global_position - player.global_position var dist = diff.length() var is_too_far = dist > item_replacement_distance diff --git a/scenes/World.tscn b/scenes/World.tscn index abee8e7..d306dd5 100644 --- a/scenes/World.tscn +++ b/scenes/World.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=6 format=2] +[gd_scene load_steps=7 format=2] [ext_resource path="res://assets/kenney_simple-space/PNG/Default/icon_crossLarge.png" type="Texture" id=1] [ext_resource path="res://actors/Player.tscn" type="PackedScene" id=2] @@ -8,6 +8,11 @@ [sub_resource type="CircleShape2D" id=1] radius = 29.0172 +[sub_resource type="NavigationPolygon" id=2] +vertices = PoolVector2Array( -400, -400, 400, -400, 400, 400, -400, 400 ) +polygons = [ PoolIntArray( 0, 1, 2, 3 ) ] +outlines = [ PoolVector2Array( -400, -400, 400, -400, 400, 400, -400, 400 ) ] + [node name="World" type="Node2D"] script = ExtResource( 4 ) @@ -64,6 +69,10 @@ autostart = true wait_time = 0.1 autostart = true +[node name="NavigationPolygonInstance" type="NavigationPolygonInstance" parent="."] +navpoly = SubResource( 2 ) +navigation_layers = 2 + [connection signal="body_entered" from="Destination" to="." method="_on_Destination_body_entered"] [connection signal="debug" from="Player" to="." method="_on_Player_debug"] [connection signal="destination_set" from="Player" to="." method="_on_Player_destination_set"] diff --git a/weapons/Weapons.gd b/weapons/Weapons.gd index d191331..fc4981f 100644 --- a/weapons/Weapons.gd +++ b/weapons/Weapons.gd @@ -3,6 +3,17 @@ tool const Constants = preload("res://utils/Constants.gd") +static func create_non_gun() -> Weapon: + var weapon = Weapon.new() + weapon.shooting_range = 0 + weapon.particle_lifetime_secs = 0 + weapon.cooldown_seconds = 0 + weapon.damage_amount = 10 + weapon.name = "" + weapon.ammo = 0 + weapon.reload_size = 0 + return weapon + static func create_basic_gun() -> Weapon: var weapon = Weapon.new() weapon.particle = preload("res://weapons/Bullet.tscn") @@ -15,7 +26,7 @@ static func create_basic_gun() -> Weapon: weapon.reload_size = 1 return weapon -static func create_gattling_gun() -> Weapon: +static func create_smg_gun() -> Weapon: var weapon = Weapon.new() weapon.particle = preload("res://weapons/Bullet.tscn") weapon.shooting_range = 400