From 8c2c33644e38d32be901492bdb2e27b8e49c7e65 Mon Sep 17 00:00:00 2001 From: HumanoidSandvichDispenser Date: Sun, 27 Nov 2022 19:37:16 -0800 Subject: [PATCH] snapshot (WIP) --- Assets/Sounds/hurt.wav | Bin 0 -> 13026 bytes Assets/Sounds/hurt.wav.import | 24 +++ Assets/Sounds/parry.wav | Bin 0 -> 41162 bytes Assets/Sounds/parry.wav.import | 24 +++ Characters/ExampleEnemy.tscn | 8 +- Characters/NPC.cs | 302 ++++++++++++++++++---------- Characters/Player.tscn | 15 +- Characters/States/PlayerState.cs | 2 - Extensions/Node.cs | 9 +- Extensions/Node2DExtensions.cs | 13 ++ Extensions/Vector2.cs | 39 ++++ Items/Weapons/Sword.tscn | 94 ++++----- Scenes/Level.tscn | 33 ++- Tests/ContextBasedSteering.cs | 109 ++++------ Tests/ContextBasedSteeringTest.tscn | 6 +- UI/HealthBar.tscn | 18 ++ export_presets.cfg | 32 ++- project.godot | 7 + 18 files changed, 469 insertions(+), 266 deletions(-) create mode 100644 Assets/Sounds/hurt.wav create mode 100644 Assets/Sounds/hurt.wav.import create mode 100644 Assets/Sounds/parry.wav create mode 100644 Assets/Sounds/parry.wav.import create mode 100644 Extensions/Node2DExtensions.cs create mode 100644 Extensions/Vector2.cs create mode 100644 UI/HealthBar.tscn diff --git a/Assets/Sounds/hurt.wav b/Assets/Sounds/hurt.wav new file mode 100644 index 0000000000000000000000000000000000000000..4b31d395617ebd6ab15479fdab2f5e0729811307 GIT binary patch literal 13026 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 literal 0 HcmV?d00001 diff --git a/Assets/Sounds/hurt.wav.import b/Assets/Sounds/hurt.wav.import new file mode 100644 index 0000000..0fc498f --- /dev/null +++ b/Assets/Sounds/hurt.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://njun3e6v4854" +path="res://.godot/imported/hurt.wav-d823d511f814815fa850bdfbaa893eea.sample" + +[deps] + +source_file="res://Assets/Sounds/hurt.wav" +dest_files=["res://.godot/imported/hurt.wav-d823d511f814815fa850bdfbaa893eea.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/Assets/Sounds/parry.wav b/Assets/Sounds/parry.wav new file mode 100644 index 0000000000000000000000000000000000000000..7ed32bfd0f46025283315aeec0faa768203f507e GIT binary patch literal 41162 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 literal 0 HcmV?d00001 diff --git a/Assets/Sounds/parry.wav.import b/Assets/Sounds/parry.wav.import new file mode 100644 index 0000000..7c7f0d9 --- /dev/null +++ b/Assets/Sounds/parry.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://c4n7ioxpukdwi" +path="res://.godot/imported/parry.wav-95f3f1d45e3d2fd9dd7d767397e50846.sample" + +[deps] + +source_file="res://Assets/Sounds/parry.wav" +dest_files=["res://.godot/imported/parry.wav-95f3f1d45e3d2fd9dd7d767397e50846.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/Characters/ExampleEnemy.tscn b/Characters/ExampleEnemy.tscn index a564841..3e73e02 100644 --- a/Characters/ExampleEnemy.tscn +++ b/Characters/ExampleEnemy.tscn @@ -11,8 +11,7 @@ [ext_resource type="PackedScene" uid="uid://d72ehtv1ks0e" path="res://Items/Weapons/Sword.tscn" id="8_s3c8r"] [ext_resource type="AudioStream" uid="uid://njun3e6v4854" path="res://Assets/Sounds/hurt.wav" id="10_n1e64"] -[sub_resource type="ShaderMaterial" id="ShaderMaterial_8jbxb"] -resource_local_to_scene = true +[sub_resource type="ShaderMaterial" id="ShaderMaterial_ms3xg"] shader = ExtResource("1_fx1w5") shader_parameter/color = null shader_parameter/intensity = 0.0 @@ -103,9 +102,9 @@ _data = { [node name="ExampleEnemy" type="CharacterBody2D" node_paths=PackedStringArray("Sprite", "Inventory", "StateMachine")] texture_filter = 3 -material = SubResource("ShaderMaterial_8jbxb") +material = SubResource("ShaderMaterial_ms3xg") y_sort_enabled = true -collision_layer = 2 +collision_layer = 10 script = ExtResource("1_2yopk") Health = 50.0 Sprite = NodePath("Sprite") @@ -114,7 +113,6 @@ StateMachine = NodePath("StateMachine") Faction = 2 [node name="Sprite" type="AnimatedSprite2D" parent="."] -use_parent_material = true frames = SubResource("SpriteFrames_4tm2b") animation = &"move" playing = true diff --git a/Characters/NPC.cs b/Characters/NPC.cs index 024b79b..9b2868f 100644 --- a/Characters/NPC.cs +++ b/Characters/NPC.cs @@ -1,4 +1,5 @@ using Godot; +using SupaLidlGame.Extensions; using SupaLidlGame.Items; using System; @@ -13,15 +14,17 @@ namespace SupaLidlGame.Characters public float[] Weights => _weights; + [Export] + public float PreferredDistance { get; protected set; } = 64.0f; + protected float[] _weights = new float[16]; - protected Vector2[] _weightDirs = new Vector2[16]; protected int _bestWeightIdx; protected double _thinkTimeElapsed = 0; + protected Vector2 _blockingDir; + protected static readonly Vector2[] _weightDirs = new Vector2[16]; - public override void _Ready() + static NPC() { - base._Ready(); - Array.Fill(_weights, 0); for (int i = 0; i < 16; i++) { float y = Mathf.Sin(Mathf.Pi * i * 2 / 16); @@ -30,6 +33,12 @@ namespace SupaLidlGame.Characters } } + public override void _Ready() + { + base._Ready(); + Array.Fill(_weights, 0); + } + /* public override void _Process(double delta) { @@ -47,10 +56,10 @@ namespace SupaLidlGame.Characters public override void _Draw() { - #if DEBUG +#if DEBUG for (int i = 0; i < 16; i++) { - Vector2 vec = _weightDirs[i] * _weights[i] * 128; + Vector2 vec = _weightDirs[i] * _weights[i] * 32; Color c = Colors.Green; if (_bestWeightIdx == i) { @@ -63,7 +72,12 @@ namespace SupaLidlGame.Characters } DrawLine(Vector2.Zero, vec, c); } - #endif + /* + DrawLine(Vector2.Zero, Direction * 32, new Color(0, 1, 0)); + DrawLine(Vector2.Zero, Target * 32, Colors.Blue); + DrawLine(Vector2.Zero, _blockingDir, Colors.Red, 2); + */ +#endif base._Draw(); } @@ -93,18 +107,193 @@ namespace SupaLidlGame.Characters { _thinkTimeElapsed = 0; Think(); + QueueRedraw(); } - Direction = Target; - //Direction = _weightDirs[_bestWeightIdx]; + //Direction = GetDirection(Target); + Direction = _weightDirs[_bestWeightIdx]; + } + + public void UpdateWeights(Vector2 pos) + { + // FIXME: TODO: remove all the spaghetti + Vector2 dir = Target.Normalized(); + float dist = GlobalPosition.DistanceSquaredTo(pos); + + var spaceState = GetWorld2d().DirectSpaceState; + var exclude = new Godot.Collections.Array(); + exclude.Add(this.GetRid()); + + for (int i = 0; i < 16; i++) + { + float directDot = _weightDirs[i].Dot(dir); + // clamp dot from [-1, 1] to [0, 1] + directDot = (directDot + 1) / 2; + + float strafeDot = Math.Abs(_weightDirs[i].Dot(dir.Clockwise90())); + float currDirDot = (_weightDirs[i].Dot(Direction) + 1) / 16; + strafeDot = Mathf.Pow((strafeDot + 1) / 2, 2) + currDirDot; + + if (dist > 4096) + { + _weights[i] = directDot; + } + else if (dist > 64) + { + float dDotWeight = Mathf.Sqrt(dist / 4096); + float sDotWeight = 1 - dDotWeight; + _weights[i] = (dDotWeight * directDot) + (sDotWeight * strafeDot); + } + else + { + _weights[i] = strafeDot; + } + + } + + for (int i = 0; i < 16; i++) + { + var rayParams = new PhysicsRayQueryParameters2D + { + Exclude = exclude, + CollideWithBodies = true, + From = GlobalPosition, + To = GlobalPosition + (_weightDirs[i] * 24), + CollisionMask = 1 + 8 + }; + + var result = spaceState.IntersectRay(rayParams); + + // if we hit something + if (result.Count > 0) + { + // then we subtract the value of this from the other weights + float oldWeight = _weights[i]; + for (int j = 0; j < 16; j++) + { + if (i == j) + { + _weights[i] = 0; + } + else + { + float dot = _weightDirs[i].Dot(_weightDirs[j]); + _weights[j] -= _weights[j] * dot; + } + } + } + } + + + float bestWeight = 0; + for (int i = 0; i < 16; i++) + { + if (_weights[i] > bestWeight) + { + _bestWeightIdx = i; + bestWeight = _weights[i]; + } + } + } + + public Vector2 GetDirection(Vector2 towards) + { + float directWeight; + float strafeWeight; + + float dist = towards.Length(); + + Vector2 directDir = towards.Normalized(); + Vector2 strafeDir; + float crossProduct = towards.Cross(Direction); + + // strafeDir is either counter clockwise or clockwise depending on + // the direction the NPC is already traveling + + // enemies might rapidly change direction if compared to 0 + if (crossProduct < -1) + { + strafeDir = directDir.Counterclockwise90(); + } + else + { + strafeDir = directDir.Counterclockwise90(); + //strafeDir = directDir.Clockwise90(); + } + + // weights approach 1 + // dy/dx = 1 - y + // y = 1 - e^(-x) + + directWeight = 1 - Mathf.Pow(Mathf.E, -(dist / PreferredDistance)); + strafeWeight = 1 - directWeight; + + /* + Vector2 midpoint = (strafeDir * strafeWeight) + .Midpoint(directDir * directWeight); + */ + Vector2 midpoint = (directDir * directWeight) + .Midpoint(strafeDir * strafeWeight); + + _blockingDir = GetBlocking(); + midpoint += _blockingDir; + + return midpoint.Normalized(); + } + + public Vector2 GetBlocking() + { + var spaceState = GetWorld2d().DirectSpaceState; + int rayLength = 16; + float[] weights = new float[16]; + Vector2[] rays = new Vector2[16]; + Vector2 net = Vector2.Zero; + for (int i = 0; i < 16; i++) + { + // cast ray and gets its length + // the length determines its strength + + // exclude itself from raycasts + var exclude = new Godot.Collections.Array(); + exclude.Add(GetRid()); + + var rayParams = new PhysicsRayQueryParameters2D + { + CollisionMask = 9, + From = GlobalPosition, + To = _weightDirs[i] * rayLength, + Exclude = exclude + }; + var result = spaceState.IntersectRay(rayParams); + if (result.Count > 0) + { + Vector2 position = (Vector2)result["position"]; + float hitDist = GlobalPosition.DistanceTo(position); + //float hitDist = GlobalPosition.DistanceSquaredTo(position); + //float rayDist = Mathf.Pow(rayLength, 2); + //float weight = rayDist - hitDist; + float weight = rayLength - hitDist; + GD.Print(weight); + rays[i] = _weightDirs[i] * weight; + net += rays[i]; + } + else + { + rays[i] = Vector2.Zero; + } + } + + return net; + //return -Vector2Extensions.Midpoints(rays); } protected virtual void Think() { Vector2 pos = FindBestTarget().GlobalPosition; Target = pos - GlobalPosition;//GlobalPosition.DirectionTo(pos); - Vector2 dir = Target.Normalized(); + Vector2 dir = Target; float dist = GlobalPosition.DistanceSquaredTo(pos); + UpdateWeights(pos); if (Target.LengthSquared() < 1024) { @@ -113,99 +302,6 @@ namespace SupaLidlGame.Characters UseCurrentItem(); } } - - #if DEBUGs - for (int i = 0; i < 16; i++) - { - float directDot = _weightDirs[i].Dot(dir); - directDot = (directDot + 1) / 2; - - // this dot product resembles values of sine rather than cosine - // use it to weigh direction horizontally - Vector2 rotatedDir = new Vector2(-dir.y, dir.x); - float horizDot = Math.Abs(_weightDirs[i].Dot(rotatedDir)); - - // this is a smaller weight so they are more likely to pick the - // direction they are currently heading when choosing between two - // horizontal weights - float currDirDot = (_weightDirs[i].Dot(Direction) + 1) / 16; - - // square so lower values are even lower - horizDot = Mathf.Pow((horizDot + 1) / 2, 2) + currDirDot; - - // "When will I use math in the real world" Clueful - - - if (dist > 4096) - { - _weights[i] = directDot; - } - else if (dist > 64) - { - //float directDotWeighting = dist / 4096; - //float directDotWeighting = Mathf.Log(dist) / _log1024; - float directDotWeighting = Mathf.Sqrt(dist / 4096); - float horizDotWeighting = 1 - directDotWeighting; - - _weights[i] = (directDot * directDotWeighting) + - (horizDot * horizDotWeighting * 0.5f); - } - else - { - // shorter than 64 - _weights[i] = horizDot; - } - - // now we shall subtract weights whose rays collide - // with something - - { - var spaceState = GetWorld2d().DirectSpaceState; - var exclude = new Godot.Collections.Array(); - exclude.Add(this.GetRid()); - var rayParams = new PhysicsRayQueryParameters2D - { - Exclude = exclude, - CollideWithBodies = true, - From = GlobalPosition, - To = GlobalPosition + (_weightDirs[i] * 16) - }; - - var result = spaceState.IntersectRay(rayParams); - - // if our ray cast hits something - if (result.Count > 0) - { - // then we subtract the dot product of other directions - for (int j = 0; j < 16; j++) - { - if (i == j) - { - _weights[i] = 0; - } - else - { - float dot = _weightDirs[i].Dot(_weightDirs[j]) / 2; - _weights[j] -= (dot + 1) / 2; - } - } - } - } - } - - float bestWeight = 0; - - for (int i = 0; i < 16; i++) - { - if (_weights[i] > bestWeight) - { - bestWeight = _weights[i]; - _bestWeightIdx = i; - } - } - - QueueRedraw(); - #endif } } } diff --git a/Characters/Player.tscn b/Characters/Player.tscn index 513b8bf..d41bf2c 100644 --- a/Characters/Player.tscn +++ b/Characters/Player.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=29 format=3 uid="uid://b2254pup8k161"] +[gd_scene load_steps=28 format=3 uid="uid://b2254pup8k161"] [ext_resource type="Script" path="res://Characters/Player.cs" id="1_flygr"] [ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="2_ngsgt"] @@ -105,19 +105,11 @@ _data = { "RESET": SubResource("Animation_k6l16") } -[sub_resource type="Environment" id="Environment_rs2vs"] -background_mode = 3 -background_energy_multiplier = 16.0 -ambient_light_source = 3 -ambient_light_energy = 2.68 -glow_enabled = true -glow_intensity = 8.0 - [node name="Player" type="CharacterBody2D" node_paths=PackedStringArray("Camera", "Sprite", "Inventory", "StateMachine")] texture_filter = 3 material = SubResource("ShaderMaterial_h78y7") y_sort_enabled = true -collision_layer = 2 +collision_layer = 6 script = ExtResource("1_flygr") Camera = NodePath("Camera2D") Speed = 64.0 @@ -193,9 +185,6 @@ libraries = { "": SubResource("AnimationLibrary_xe5eq") } -[node name="WorldEnvironment" type="WorldEnvironment" parent="."] -environment = SubResource("Environment_rs2vs") - [node name="HurtSound" type="AudioStreamPlayer2D" parent="."] stream = ExtResource("12_h0x0g") diff --git a/Characters/States/PlayerState.cs b/Characters/States/PlayerState.cs index 4889bab..763fc59 100644 --- a/Characters/States/PlayerState.cs +++ b/Characters/States/PlayerState.cs @@ -42,8 +42,6 @@ namespace SupaLidlGame.Characters.State if (Character.Inventory.SelectedItem is not null) { Character.UseCurrentItem(); - //Character.Inventory.SelectedItem.Use(); - //return AttackState; } } diff --git a/Extensions/Node.cs b/Extensions/Node.cs index 3b160f5..2defb9c 100644 --- a/Extensions/Node.cs +++ b/Extensions/Node.cs @@ -26,9 +26,14 @@ namespace SupaLidlGame.Extensions } /// - /// + /// A version GetNode that returns null rather than cause an + /// exception if the node is not found or is not the same type. /// - public static T GetNode(this Node node, string name) where T : Node + /// + /// null if name does not match + /// a valid Node + /// + public static T GetN(this Node node, string name) where T : Node { return node.GetNode(name) as T; } diff --git a/Extensions/Node2DExtensions.cs b/Extensions/Node2DExtensions.cs new file mode 100644 index 0000000..1acaf32 --- /dev/null +++ b/Extensions/Node2DExtensions.cs @@ -0,0 +1,13 @@ +using Godot; + +namespace SupaLidlGame.Extensions +{ + public static class Node2DExtensions + { + public static void RayCast(this Node2D node, Vector2 ray) + { + //var spaceState = node.GetWorld2d().DirectSpaceState; + //var result = spaceState.IntersectRay(); + } + } +} diff --git a/Extensions/Vector2.cs b/Extensions/Vector2.cs new file mode 100644 index 0000000..4a83aef --- /dev/null +++ b/Extensions/Vector2.cs @@ -0,0 +1,39 @@ +using Godot; +using System.Linq; + +namespace SupaLidlGame.Extensions +{ + public static class Vector2Extensions + { + public static Vector2 Midpoint(this Vector2 vector, Vector2 other) + { + return new Vector2((vector.x + other.x) / 2, + (vector.y + other.y) / 2); + } + + public static Vector2 Midpoints(params Vector2[] vectors) + { + int length = vectors.Length; + float x = 0; + float y = 0; + + for (int i = 0; i < length; i++) + { + x += vectors[i].x; + y += vectors[i].y; + } + + return new Vector2(x / length, y / length); + } + + public static Vector2 Counterclockwise90(this Vector2 vector) + { + return new Vector2(-vector.y, vector.x); + } + + public static Vector2 Clockwise90(this Vector2 vector) + { + return new Vector2(vector.y, -vector.x); + } + } +} diff --git a/Items/Weapons/Sword.tscn b/Items/Weapons/Sword.tscn index bd17bf4..98ab60c 100644 --- a/Items/Weapons/Sword.tscn +++ b/Items/Weapons/Sword.tscn @@ -141,6 +141,52 @@ tracks/2/keys = { "values": [0, 1, 2, 0] } +[sub_resource type="Animation" id="Animation_nivo8"] +resource_name = "use-npc" +length = 1.5 +step = 0.05 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Anchor:rotation") +tracks/0/interp = 1 +tracks/0/loop_wrap = false +tracks/0/keys = { +"times": PackedFloat32Array(0.05, 0.3, 0.4, 0.45, 0.75, 1.5), +"transitions": PackedFloat32Array(1, 4, 1, 2, 4, 1), +"update": 3, +"values": [-0.610865, -1.309, 3.92699, 3.92699, 3.75246, -0.785398] +} +tracks/1/type = "method" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0.3, 0.45), +"transitions": PackedFloat32Array(1, 1), +"values": [{ +"args": [], +"method": &"Attack" +}, { +"args": [], +"method": &"Deattack" +}] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("SwingSprite:frame") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 0.4, 0.5, 0.6), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"update": 1, +"values": [0, 1, 2, 0] +} + [sub_resource type="Animation" id="Animation_y4mj3"] resource_name = "use2" length = 0.75 @@ -187,52 +233,6 @@ tracks/2/keys = { "values": [0, 1, 3, 0] } -[sub_resource type="Animation" id="Animation_nivo8"] -resource_name = "use-npc" -length = 1.5 -step = 0.05 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("Anchor:rotation") -tracks/0/interp = 1 -tracks/0/loop_wrap = false -tracks/0/keys = { -"times": PackedFloat32Array(0.05, 0.3, 0.4, 0.45, 0.75, 1.5), -"transitions": PackedFloat32Array(1, 4, 1, 2, 4, 1), -"update": 3, -"values": [-0.610865, -0.959931, 3.92699, 3.92699, 3.75246, -0.785398] -} -tracks/1/type = "method" -tracks/1/imported = false -tracks/1/enabled = true -tracks/1/path = NodePath(".") -tracks/1/interp = 1 -tracks/1/loop_wrap = true -tracks/1/keys = { -"times": PackedFloat32Array(0.3, 0.45), -"transitions": PackedFloat32Array(1, 1), -"values": [{ -"args": [], -"method": &"Attack" -}, { -"args": [], -"method": &"Deattack" -}] -} -tracks/2/type = "value" -tracks/2/imported = false -tracks/2/enabled = true -tracks/2/path = NodePath("SwingSprite:frame") -tracks/2/interp = 1 -tracks/2/loop_wrap = true -tracks/2/keys = { -"times": PackedFloat32Array(0, 0.4, 0.5, 0.6), -"transitions": PackedFloat32Array(1, 1, 1, 1), -"update": 1, -"values": [0, 1, 2, 0] -} - [sub_resource type="Animation" id="Animation_2k2er"] resource_name = "use2-npc" length = 0.75 @@ -247,7 +247,7 @@ tracks/0/keys = { "times": PackedFloat32Array(0.05, 0.3, 0.4, 0.45, 0.75), "transitions": PackedFloat32Array(1, 4, 1, 2, 1), "update": 3, -"values": [3.75246, 4.10152, -0.785398, -0.785398, -0.610865] +"values": [3.75246, 4.45059, -0.785398, -0.785398, -0.610865] } tracks/1/type = "method" tracks/1/imported = false diff --git a/Scenes/Level.tscn b/Scenes/Level.tscn index 8748a15..55613ed 100644 --- a/Scenes/Level.tscn +++ b/Scenes/Level.tscn @@ -1,9 +1,8 @@ -[gd_scene load_steps=9 format=3 uid="uid://dd6xy1y0m8smm"] +[gd_scene load_steps=6 format=3 uid="uid://dd6xy1y0m8smm"] [ext_resource type="Texture2D" uid="uid://gm2pcnfg7h8j" path="res://Assets/Sprites/tileset.png" id="1_k6myx"] [ext_resource type="PackedScene" uid="uid://b2254pup8k161" path="res://Characters/Player.tscn" id="1_m35hr"] [ext_resource type="PackedScene" uid="uid://ddcf6bfv212wj" path="res://Characters/ExampleEnemy.tscn" id="2_uti3y"] -[ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="4_056cf"] [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_1pa1q"] texture = ExtResource("1_k6myx") @@ -233,6 +232,7 @@ texture = ExtResource("1_k6myx") 7:3/0 = 0 7:3/0/physics_layer_0/linear_velocity = Vector2(0, 0) 7:3/0/physics_layer_0/angular_velocity = 0.0 +7:3/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, 2, 8, 2, 8, 12, -8, 12) 8:3/0 = 0 8:3/0/physics_layer_0/linear_velocity = Vector2(0, 0) 8:3/0/physics_layer_0/angular_velocity = 0.0 @@ -308,6 +308,7 @@ texture = ExtResource("1_k6myx") 6:4/0 = 0 6:4/0/physics_layer_0/linear_velocity = Vector2(0, 0) 6:4/0/physics_layer_0/angular_velocity = 0.0 +6:4/0/physics_layer_0/polygon_0/points = PackedVector2Array(-8, -1, 8, -1, 8, 8, -8, 8) 8:4/0 = 0 8:4/0/physics_layer_0/linear_velocity = Vector2(0, 0) 8:4/0/physics_layer_0/angular_velocity = 0.0 @@ -1101,18 +1102,6 @@ texture = ExtResource("1_k6myx") physics_layer_0/collision_layer = 1 sources/0 = SubResource("TileSetAtlasSource_1pa1q") -[sub_resource type="ShaderMaterial" id="ShaderMaterial_whp32"] -resource_local_to_scene = true -shader = ExtResource("4_056cf") -shader_parameter/color = null -shader_parameter/intensity = 0.0 - -[sub_resource type="ShaderMaterial" id="ShaderMaterial_rthg0"] -resource_local_to_scene = true -shader = ExtResource("4_056cf") -shader_parameter/color = null -shader_parameter/intensity = 0.0 - [node name="Level" type="Node2D"] [node name="TileMap" type="TileMap" parent="."] @@ -1122,7 +1111,7 @@ tile_set = SubResource("TileSet_18c7j") format = 2 layer_0/name = "Walls" layer_0/y_sort_enabled = true -layer_0/tile_data = PackedInt32Array(131065, 458752, 0, -65531, 458752, 0) +layer_0/tile_data = PackedInt32Array(131065, 458752, 0, -65531, 458752, 0, 327679, 458752, 3, 327687, 458752, 3) layer_1/name = "Ground 2" layer_1/enabled = true layer_1/modulate = Color(1, 1, 1, 1) @@ -1150,11 +1139,21 @@ position = Vector2(-81, -34) motion_mode = 1 [node name="ExampleEnemy" parent="TileMap" instance=ExtResource("2_uti3y")] -material = SubResource("ShaderMaterial_whp32") position = Vector2(38, 42) scale = Vector2(1.00571, 1) [node name="ExampleEnemy2" parent="TileMap" instance=ExtResource("2_uti3y")] -material = SubResource("ShaderMaterial_rthg0") position = Vector2(190, 99) scale = Vector2(1.00571, 1) + +[node name="ExampleEnemy3" parent="TileMap" instance=ExtResource("2_uti3y")] +position = Vector2(175, 2) +scale = Vector2(1.00571, 1) + +[node name="ExampleEnemy4" parent="TileMap" instance=ExtResource("2_uti3y")] +position = Vector2(122, 36) +scale = Vector2(1.00571, 1) + +[node name="ExampleEnemy5" parent="TileMap" instance=ExtResource("2_uti3y")] +position = Vector2(207, 56) +scale = Vector2(1.00571, 1) diff --git a/Tests/ContextBasedSteering.cs b/Tests/ContextBasedSteering.cs index 2bacd73..17354c7 100644 --- a/Tests/ContextBasedSteering.cs +++ b/Tests/ContextBasedSteering.cs @@ -1,99 +1,58 @@ using Godot; +using SupaLidlGame.Extensions; using System; namespace SupaLidlGame.Prototyping { public partial class ContextBasedSteering : Node2D { - float[] _weights = new float[16]; - Vector2[] _weightDirs = new Vector2[16]; - int _bestWeightIdx; + public float PreferredDistance { get; set; } = 256.0f; + Vector2 _direction; // Called when the node enters the scene tree for the first time. public override void _Ready() { - Array.Fill(_weights, 1); - for (int i = 0; i < 16; i++) - { - float y = Mathf.Sin(Mathf.Pi * i * 2 / 16); - float x = Mathf.Cos(Mathf.Pi * i * 2 / 16); - _weightDirs[i] = new Vector2(x, y); - } + GD.Print("Started ContextBasedSteering test"); } // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { - Vector2 pos = GetLocalMousePosition(); - Vector2 dir = GlobalPosition.DirectionTo(pos); - float dist = GlobalPosition.DistanceSquaredTo(pos); - - for (int i = 0; i < 16; i++) - { - float directDot = _weightDirs[i].Dot(dir); - directDot = (directDot + 1) / 2; - - // this dot product resembles values of sine rather than cosine - // use it to weigh direction horizontally - Vector2 rotatedDir = new Vector2(-dir.y, dir.x); - float horizDot = Math.Abs(_weightDirs[i].Dot(rotatedDir)); - // square so lower values are even lower - horizDot = Mathf.Pow((horizDot + 1) / 2, 2); - - // "When will I use math in the real world" Clueful - - - if (dist > 1024) - { - _weights[i] = directDot; - } - else if (dist > 256) - { - float directDotWeighting = (dist - 256) / 768; - float horizDotWeighting = 1 - directDotWeighting; - - _weights[i] = (directDot * directDotWeighting) + - (horizDot * horizDotWeighting); - } - else - { - // shorter than 256 - _weights[i] = horizDot; - } - - // now we shall subtract weights whose rays collide - // with something - - { - - } - } - - float bestWeight = 0; - for (int i = 0; i < 16; i++) - { - if (_weights[i] > bestWeight) - { - bestWeight = _weights[i]; - _bestWeightIdx = i; - } - } - + _direction = GetDirection(); QueueRedraw(); } + public Vector2 GetDirection() + { + float directWeight; + float strafeWeight; + + Vector2 towards = GetGlobalMousePosition() - GlobalPosition; + float dist = towards.Length(); + + Vector2 directDir = towards.Normalized(); + Vector2 strafeDir = directDir.Clockwise90(); + + // weights approach 1 + // dy/dx = 1 - y + // y = 1 - e^(-x) + + directWeight = 1 - Mathf.Pow(Mathf.E, -(dist / PreferredDistance)); + strafeWeight = 1 - directWeight; + + /* + Vector2 midpoint = (strafeDir * strafeWeight) + .Midpoint(directDir * directWeight); + */ + Vector2 midpoint = (directDir * directWeight) + .Midpoint(strafeDir * strafeWeight); + + return midpoint.Normalized(); + } + public override void _Draw() { - for (int i = 0; i < 16; i++) - { - Vector2 vec = _weightDirs[i] * _weights[i] * 128; - Color c = Colors.Green; - if (_bestWeightIdx == i) - { - c = Colors.Blue; - } - DrawLine(GlobalPosition, GlobalPosition + vec, c); - } + DrawLine(Vector2.Zero, _direction * 256, Colors.Green); } } } diff --git a/Tests/ContextBasedSteeringTest.tscn b/Tests/ContextBasedSteeringTest.tscn index a075f47..88e46fc 100644 --- a/Tests/ContextBasedSteeringTest.tscn +++ b/Tests/ContextBasedSteeringTest.tscn @@ -1,9 +1,13 @@ -[gd_scene load_steps=2 format=3 uid="uid://c2d4dmf4yg481"] +[gd_scene load_steps=3 format=3 uid="uid://c2d4dmf4yg481"] [ext_resource type="Script" path="res://Tests/ContextBasedSteering.cs" id="1_g16e3"] +[ext_resource type="Texture2D" uid="uid://bw052v8ikfget" path="res://icon.svg" id="2_cvdwc"] [node name="ContextBasedSteeringTest" type="Node2D"] [node name="ContextBasedSteering" type="Node2D" parent="."] position = Vector2(410, 242) script = ExtResource("1_g16e3") + +[node name="Icon" type="Sprite2D" parent="ContextBasedSteering"] +texture = ExtResource("2_cvdwc") diff --git a/UI/HealthBar.tscn b/UI/HealthBar.tscn new file mode 100644 index 0000000..deac9df --- /dev/null +++ b/UI/HealthBar.tscn @@ -0,0 +1,18 @@ +[gd_scene load_steps=2 format=3 uid="uid://bxo553hblp6nf"] + +[ext_resource type="FontFile" uid="uid://cgwa8bjiyv534" path="res://Assets/Fonts/alagard.ttf" id="1_700b8"] + +[node name="HealthBar" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="ProgressBar" type="ProgressBar" parent="."] +offset_right = 128.0 +offset_bottom = 32.0 +theme_override_fonts/font = ExtResource("1_700b8") +step = 1.0 +value = 84.0 diff --git a/export_presets.cfg b/export_presets.cfg index b9ed951..90258ed 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -7,7 +7,7 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="" -export_path="./SupaLidlGame.exe" +export_path="Build/SupaLidlGame.exe" encryption_include_filters="" encryption_exclude_filters="" encrypt_pck=false @@ -45,3 +45,33 @@ application/product_name="" application/file_description="" application/copyright="" application/trademarks="" + +[preset.1] + +name="Linux/X11" +platform="Linux/X11" +runnable=true +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_export_mode=1 +script_encryption_key="" + +[preset.1.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_script=1 +binary_format/embed_pck=false +texture_format/bptc=false +texture_format/s3tc=true +texture_format/etc=false +texture_format/etc2=false +texture_format/no_bptc_fallbacks=true +binary_format/architecture="x86_64" diff --git a/project.godot b/project.godot index bafb510..dbf8aca 100644 --- a/project.godot +++ b/project.godot @@ -62,6 +62,13 @@ equip={ ] } +[layer_names] + +2d_physics/layer_1="World" +2d_physics/layer_2="Character" +2d_physics/layer_3="Player" +2d_physics/layer_4="NPC" + [physics] 2d/default_gravity=0.0