From fcb9c060813cac875237157da4ea906fe13f7310 Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Mon, 19 Aug 2024 16:53:54 +0300 Subject: [PATCH] chore: init --- .gitattributes | 2 + .gitignore | 3 + LICENSE.txt | 21 ++++ README.md | 17 ++++ addons/boid_2d/boid.gd | 121 +++++++++++++++++++++++ addons/boid_2d/boid.tscn | 34 +++++++ addons/boid_2d/boid_2d.gd | 10 ++ addons/boid_2d/boid_2d.svg | 78 +++++++++++++++ addons/boid_2d/boid_2d.svg.import | 38 +++++++ addons/boid_2d/plugin.cfg | 7 ++ examples/boid_2d/example_boid.svg | 65 ++++++++++++ examples/boid_2d/example_boid.svg.import | 37 +++++++ examples/boid_2d/example_boid.tscn | 15 +++ examples/boid_2d/follow/example.gd | 18 ++++ examples/boid_2d/follow/example.tscn | 19 ++++ examples/boid_2d/simple/example.gd | 13 +++ examples/boid_2d/simple/example.tscn | 12 +++ project.godot | 24 +++++ resources/boid_2d.png | Bin 0 -> 3690 bytes resources/boid_2d.png.import | 34 +++++++ 20 files changed, 568 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 addons/boid_2d/boid.gd create mode 100644 addons/boid_2d/boid.tscn create mode 100644 addons/boid_2d/boid_2d.gd create mode 100644 addons/boid_2d/boid_2d.svg create mode 100644 addons/boid_2d/boid_2d.svg.import create mode 100644 addons/boid_2d/plugin.cfg create mode 100644 examples/boid_2d/example_boid.svg create mode 100644 examples/boid_2d/example_boid.svg.import create mode 100644 examples/boid_2d/example_boid.tscn create mode 100644 examples/boid_2d/follow/example.gd create mode 100644 examples/boid_2d/follow/example.tscn create mode 100644 examples/boid_2d/simple/example.gd create mode 100644 examples/boid_2d/simple/example.tscn create mode 100644 project.godot create mode 100644 resources/boid_2d.png create mode 100644 resources/boid_2d.png.import diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0af181c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Godot 4+ specific ignores +.godot/ +/android/ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..1b1e6e2 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 yusdacra + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f33ea2 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# boid_2d + +Addon for Godot that adds a 2D node for simulating boids / flocking. + +![boids](./resources/boids.gif) + +## Usage + +Clone the repository and copy over the addon. +Make an inherited scene from `boid.tscn`, add a `Sprite2D` (or whatever visuals you have) and instantiate and spawn many. +Checkout the examples on how to use it more. + +## TODO + +- [ ] fix weird spasming behaviour +- [ ] improve collision (dont only bounce, maybe follow wall in some conditions etc.) +- [ ] improve performance (BoidManager autoload that tracks and manages every boid?) diff --git a/addons/boid_2d/boid.gd b/addons/boid_2d/boid.gd new file mode 100644 index 0000000..1c8d3b5 --- /dev/null +++ b/addons/boid_2d/boid.gd @@ -0,0 +1,121 @@ +extends Area2D +class_name Boid + + +## sets the `RayCast2D` used to detect walls. +@export var wallcast: RayCast2D +## sets the `Area2D` used for vision (seeing other boids). +@export var vision: Area2D +## sets the rotate timer, allowing boids to perform random rotations based on the timer's timeout signal. +@export var rotate_timer: Timer + +@export_group("properties") +## controls the target (max) speed. +@export var target_speed := 6.0 +## controls how much other boids affect this boid. +## higher values will make them more dispersed. +@export var steer_away_factor := 40 +## controls whether or not to run collisions before running boid calculations. +## enabling this can help reduce boids escaping colliders, especially if they are following something. +@export var collide_first := false + +@export_group("follow") +## controls which node to try and follow, if any +@export var follow_point: Node2D +## controls the radius at which the boid will target, instead of the target directly +@export var follow_radius := 100.0 + +var last_follow_pos: Vector2 = Vector2.ZERO +var follow_target: Vector2 +var speed := target_speed +var vel := Vector2.ZERO +var boidsSeen: Dictionary = {} + + +func _ready() -> void: + assert(wallcast, "boid invalid: wallcast (RayCast3D) not assigned") + assert(vision, "boid invalid: vision (Area2D) not assigned") + if rotate_timer: + rotate_timer.timeout.connect(_on_rotate_timer_timeout) + + +func _physics_process(delta: float) -> void: + if collide_first: + _process_collision() + _process_boids() + else: + _process_boids() + _process_collision() + # move boid + var vel_dir := vel.normalized() + # fix if a boid stops by getting seperated and its vel being cancelled at the same time + if vel_dir.is_zero_approx(): vel_dir = Vector2.RIGHT + vel = vel_dir * speed + global_position += vel + # rotate boid + global_rotation = atan2(vel_dir.y, vel_dir.x) + + +func _process_boids() -> void: + var numOfBoids := boidsSeen.size() + var avgVel := Vector2.ZERO + var avgPos := Vector2.ZERO + var steerAway := Vector2.ZERO + if numOfBoids > 0: + for boid: Boid in boidsSeen.values(): + avgVel += boid.vel; avgPos += boid.global_position + var dist := boid.global_position - global_position + steerAway -= dist * (steer_away_factor / dist.length()) + + # apply follow point vel + if follow_point: + var dist_to_follow := global_position.distance_to(follow_point.global_position) + if global_position.distance_to(follow_target) < 10.0 or dist_to_follow > follow_radius: + _calc_follow_target() + # slow down speed when nearing target + speed = maxf(0.0, lerpf(target_speed, 0.0, follow_radius / dist_to_follow)) + var target_vel := (follow_point.global_position - last_follow_pos) * Engine.physics_ticks_per_second + avgVel += target_vel + avgPos += follow_target + var dist := follow_target - global_position + steerAway -= dist * ((steer_away_factor + follow_radius) / dist.length()) + numOfBoids += 1 + last_follow_pos = follow_point.global_position + + if numOfBoids > 0: + avgVel /= numOfBoids + vel += (avgVel - vel) / 2 + + avgPos /= numOfBoids + vel += avgPos - global_position + + steerAway /= numOfBoids + vel += steerAway + + +func _calc_follow_target() -> void: + var follow_vec := follow_point.global_position - global_position + var target_length := follow_vec.length() + follow_radius + follow_target = global_position + follow_vec.normalized() * target_length + + +func _process_collision() -> void: + wallcast.force_raycast_update() + if not wallcast.is_colliding(): return + + var col_normal: Vector2 = wallcast.get_collision_normal() + vel = vel.bounce(col_normal) + + +func _on_vision_area_entered(area: Area2D) -> void: + if area == self: return + boidsSeen[area.get_instance_id()] = area + + +func _on_vision_area_exited(area: Area2D) -> void: + boidsSeen.erase(area.get_instance_id()) + + +func _on_rotate_timer_timeout() -> void: + vel -= Vector2(randf(), randf()) * speed + rotate_timer.start() diff --git a/addons/boid_2d/boid.tscn b/addons/boid_2d/boid.tscn new file mode 100644 index 0000000..0217c0e --- /dev/null +++ b/addons/boid_2d/boid.tscn @@ -0,0 +1,34 @@ +[gd_scene load_steps=4 format=3 uid="uid://bq7s2yf0fohes"] + +[ext_resource type="Script" path="res://addons/boid_2d/boid.gd" id="1_xwhwb"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_ackok"] +size = Vector2(32, 16) + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_ipkm3"] +size = Vector2(60, 60) + +[node name="Boid" type="Area2D" node_paths=PackedStringArray("wallcast", "vision")] +collision_mask = 0 +monitoring = false +script = ExtResource("1_xwhwb") +wallcast = NodePath("WallCast") +vision = NodePath("Vision") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("RectangleShape2D_ackok") + +[node name="WallCast" type="RayCast2D" parent="."] +enabled = false +target_position = Vector2(50, 0) + +[node name="Vision" type="Area2D" parent="."] +collision_layer = 0 +monitorable = false + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Vision"] +position = Vector2(42, 0) +shape = SubResource("RectangleShape2D_ipkm3") + +[connection signal="area_entered" from="Vision" to="." method="_on_vision_area_entered"] +[connection signal="area_exited" from="Vision" to="." method="_on_vision_area_exited"] diff --git a/addons/boid_2d/boid_2d.gd b/addons/boid_2d/boid_2d.gd new file mode 100644 index 0000000..1909b35 --- /dev/null +++ b/addons/boid_2d/boid_2d.gd @@ -0,0 +1,10 @@ +@tool +extends EditorPlugin + + +func _enter_tree() -> void: + add_custom_type("Boid2D", "Area2D", preload("boid.gd"), preload("boid_2d.svg")) + + +func _exit_tree() -> void: + remove_custom_type("Boid2D") diff --git a/addons/boid_2d/boid_2d.svg b/addons/boid_2d/boid_2d.svg new file mode 100644 index 0000000..ca169d2 --- /dev/null +++ b/addons/boid_2d/boid_2d.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + diff --git a/addons/boid_2d/boid_2d.svg.import b/addons/boid_2d/boid_2d.svg.import new file mode 100644 index 0000000..a686f80 --- /dev/null +++ b/addons/boid_2d/boid_2d.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c32akbxu1rkj8" +path="res://.godot/imported/boid_2d.svg-172f46790795ecd4cef523288fe60368.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/boid_2d/boid_2d.svg" +dest_files=["res://.godot/imported/boid_2d.svg-172f46790795ecd4cef523288fe60368.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/boid_2d/plugin.cfg b/addons/boid_2d/plugin.cfg new file mode 100644 index 0000000..e80610e --- /dev/null +++ b/addons/boid_2d/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Boid2D" +description="Addon for implementing boids / flocking in Godot." +author="yusdacra" +version="0.1" +script="boid_2d.gd" diff --git a/examples/boid_2d/example_boid.svg b/examples/boid_2d/example_boid.svg new file mode 100644 index 0000000..ffc25c7 --- /dev/null +++ b/examples/boid_2d/example_boid.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + diff --git a/examples/boid_2d/example_boid.svg.import b/examples/boid_2d/example_boid.svg.import new file mode 100644 index 0000000..845b073 --- /dev/null +++ b/examples/boid_2d/example_boid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://rk5u1wthr0n0" +path="res://.godot/imported/example_boid.svg-ebae3589d3b59182aead052ab0bb5c16.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://examples/boid_2d/example_boid.svg" +dest_files=["res://.godot/imported/example_boid.svg-ebae3589d3b59182aead052ab0bb5c16.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=6.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/examples/boid_2d/example_boid.tscn b/examples/boid_2d/example_boid.tscn new file mode 100644 index 0000000..ac55e9d --- /dev/null +++ b/examples/boid_2d/example_boid.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=3 format=3 uid="uid://bcyffgnn2ahl3"] + +[ext_resource type="PackedScene" uid="uid://bq7s2yf0fohes" path="res://addons/boid_2d/boid.tscn" id="1_825c0"] +[ext_resource type="Texture2D" uid="uid://rk5u1wthr0n0" path="res://examples/boid_2d/example_boid.svg" id="2_qfbgc"] + +[node name="Boid" instance=ExtResource("1_825c0")] +collision_layer = 2 + +[node name="Vision" parent="." index="2"] +collision_mask = 2 + +[node name="Sprite2D" type="Sprite2D" parent="." index="3"] +position = Vector2(-2.17226e-06, 2.38419e-07) +scale = Vector2(0.111111, 0.111111) +texture = ExtResource("2_qfbgc") diff --git a/examples/boid_2d/follow/example.gd b/examples/boid_2d/follow/example.gd new file mode 100644 index 0000000..c6c5cd2 --- /dev/null +++ b/examples/boid_2d/follow/example.gd @@ -0,0 +1,18 @@ +extends Node2D + + +func _ready() -> void: + for i in 40: spawnBoid() + + +func _process(delta: float) -> void: + $Path2D/PathFollow2D.progress_ratio += delta * 0.1 + + +func spawnBoid() -> void: + var boid: Boid = preload("../example_boid.tscn").instantiate() + var screensize := get_viewport_rect().size + boid.modulate = Color(randf(), randf(), randf(), 1) + boid.global_position = Vector2((randf_range(200, screensize.x - 200)), (randf_range(200, screensize.y - 200))) + boid.follow_point = $Path2D/PathFollow2D + add_child(boid) diff --git a/examples/boid_2d/follow/example.tscn b/examples/boid_2d/follow/example.tscn new file mode 100644 index 0000000..14a1d93 --- /dev/null +++ b/examples/boid_2d/follow/example.tscn @@ -0,0 +1,19 @@ +[gd_scene load_steps=3 format=3 uid="uid://ckc0dhvrksfh4"] + +[ext_resource type="Script" path="res://examples/boid_2d/follow/example.gd" id="1_cb4mx"] + +[sub_resource type="Curve2D" id="Curve2D_ncwi0"] +_data = { +"points": PackedVector2Array(69.2957, 57.9564, -69.2957, -57.9564, 1117, 34, 79.375, -118.433, -79.375, 118.433, 34, 28, -61.7361, -114.653, 61.7361, 114.653, 22, 624, -42.8373, 69.2957, 42.8373, -69.2957, 1126, 622, 66.7758, 73.0754, -66.7758, -73.0754, 1117, 34) +} +point_count = 5 + +[node name="Example" type="Node2D"] +script = ExtResource("1_cb4mx") + +[node name="Path2D" type="Path2D" parent="."] +curve = SubResource("Curve2D_ncwi0") + +[node name="PathFollow2D" type="PathFollow2D" parent="Path2D"] +position = Vector2(1117, 34) +rotation = -2.44507 diff --git a/examples/boid_2d/simple/example.gd b/examples/boid_2d/simple/example.gd new file mode 100644 index 0000000..fe1c074 --- /dev/null +++ b/examples/boid_2d/simple/example.gd @@ -0,0 +1,13 @@ +extends Node2D + + +func _ready() -> void: + for i in 100: spawnBoid() + + +func spawnBoid() -> void: + var boid: Boid = preload("../example_boid.tscn").instantiate() + var screensize := get_viewport_rect().size + boid.modulate = Color(randf(), randf(), randf(), 1) + boid.global_position = Vector2((randf_range(200, screensize.x - 200)), (randf_range(200, screensize.y - 200))) + add_child(boid) diff --git a/examples/boid_2d/simple/example.tscn b/examples/boid_2d/simple/example.tscn new file mode 100644 index 0000000..3652ef8 --- /dev/null +++ b/examples/boid_2d/simple/example.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=2 format=3 uid="uid://op0qicvpbjt6"] + +[ext_resource type="Script" path="res://examples/boid_2d/simple/example.gd" id="1_3gcrf"] + +[node name="Example" type="Node2D"] +script = ExtResource("1_3gcrf") + +[node name="StaticBody2D" type="StaticBody2D" parent="."] +collision_priority = 4.0 + +[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="StaticBody2D"] +polygon = PackedVector2Array(1152, 0, 1152, 648, 0, 648, 0, 0, 1088, 0, 1088, 64, 64, 64, 64, 576, 1088, 576, 1088, 0) diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..8372d9d --- /dev/null +++ b/project.godot @@ -0,0 +1,24 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="Boid Addon" +run/main_scene="res://examples/boid_2d/simple/example.tscn" +config/features=PackedStringArray("4.3", "GL Compatibility") + +[editor_plugins] + +enabled=PackedStringArray("res://addons/boid_2d/plugin.cfg") + +[rendering] + +renderer/rendering_method="gl_compatibility" +renderer/rendering_method.mobile="gl_compatibility" diff --git a/resources/boid_2d.png b/resources/boid_2d.png new file mode 100644 index 0000000000000000000000000000000000000000..5176eb7d6de8b7f88b1b33902e8a2664b2ae90e8 GIT binary patch literal 3690 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 IM6N<$f_w__U;qFB literal 0 HcmV?d00001 diff --git a/resources/boid_2d.png.import b/resources/boid_2d.png.import new file mode 100644 index 0000000..a446fe4 --- /dev/null +++ b/resources/boid_2d.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cp6l2f1oac4ce" +path="res://.godot/imported/boid_2d.png-b56b7a427c09dbbdb1e601cb745d4254.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://resources/boid_2d.png" +dest_files=["res://.godot/imported/boid_2d.png-b56b7a427c09dbbdb1e601cb745d4254.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1