From 6bc0e995dbbad4c563ce9661dd29191eed817cc1 Mon Sep 17 00:00:00 2001 From: ToasterPanic Date: Mon, 1 Dec 2025 22:48:05 -0500 Subject: [PATCH] Enemies & very basic enemy AI --- scenes/game.tscn | 34 ++++++++- scripts/enemy.gd | 171 +++++++++++++++++++++++++++++++++++++++++++ scripts/enemy.gd.uid | 1 + scripts/game.gd | 2 +- 4 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 scripts/enemy.gd create mode 100644 scripts/enemy.gd.uid diff --git a/scenes/game.tscn b/scenes/game.tscn index 398cf93..7540615 100644 --- a/scenes/game.tscn +++ b/scenes/game.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=11 format=3 uid="uid://bauklhpieuivd"] +[gd_scene load_steps=12 format=3 uid="uid://bauklhpieuivd"] [ext_resource type="Script" uid="uid://d0qswyhwbhdua" path="res://scripts/game.gd" id="1_u5sy4"] [ext_resource type="Texture2D" uid="uid://dtwo7g0ipc4k" path="res://textures/ship_1.png" id="1_uwrxv"] @@ -8,6 +8,7 @@ [ext_resource type="AudioStream" uid="uid://5tr30e1tmdp6" path="res://sounds/collision.mp3" id="2_lbhrr"] [ext_resource type="AudioStream" uid="uid://b6tyof2j3ytbv" path="res://sounds/concrete_halls.mp3" id="3_0tnpc"] [ext_resource type="AudioStream" uid="uid://b1ung55xg31l3" path="res://sounds/boost_finish.mp3" id="3_p57ef"] +[ext_resource type="Script" uid="uid://8d4jhy3fo32b" path="res://scripts/enemy.gd" id="9_0tnpc"] [sub_resource type="CircleShape2D" id="CircleShape2D_uwrxv"] radius = 16.0 @@ -59,6 +60,36 @@ rotation_smoothing_speed = 15.0 shape = SubResource("CircleShape2D_lnu2h") debug_color = Color(0.9686392, 0, 0.4696517, 0.41960785) +[node name="Enemy" type="RigidBody2D" parent="."] +position = Vector2(0, -418) +linear_damp = 6.247 +script = ExtResource("9_0tnpc") + +[node name="Boost" type="AudioStreamPlayer" parent="Enemy"] +stream = ExtResource("2_iywne") + +[node name="BoostFinish" type="AudioStreamPlayer" parent="Enemy"] +stream = ExtResource("3_p57ef") +volume_db = -8.0 + +[node name="Collision" type="AudioStreamPlayer" parent="Enemy"] +stream = ExtResource("2_lbhrr") + +[node name="Sprite" type="Sprite2D" parent="Enemy"] +texture = ExtResource("1_uwrxv") + +[node name="CollisionShape" type="CollisionShape2D" parent="Enemy"] +shape = SubResource("CircleShape2D_uwrxv") + +[node name="Hitbox" type="Area2D" parent="Enemy"] + +[node name="CollisionShape" type="CollisionShape2D" parent="Enemy/Hitbox"] +shape = SubResource("CircleShape2D_lnu2h") +debug_color = Color(0.9686392, 0, 0.4696517, 0.41960785) + +[node name="SightCast" type="RayCast2D" parent="Enemy"] +target_position = Vector2(0, -256) + [node name="Unloadables" type="Node2D" parent="."] [node name="UI" type="CanvasLayer" parent="."] @@ -89,3 +120,4 @@ grow_vertical = 2 text = "DIST: 2048" [connection signal="body_shape_entered" from="Player/Hitbox" to="Player" method="_on_hitbox_body_shape_entered"] +[connection signal="body_shape_entered" from="Enemy/Hitbox" to="Enemy" method="_on_hitbox_body_shape_entered"] diff --git a/scripts/enemy.gd b/scripts/enemy.gd new file mode 100644 index 0000000..8629e73 --- /dev/null +++ b/scripts/enemy.gd @@ -0,0 +1,171 @@ +extends RigidBody2D + +var health = 1000 +var boost = 100 +var speed = 480 +var time_since_last_collision = 1 +var boosting = false + +var axis = 0 +var movement_axis = -1 + +var AI_MODE_ATTACK = 1 + +var ai_mode = AI_MODE_ATTACK + +var AI_STATE_EVADE_OBJECT = 1 +var AI_STATE_TRACK_ENEMY = 2 +var AI_STATE_STATIONARY_ENEMY = 3 + +var object_to_evade = null +var last_ai_state_before_evasion = AI_STATE_TRACK_ENEMY + +var ai_state = AI_STATE_TRACK_ENEMY +var target_rotation = 0 +var target_rotation_speed = 1 + +var ai_tick = 0.1 +@onready var player = get_parent().get_node("Player") + +func cast(angle): + $SightCast.rotation_degrees = angle + $SightCast.force_raycast_update() + + return $SightCast.get_collider() + +func check_front(): + var front_cast = cast(0) + if !front_cast: + $SightCast.position.x = -20 + front_cast = cast(0) + + if !front_cast: + $SightCast.position.x = 20 + front_cast = cast(0) + + $SightCast.position.x = 0 + + if front_cast: + $SightCast.position.x = -20 + $SightCast.force_raycast_update() + var cast_left = $SightCast.get_collision_point() + + $SightCast.position.x = 20 + $SightCast.force_raycast_update() + var cast_right = $SightCast.get_collision_point() + + $SightCast.position.x = 0 + + if (cast_left - $SightCast.position).length() > (cast_right - $SightCast.position).length(): return -1 + else: return 1 + + return 1 + +func _process(delta: float) -> void: + ai_tick -= delta + + if boosting: + if Input.is_action_pressed("boost"): + boost -= delta * 40 + + if boost <= 0: + boost = 0 + boosting = false + $BoostFinish.play() + else: + boosting = false + $BoostFinish.play() + else: + boost += delta * 25 + if boost > 100: boost = 100 + + $Boost.stop() + if Input.is_action_just_pressed("boost") and (boost > 33): + boosting = true + $Boost.play() + + if time_since_last_collision <= 1: + time_since_last_collision += delta + + if ai_tick < 0: + ai_tick = 0.1 + + movement_axis = -1 + + if ai_state == AI_STATE_TRACK_ENEMY: + target_rotation = global_position.direction_to(player.position).angle() + deg_to_rad(270) + + if ai_state == AI_STATE_STATIONARY_ENEMY: + target_rotation = global_position.direction_to(player.position).angle() + deg_to_rad(270) + movement_axis = 0 + + var front_cast = cast(0) + if !front_cast: + $SightCast.position.x = -20 + front_cast = cast(0) + + if !front_cast: + $SightCast.position.x = 20 + front_cast = cast(0) + + $SightCast.position.x = 0 + + if front_cast: + var front_cast_distance = ($SightCast.get_collision_point() - position).length() + + if (front_cast_distance < 160 * (speed / 300)) and (front_cast_distance < (player.position - position).length()): + if ai_state != AI_STATE_EVADE_OBJECT: last_ai_state_before_evasion = ai_state + + ai_state = AI_STATE_EVADE_OBJECT + target_rotation += deg_to_rad(45) * check_front() + + object_to_evade = front_cast + + target_rotation_speed = 1 + + if ai_state == AI_STATE_EVADE_OBJECT: + if not front_cast: + ai_state = last_ai_state_before_evasion + else: + var front_cast_distance = ($SightCast.get_collision_point() - position).length() + if front_cast_distance > 160 * (speed / 300): + ai_state = last_ai_state_before_evasion + + print(ai_state) + +func _physics_process(delta: float) -> void: + axis = 0 + + var difference = fmod(target_rotation, deg_to_rad(360)) - fmod(rotation, deg_to_rad(360)) + var normalized_difference = fmod(difference + deg_to_rad(180), deg_to_rad(360)) - deg_to_rad(180) + var target_distance = normalized_difference + + if target_distance < 0: + axis = target_rotation_speed + if target_distance > 0: + axis = -target_rotation_speed + + angular_velocity = deg_to_rad(180 * axis) + + var final_speed = speed + + if boosting: + movement_axis = -1 + final_speed = 1024 + + # Slow down on collision and gradually speed up + if time_since_last_collision < 0.85: final_speed *= (time_since_last_collision + 0.15) + + var new_velocity = transform.y * movement_axis * final_speed + + if (new_velocity.length() > linear_velocity.length() - 12): + linear_velocity = new_velocity + + +func _on_hitbox_body_shape_entered(body_rid: RID, body: Node2D, body_shape_index: int, local_shape_index: int) -> void: + if body.linear_velocity.length() + linear_velocity.length() > 256: + health -= 100 + + $Collision.play() + + time_since_last_collision = 0 diff --git a/scripts/enemy.gd.uid b/scripts/enemy.gd.uid new file mode 100644 index 0000000..7d0d476 --- /dev/null +++ b/scripts/enemy.gd.uid @@ -0,0 +1 @@ +uid://8d4jhy3fo32b diff --git a/scripts/game.gd b/scripts/game.gd index e3a2ab5..c6fb248 100644 --- a/scripts/game.gd +++ b/scripts/game.gd @@ -27,7 +27,7 @@ func _process(delta: float) -> void: if time_since_last_atmospheric_track > 60: var tracks = [$BalladOfTheCats, $ConcreteHalls] - current_atmospheric_track = tracks[randi_range(0, tracks.size())] + current_atmospheric_track = tracks[randi_range(0, tracks.size() - 1)] current_atmospheric_track.play() chunk_tick_timer -= delta