added donut colisions

This commit is contained in:
Tabby 2025-08-07 11:34:04 +10:00
parent 0de9e32a02
commit 69c260b1e8
33 changed files with 527 additions and 2 deletions

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 angrykoala
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.

View file

@ -0,0 +1,26 @@
# Donut Collision Polygon 2D for Godot
_by @angrykoala_
A donut-shaped collision shape for Godot.
This plugin creates a new Node type for a donut collision shape for 2D collisions.
![Donut Example](screenshots/donut_example.png)
![Donut Rectangle](screenshots/donut_rectangle_example.png)
## Instructions
1. Add the folder `donut_collision_polygon2D` to `addons/`.
2. Activate the plugin in [Godot](https://docs.godotengine.org/en/stable/tutorials/plugins/editor/installing_plugins.html).
3. Create any collision or physics 2D node (e.g. `Area2D` or `RigidBody2D`).
4. Add a `DonutCollisionPolygon2D` or `DonutCollisionRectangle2D` as child.
![Tree Example](screenshots/tree_example.png)
## Properties
* **radius**: Defines the radius of the donut. This will be the circumference at the **center** of the donut.
* **width**: The width of the donut.
* **quality**: The number of points to use **per circumference**. The total points count will be `quality*2+1`.
## License
Made by [@angrykoala](https://github.com/angrykoala). [MIT License](LICENSE)

View file

@ -0,0 +1,48 @@
@tool
class_name DonutCollisionPolygon2D
extends CollisionPolygon2D
## A Collider in the shape of a Donut
@export var radius: float = 10.0:
set(rad):
radius = rad
update_polygons()
@export var width: float = 2.0:
set(w):
width = w
update_polygons()
@export_range(5, 256) var quality: int = 32:
set(q):
quality = q
update_polygons()
func _ready() -> void:
build_mode = CollisionPolygon2D.BUILD_SOLIDS
update_polygons()
func update_polygons() -> void:
var points := get_polygon_points(Vector2(0,0), radius)
polygon = points
func get_polygon_points(center: Vector2, radius: float) -> PackedVector2Array:
var half_width := width / 2
var inner_circle := get_circle_points(center, radius - half_width)
var outer_circle := get_circle_points(center, radius + half_width)
inner_circle.reverse()
var points := PackedVector2Array()
points.append_array(outer_circle)
points.append_array(inner_circle)
points.append(outer_circle[0] + Vector2(0.0001, 0.0001))
return points
func get_circle_points(center: Vector2, radius: float, angle_from: float = 0, angle_to: float = 360) -> PackedVector2Array:
var nb_points := 16
var points_arc := PackedVector2Array()
for i in range(quality + 1):
var angle_point := deg_to_rad(angle_from + i * (angle_to - angle_from) / quality - 90)
points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
return points_arc

View file

@ -0,0 +1,4 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<circle cx="8" cy="8" r="7" stroke="#8da5f3" stroke-width="2" fill="none" />
<circle cx="8" cy="8" r="4" stroke="#8da5f3" stroke-width="2" fill="none" />
</svg>

After

Width:  |  Height:  |  Size: 251 B

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bcwggi4xo00lm"
path="res://.godot/imported/donut_collision_polygon2D.svg-84f3efc5ce2b2fce93bc5753624e2eb6.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/godot-donut-collision-polygon-2d-7780d026ae3d4a37fe68f43cc1f97ba664dd775d/addons/donut_collision_polygon2D/donut_collision_polygon2D.svg"
dest_files=["res://.godot/imported/donut_collision_polygon2D.svg-84f3efc5ce2b2fce93bc5753624e2eb6.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=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,46 @@
@tool
class_name DonutCollisionRectangle
extends CollisionPolygon2D
## A Collider in the shape of a Rectangle with a hole in the middle
@export var size := Vector2(10.0, 20.0):
set(new_size):
size = new_size
update_polygons()
@export var width: float = 2.0:
set(new_width):
width = new_width
update_polygons()
func _ready() -> void:
build_mode = CollisionPolygon2D.BUILD_SOLIDS
update_polygons()
func update_polygons() -> void:
var points := get_rectangle_points(size)
polygon = points
func get_rectangle_points(size: Vector2) -> PackedVector2Array:
var half_width := width / 2
var inner_rectangle := get_rectangle_corners(size - Vector2(half_width, half_width))
var outer_rectangle := get_rectangle_corners(size + Vector2(half_width, half_width))
inner_rectangle.reverse()
var points := PackedVector2Array()
points.append_array(outer_rectangle)
points.append(outer_rectangle[0]) # Connect the last point of the outer rectangle to the first point
points.append_array(inner_rectangle)
points.append(inner_rectangle[0]) # Connect the last point of the inner rectangle to the first point
return points
func get_rectangle_corners(size: Vector2) -> PackedVector2Array:
var half_size := size / 2
var points := PackedVector2Array()
points.push_back(Vector2(-half_size.x, -half_size.y))
points.push_back(Vector2(half_size.x, -half_size.y))
points.push_back(Vector2(half_size.x, half_size.y))
points.push_back(Vector2(-half_size.x, half_size.y))
return points

View file

@ -0,0 +1,4 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="1" width="14" height="14" stroke="#8da5f3" stroke-width="2" fill="none" />
<rect x="4" y="4" width="8" height="8" stroke="#8da5f3" stroke-width="2" fill="none" />
</svg>

After

Width:  |  Height:  |  Size: 270 B

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dn8hljsdwd42q"
path="res://.godot/imported/donut_collision_rectangle2D.svg-1850bd2eee6a5b0e414b6b5601b42d81.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/godot-donut-collision-polygon-2d-7780d026ae3d4a37fe68f43cc1f97ba664dd775d/addons/donut_collision_polygon2D/donut_collision_rectangle2D.svg"
dest_files=["res://.godot/imported/donut_collision_rectangle2D.svg-1850bd2eee6a5b0e414b6b5601b42d81.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=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,7 @@
[plugin]
name="DonutCollisionPolygon2D"
description="A donut-shaped collision polygon"
author="angrykoala"
version="1.2"
script="plugin.gd"

View file

@ -0,0 +1,11 @@
@tool
extends EditorPlugin
func _enter_tree():
add_custom_type("DonutCollisionPolygon2D", "CollisionPolygon2D", preload("donut_collision_polygon2D.gd"), preload("donut_collision_polygon2D.svg"))
add_custom_type("DonutCollisionRectangle2D", "CollisionPolygon2D", preload("donut_collision_rectangle2D.gd"),preload("donut_collision_rectangle2D.svg"))
func _exit_tree():
remove_custom_type("DonutCollisionPolygon2D")
remove_custom_type("DonutCollisionRectangle2D")

View file

@ -0,0 +1,7 @@
[gd_resource type="Environment" load_steps=2 format=2]
[sub_resource type="ProceduralSky" id=1]
[resource]
background_mode = 2
background_sky = SubResource( 1 )

View file

@ -0,0 +1,26 @@
[gd_scene load_steps=4 format=3 uid="uid://drd1j4dv673ld"]
[ext_resource type="Script" path="res://example/example_area.gd" id="1"]
[ext_resource type="Script" path="res://addons/donut_collision_polygon2D/donut_collision_polygon2D.gd" id="2"]
[ext_resource type="Texture2D" uid="uid://xfkvttl8cxns" path="res://addons/godot-donut-collision-polygon-2d-7780d026ae3d4a37fe68f43cc1f97ba664dd775d/example/icon.png" id="3"]
[node name="Example" type="Node2D"]
[node name="Area2D" type="Area2D" parent="."]
script = ExtResource("1")
[node name="DonutCollisionPolygon2D" type="CollisionPolygon2D" parent="Area2D"]
polygon = PackedVector2Array(1.01033e-14, -165, 32.1899, -161.83, 63.1428, -152.44, 91.6691, -137.192, 116.673, -116.673, 137.192, -91.6691, 152.44, -63.1428, 161.83, -32.1899, 165, 0, 161.83, 32.1899, 152.44, 63.1428, 137.192, 91.6691, 116.673, 116.673, 91.6691, 137.192, 63.1428, 152.44, 32.1899, 161.83, 1.01033e-14, 165, -32.1899, 161.83, -63.1428, 152.44, -91.6691, 137.192, -116.673, 116.673, -137.192, 91.6691, -152.44, 63.1428, -161.83, 32.1899, -165, 2.02067e-14, -161.83, -32.1899, -152.44, -63.1428, -137.192, -91.6691, -116.673, -116.673, -91.6691, -137.192, -63.1428, -152.44, -32.1899, -161.83, -3.031e-14, -165, -2.47991e-14, -135, -26.3372, -132.406, -51.6623, -124.724, -75.002, -112.248, -95.4594, -95.4594, -112.248, -75.002, -124.724, -51.6623, -132.406, -26.3372, -135, 1.65327e-14, -132.406, 26.3372, -124.724, 51.6623, -112.248, 75.002, -95.4594, 95.4594, -75.002, 112.248, -51.6623, 124.724, -26.3372, 132.406, 8.26637e-15, 135, 26.3372, 132.406, 51.6623, 124.724, 75.002, 112.248, 95.4594, 95.4594, 112.248, 75.002, 124.724, 51.6623, 132.406, 26.3372, 135, 0, 132.406, -26.3372, 124.724, -51.6623, 112.248, -75.002, 95.4594, -95.4594, 75.002, -112.248, 51.6623, -124.724, 26.3372, -132.406, 8.26637e-15, -135, 0.0001, -165)
script = ExtResource("2")
radius = 150.0
width = 30.0
[node name="Camera2D" type="Camera2D" parent="."]
[node name="icon" type="Sprite2D" parent="."]
scale = Vector2(3, 3)
texture = ExtResource("3")
[connection signal="input_event" from="Area2D" to="Area2D" method="_on_input_event"]
[connection signal="mouse_entered" from="Area2D" to="Area2D" method="_on_mouse_enter"]
[connection signal="mouse_exited" from="Area2D" to="Area2D" method="_on_mouse_exit"]

View file

@ -0,0 +1,16 @@
extends Area2D
func _on_mouse_enter() -> void:
print("Mouse Enter")
func _on_mouse_exit() -> void:
print("Mouse Exit")
func _on_input_event(_viewport: Node, event: InputEvent, _shape_idx: int) -> void:
if event is InputEventMouseButton \
and event.button_index == MOUSE_BUTTON_LEFT \
and event.is_pressed():
print("On Mouse Click")

View file

@ -0,0 +1,26 @@
[gd_scene load_steps=4 format=3 uid="uid://vogtjcpnjohc"]
[ext_resource type="Script" path="res://example/example_area.gd" id="1_fnxl8"]
[ext_resource type="Script" path="res://addons/donut_collision_polygon2D/donut_collision_rectangle2D.gd" id="2_xud1h"]
[ext_resource type="Texture2D" uid="uid://xfkvttl8cxns" path="res://addons/godot-donut-collision-polygon-2d-7780d026ae3d4a37fe68f43cc1f97ba664dd775d/example/icon.png" id="3_qnowy"]
[node name="ExampleSquare" type="Node2D"]
[node name="Area2D" type="Area2D" parent="."]
script = ExtResource("1_fnxl8")
[node name="DonutCollisionRectangle2D" type="CollisionPolygon2D" parent="Area2D"]
polygon = PackedVector2Array(-160, -160, 160, -160, 160, 160, -160, 160, -160, -160, -140, 140, 140, 140, 140, -140, -140, -140, -140, 140)
script = ExtResource("2_xud1h")
size = Vector2(300, 300)
width = 40.0
[node name="Camera2D" type="Camera2D" parent="."]
[node name="icon" type="Sprite2D" parent="."]
scale = Vector2(3, 3)
texture = ExtResource("3_qnowy")
[connection signal="input_event" from="Area2D" to="Area2D" method="_on_input_event"]
[connection signal="mouse_entered" from="Area2D" to="Area2D" method="_on_mouse_enter"]
[connection signal="mouse_exited" from="Area2D" to="Area2D" method="_on_mouse_exit"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://xfkvttl8cxns"
path="res://.godot/imported/icon.png-7eea17e57ed3675faa8b72817d0880ed.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/godot-donut-collision-polygon-2d-7780d026ae3d4a37fe68f43cc1f97ba664dd775d/example/icon.png"
dest_files=["res://.godot/imported/icon.png-7eea17e57ed3675faa8b72817d0880ed.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

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://byywtrj3eq3wm"
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.png"
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.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

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/donut_example.png-e10c5658522da25266912e65fd566f17.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://screenshots/donut_example.png"
dest_files=[ "res://.import/donut_example.png-e10c5658522da25266912e65fd566f17.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/tree_example.png-30e5634b0df2b8ed1a0c3af2929c3498.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://screenshots/tree_example.png"
dest_files=[ "res://.import/tree_example.png-30e5634b0df2b8ed1a0c3af2929c3498.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0