From e6448c7f0aa79a0119b9e40d768a1c6c92b94cfc Mon Sep 17 00:00:00 2001 From: Marco Date: Mon, 11 Nov 2024 15:41:34 +0100 Subject: [PATCH] Debug stats and nonfunctional bursts --- Scenes/debug_stats.tscn | 11 ++++ Scenes/game.tscn | 5 +- Scripts/DebugStats.cs | 109 +++++++++++++++++++++++++++++++++++++ Scripts/Enemy.cs | 115 ++++++++++++++++++++++++++++------------ Scripts/debug_stats.gd | 50 +++++++++++++++++ project.godot | 4 ++ 6 files changed, 259 insertions(+), 35 deletions(-) create mode 100644 Scenes/debug_stats.tscn create mode 100644 Scripts/DebugStats.cs create mode 100644 Scripts/debug_stats.gd diff --git a/Scenes/debug_stats.tscn b/Scenes/debug_stats.tscn new file mode 100644 index 00000000..6868bb4e --- /dev/null +++ b/Scenes/debug_stats.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=2 format=3 uid="uid://cio2gclfs7s37"] + +[ext_resource type="Script" path="res://Scripts/DebugStats.cs" id="1_22s7t"] + +[node name="DebugStats" type="MarginContainer"] +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("1_22s7t") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 diff --git a/Scenes/game.tscn b/Scenes/game.tscn index 902ca158..71947d76 100644 --- a/Scenes/game.tscn +++ b/Scenes/game.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=6 format=3 uid="uid://cwfaxgr8pgfga"] +[gd_scene load_steps=7 format=3 uid="uid://cwfaxgr8pgfga"] [ext_resource type="Script" path="res://Scenes/game.gd" id="1_s0gpj"] [ext_resource type="Script" path="res://Scenes/PixelPerfectRendering.gd" id="2_gdejn"] [ext_resource type="PackedScene" uid="uid://bv451a8wgty4u" path="res://Scenes/test.tscn" id="3_l2ygy"] [ext_resource type="Script" path="res://Scenes/SubViewportSprite.gd" id="4_adb7a"] +[ext_resource type="PackedScene" uid="uid://cio2gclfs7s37" path="res://Scenes/debug_stats.tscn" id="5_ixk64"] [sub_resource type="ViewportTexture" id="ViewportTexture_m5h5j"] viewport_path = NodePath("PixelPerfectRendering/SubViewport") @@ -30,3 +31,5 @@ position = Vector2(960, 540) scale = Vector2(6, 6) texture = SubResource("ViewportTexture_m5h5j") script = ExtResource("4_adb7a") + +[node name="DebugStats" parent="." instance=ExtResource("5_ixk64")] diff --git a/Scripts/DebugStats.cs b/Scripts/DebugStats.cs new file mode 100644 index 00000000..21e1db9e --- /dev/null +++ b/Scripts/DebugStats.cs @@ -0,0 +1,109 @@ +using Godot; +using System; +using System.Collections.Generic; + +public partial class DebugStats : Node +{ + //public DebugStats Stats => GetNode("VBoxContainer"); + + private class Property + { + public string NumFormat { get; set; } = "%4.2f"; + public Node Object { get; set; } // The object being tracked. + public string PropertyPath { get; set; } // The property to display (NodePath). + public Label LabelRef { get; set; } // A reference to the Label. + public string Display { get; set; } // Display option (rounded, etc.) + + public Property(Node obj, string property, Label label, string display) + { + Object = obj; + PropertyPath = property; + LabelRef = label; + Display = display; + } + + public void SetLabel() + { + // Sets the label's text. + string s = $"{Object.Name}/{PropertyPath} : "; + var p = Object.GetIndexed(PropertyPath); + + switch (Display) + { + case "": + s += p.ToString(); + break; + case "length": + // Handle length for Vector2 and Vector3 + if (p.VariantType == Variant.Type.Vector2) + { + Vector2 v2 = p.As(); + s += string.Format(NumFormat, v2.Length()); + } + else if (p.VariantType == Variant.Type.Vector3) + { + Vector3 v3 = p.As(); + s += string.Format(NumFormat, v3.Length()); + } + else + { + s += "N/A"; // Handle non-applicable types + } + break; + case "round": + // For handling numbers (int and float) + if (p.VariantType == Variant.Type.Float || p.VariantType == Variant.Type.Int) + { + s += string.Format(NumFormat, p.As()); + } + else if (p.VariantType == Variant.Type.Vector2) + { + Vector2 v2Round = p.As(); + s += v2Round.Round().ToString(); + } + else if (p.VariantType == Variant.Type.Vector3) + { + Vector3 v3Round = p.As(); + s += v3Round.Round().ToString(); + } + break; + } + + LabelRef.Text = s; + } + } + + private List props = new List(); // An array of the tracked properties. + + + public static DebugStats Instance { get; private set; } + public override void _Ready() + { + Instance = this; + } + + public override void _Process(double delta) + { + // if (!Visible) + // return; + + foreach (var prop in props) + { + prop.SetLabel(); + } + } + + public void AddProperty(Node obj, string property, string display) + { + Label label = new Label(); + label.Set("custom_fonts/font", GD.Load("res://fonts/Xolonium-Regular.ttf")); + GetNode("VBoxContainer").AddChild(label); + + props.Add(new Property(obj, property, label, display)); + } + + public void RemoveProperty(Node obj, string property) + { + props.RemoveAll(prop => prop.Object == obj && prop.PropertyPath == property); + } +} diff --git a/Scripts/Enemy.cs b/Scripts/Enemy.cs index 6baf1cbe..ec346d91 100644 --- a/Scripts/Enemy.cs +++ b/Scripts/Enemy.cs @@ -13,40 +13,59 @@ public partial class Enemy : Area2D, IDestructible [Export] public float Health = 4f; - [Export] public double RateOfFire = 0.4f; + [Export] public double RateOfFire = 0.4f; // Time between shots within a burst [Export] public float BulletSpeed = 100f; + [Export] public int BurstCount = 3; // Number of shots per burst + + [Export] public float BurstCooldown = 1f; // Time between bursts + + [Export] public bool AimAtPlayer = true; // Whether to aim at player or shoot straight + private float _currentHealth = 0f; private bool _isDestroyed = false; - private Timer _cooldownTimer; + private Timer _burstTimer; + private Timer _shotTimer; + + private int _shotsFired = 0; - // Called when the node enters the scene tree for the first time. public override void _Ready() { _currentHealth = Health; - _cooldownTimer = GetNode("./ShootTimer"); + + // Initialize timers + _burstTimer = new Timer(); + _shotTimer = new Timer(); + + AddChild(_burstTimer); + AddChild(_shotTimer); + + _burstTimer.WaitTime = BurstCooldown; + _burstTimer.OneShot = true; + + _shotTimer.WaitTime = RateOfFire; + _shotTimer.OneShot = true; + + _shotTimer.Timeout += OnShotTimerTimeout; + _burstTimer.Timeout += OnBurstTimerTimeout; + + + + // Debug + DebugStats.Instance.AddProperty(this, "_currentHealth", ""); } - // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { switch (_currentState) { case EnemyState.Idle: - break; - case EnemyState.Primed: - // Have the raycast follow the player and shoot if visible - //HandlePlayerDetection(); break; - - //case EnemyState.Shooting: - // Shoot - //break; default: break; } @@ -64,36 +83,67 @@ public partial class Enemy : Area2D, IDestructible return; } - if (_cooldownTimer.IsStopped() && IsPlayerInSight()) + if (_burstTimer.IsStopped() && !_shotTimer.IsStopped() && IsPlayerInSight()) { - // SHOOT - var bullet = this.CreateChild(BulletScene); - // var bullet = BulletScene.Instantiate(); - // Owner.AddChild(bullet); - // bullet.Transform = this.GlobalTransform; - // bullet.Position = this.Position; - bullet.SetDirection((_cachedPlayer.GlobalPosition - this.GlobalPosition).Normalized()); - bullet.Speed = BulletSpeed; - _cooldownTimer.Start(RateOfFire); + StartBurst(); } } + private void StartBurst() + { + _shotsFired = 0; + _shotTimer.Start(); + } + + private void OnShotTimerTimeout() + { + if (_shotsFired < BurstCount) + { + Shoot(); + _shotsFired++; + if (_shotsFired < BurstCount) + { + _shotTimer.Start(); // Restart for the next shot in the burst + } + } + else + { + _burstTimer.Start(); // Start cooldown between bursts + } + } + + private void OnBurstTimerTimeout() + { + // Cooldown complete, can start new burst when conditions are met + } + + private void Shoot() + { + var bullet = this.CreateChild(BulletScene); + + if (AimAtPlayer) + { + bullet.SetDirection((_cachedPlayer.GlobalPosition - this.GlobalPosition).Normalized()); + } + else + { + bullet.SetDirection(Vector2.Right.Rotated(this.Rotation)); + } + + bullet.Speed = BulletSpeed; + } + private bool IsPlayerInSight() { var spaceState = GetWorld2D().DirectSpaceState; var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, _cachedPlayer.GlobalPosition, CollisionMask, new Godot.Collections.Array { GetRid() }); var result = spaceState.IntersectRay(query); - // If count is 0 then the player is in sight, otherwise there is level geometry in the way - return result.Count == 0; - - // if (result.Count > 0) - // GD.Print("Hit at point: ", result["position"]); + return result.Count == 0; // True if no obstacles between enemy and player } private void _on_player_detection_area_entered(Area2D area) { - // Assume area is player for now if (area is InteractionController player) { Debug.WriteLine("Enemy detection area Entered by interaction controller"); @@ -114,24 +164,21 @@ public partial class Enemy : Area2D, IDestructible } } - // Bullets collision private void _on_area_entered(Area2D area) { - + // Collision handling } private void Explode() { Debug.WriteLine("Ded"); - //CreateParticles(); - //CreateDebris(); QueueFree(); } public void Hit(float damage) { if (_isDestroyed) return; - + _currentHealth -= damage; if (!(_currentHealth <= 0)) return; _isDestroyed = true; diff --git a/Scripts/debug_stats.gd b/Scripts/debug_stats.gd new file mode 100644 index 00000000..2dc719f6 --- /dev/null +++ b/Scripts/debug_stats.gd @@ -0,0 +1,50 @@ +extends MarginContainer + +class Property: + var num_format = "%4.2f" + var object # The object being tracked. + var property # The property to display (NodePath). + var label_ref # A reference to the Label. + var display # Display option (rounded, etc.) + + func _init(_object, _property, _label, _display): + object = _object + property = _property + label_ref = _label + display = _display + + func set_label(): + # Sets the label's text. + var s = object.name + "/" + property + " : " + var p = object.get_indexed(property) + match display: + "": + s += str(p) + "length": + s += num_format % p.length() + "round": + match typeof(p): + TYPE_INT, TYPE_FLOAT: + s += num_format % p + TYPE_VECTOR2, TYPE_VECTOR3: + s += str(p.round()) + label_ref.text = s + +var props = [] # An array of the tracked properties. + +func _process(_delta): + if not visible: + return + for prop in props: + prop.set_label() + +func add_property(object, property, display): + var label = Label.new() + label.set("custom_fonts/font", load("res://debug/roboto_16.tres")) + $VBoxContainer.add_child(label) + props.append(Property.new(object, property, label, display)) + +func remove_property(object, property): + for prop in props: + if prop.object == object and prop.property == property: + props.erase(prop) diff --git a/project.godot b/project.godot index d7ff5736..14737ad1 100644 --- a/project.godot +++ b/project.godot @@ -15,6 +15,10 @@ run/main_scene="res://Scenes/game.tscn" config/features=PackedStringArray("4.3", "C#", "GL Compatibility") config/icon="res://icon.svg" +[autoload] + +DebugStats="*res://Scenes/debug_stats.tscn" + [display] window/size/viewport_width=1920