├── .DS_Store ├── .github └── FUNDING.yml ├── .gitignore ├── BootSplash.png ├── BootSplash.png.import ├── LICENSE ├── README.md ├── application-icons ├── 1.png ├── 1.png.import ├── 2.png ├── 2.png.import ├── 3.png ├── 3.png.import ├── 4.png └── 4.png.import ├── demo ├── Camera2D.tscn ├── FracturePolygon.tscn ├── WorldEnvironment.tscn ├── default_env.tres ├── icon.png ├── icon.png.import ├── smiley-face.png ├── smiley-face.png.import ├── src │ ├── 2d_environment.tres │ ├── Arrow.tscn │ ├── BlobFracture.gd │ ├── CutFracture.gd │ ├── CutShapeVisualizer.gd │ ├── CutShapeVisualizer.tscn │ ├── Fracture.gd │ ├── FractureBody.gd │ ├── FractureBody.tscn │ ├── FractureShard.gd │ ├── FractureShard.tscn │ ├── Main.gd │ ├── Main.tscn │ ├── PointFracture.gd │ ├── RigidBody2D.gd │ ├── RigidBody2D.tscn │ ├── SourcePolygon.tscn │ ├── blob │ │ ├── Blob.gd │ │ └── Blob.tscn │ ├── circle-cast │ │ ├── CircleCast.gd │ │ └── CircleCast.tscn │ ├── default_env.tres │ ├── healing-aura │ │ ├── HealingAura.gd │ │ └── HealingAura.tscn │ └── point-fracture-ball │ │ ├── PointFractureBall.gd │ │ └── PointFractureBall.tscn ├── test-scenes │ ├── blob-scenes │ │ ├── BlobSceneBasic.tscn │ │ └── usable │ │ │ ├── BlobRestoreAdvScene.tscn │ │ │ └── BlobRestoreSimpleScene.tscn │ ├── cut-scenes │ │ ├── CutSceneBasic.tscn │ │ └── usable │ │ │ └── CutFracture.tscn │ ├── fracture-scenes │ │ ├── FractureSceneBasic.tscn │ │ └── usable │ │ │ ├── Fracture.tscn │ │ │ ├── Fracture_Delaunay.tscn │ │ │ ├── Fracture_DelaunayConvex.tscn │ │ │ ├── Fracture_DelaunayRectangle.tscn │ │ │ └── Fracture_Simple.tscn │ ├── point-fracture │ │ ├── PointFractureSceneBasic.tscn │ │ └── usable │ │ │ └── PointFractureScene.tscn │ └── test-scene-basic │ │ └── TestSceneBasic.tscn ├── tile-test-01.png ├── tile-test-01.png.import └── ui │ ├── BackgroundLayer.tscn │ ├── FracturesSlider.tscn │ ├── InfoLabel.tscn │ ├── MinAreaSlider.tscn │ └── Title.tscn ├── gifs ├── godot-polygon2d-fracture(v1.2.0)-readme02.gif ├── godot-polygon2d-pointfracture-05.gif ├── polygon-fracture-restore-simple01.gif ├── polygon2d-cutfracture-showcase-02.gif ├── polygon2d-delauny-fracture-01.gif └── polygon2d-fracture-simple-01.gif ├── polygon2d-fracture ├── PolygonFracture.gd ├── PolygonLib.gd └── PolygonRestorer.gd ├── pool-manager ├── Pool2DBasic.gd └── Pool2DBasic.tscn └── project.godot /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/.DS_Store -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [SoloByte] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .import/ 4 | .godot/ 5 | export.cfg 6 | export_presets.cfg 7 | 8 | # Mono-specific ignores 9 | .mono/ 10 | data_*/ 11 | -------------------------------------------------------------------------------- /BootSplash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/BootSplash.png -------------------------------------------------------------------------------- /BootSplash.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bcjhkilic5y2p" 6 | path="res://.godot/imported/BootSplash.png-387a6805e0444ee48a86299f1b763603.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://BootSplash.png" 14 | dest_files=["res://.godot/imported/BootSplash.png-387a6805e0444ee48a86299f1b763603.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 David Grueneis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![GitHubBanner 1920x384)](https://github.com/SoloByte/godot-polygon2d-fracture/assets/34277803/6837886f-83b8-4f79-80f6-0929e13a6455) 2 | 3 | 4 | 5 | ## DEVELOPER WORDS 6 | I am currently using this tool in my game Fracture Hell. You can take a look to see what it is capable of or how it can be used. Buying Fracture Hell is a great way to support me if you are interested and any feedback regarding this tool or Fracture Hell is greatly appreciated. 7 | Fracture Hell is available on [Steam](https://store.steampowered.com/app/1713770/Fracture_Hell/) and [Itch Io](https://solobytegames.itch.io/fracture-hell). 8 | 9 | 10 | 11 | ## UPDATES 12 | - UPDATE v2.0.0 ported to Godot 4.0 (Godot 4.2+) 13 | - UPDATE v1.4.0 adds polygon restoring - simple and advanced (Godot 3.2+) 14 | - UPDATE v1.3.0 texture system + point fracture system + big cleanup (Godot 3.2+) 15 | - UPDATE v1.2.0 overhaul of mouse cut system + important bug fixes + pool manager script (bonus) + "juice". (Godot 3.2+) 16 | - UPDATE v1.1.0 adds cutting of polygons. (Godot 3.2+) 17 | 18 | 19 | ## Info 20 | 21 | > Works with GodotEngine 4.2+. Tested with Godot Versions: 4.2.2 22 | 23 | > Godot Polygon Fracture 2D v1.4 and before only works with Godot 3! Additionally, the 1.4x branch is for Godot 3 development. 24 | 25 | Two simple scripts for fracturing and cutting polygons. PolygonFracture.gd is the actual script that fractures/cuts polygons. PolygonLib.gd adds nice helper functions for polygons like calculateArea, triangulate, getRandomPointsInPolygon, getBoundingRect, makeCirclePolygon, etc. 26 | 27 | The final scripts are located in the polygon2d-fracture folder. 28 | The demo project is located in the demo folder. 29 | 30 | The pool manager script added in [Update v1.2.0] is just a bonus. You can use it as is or use it as inspiration for your own pooling system :) 31 | 32 | You can download/play the demo on [itch.io.](https://solobytegames.itch.io/godot-polygon-2d-fracture-system) :D 33 | 34 | 35 | ## Point Fracture 36 | Originally I wanted to do a more sophisticated point fracture system but it always turned out to be a complicated mess. Finally I figured out a simpler way with using the already implemented Cut-Fracture system. I added a func to generate a randomized polygon and with a point (like a collision point), this randomized polygon can be used in the cut fracture method. 37 | 38 | In this gif you can also see that polygons with textures can now be cut/fractured too :) 39 | 40 | ![](gifs/godot-polygon2d-pointfracture-05.gif) 41 | 42 | 43 | ## Cut Method 44 | 45 | There is just one cut method. It uses 1 polygon as the source and 1 polygon as a cut shape. The intersected shape (the parts overlapping in both polygons) can be fractured if desired. I also added helper funcs to PolygonLib.gd to create simple polygon shapes (currently: rectangle, circle, beam). 46 | 47 | [Update v1.2.0] overhauled the mouse cut system for the demo project. The input system was unified to 1 button so it works on touch screens too. (HOLD LMB for cut lines, Tap LMB for simple circle cut) 48 | 49 | ### NEW CUT METHOD 50 | ![](gifs/godot-polygon2d-fracture(v1.2.0)-readme02.gif) 51 | 52 | ### OLD CUT METHOD 53 | ![](gifs/polygon2d-cutfracture-showcase-02.gif) 54 | 55 | 56 | 57 | ## Fracturing Methods 58 | 59 | There are two different systems for fracturing polygons. 60 | - Delaunay Fracture -> uses the delaunay triangulation to calculate random triangles inside the polygon. DelaunyFractureConvex assumes the polygon is convex and DelaunyFractureRectangle assumes the polygon is a rectangle (convex/rectangle makes the fracturing simpler). Produces triangle fractures. 61 | - Fracture -> uses randomly generated cut lines to actually cut the polygon. Fracture and Fracture simple have different methods of obtaining the random cut lines but are otherwise the same. Produces polygon fractures. 62 | 63 | ### Delaunay System 64 | ![](gifs/polygon2d-delauny-fracture-01.gif) 65 | 66 | ### Cut Line System 67 | ![](gifs/polygon2d-fracture-simple-01.gif) 68 | 69 | 70 | 71 | ## Restoring 72 | 73 | ![](gifs/polygon-fracture-restore-simple01.gif) 74 | 75 | I added two different methods of restoring polygons to their original state. 76 | 77 | ### Simple 78 | The simple method uses the polygonRestorer.gd script. This is a simple class with an array(stack) of polygons. Each polygon represents a state. The last element represents the previous polygon, the first element represents the original polygon. The user can add new shapes, get the original state, the current state or the previous state with simple functions. 79 | 80 | ### Advanced 81 | The advanced method just uses 1 function in the polygonLib.gd script. The simple method stores the states of the polygon and can restore each state. The advanced method grows the polygon by a certain amount while restricting it to the original polygons shape. In other words: the simple method can only restore discrete states while the advanced method can restore any amount in between. Unfortunately the advanced method comes with a bigger performace cost. As seen in the demo, it should not be a problem using it every now and then but restore intervals below about 0.2 seconds can slow things down significantly. 82 | 83 | I would suggest just testing it because as always it depends on how and for what you want to use it ;) 84 | 85 | 86 | 87 | 88 | I need the fracturing for my game but I thought I share it with anyone interested. My method is not the best or most performant method out there, and also implemented via GDScript (for ease of use), so don´t expect any performance miracles. There are other solutions out there, but I did not find a simple solution for fracturing 2d polygons in the way I wanted. Maybe sometime in the future, I will look into Voronoi fractures, to make the fractures look better. (Now the polygon is just randomly fractured) 89 | 90 | 91 | 92 | ## Other Solutions 93 | - Goost (https://github.com/goostengine/goost), which can be downloaded at [Goost official website](https://goostengine.github.io/), otherwise requires building Godot from source. 94 | - Godot-3-2D-Destructible-Objects (https://github.com/hiulit/Godot-3-2D-Destructible-Objects) but only works for sprites 95 | -------------------------------------------------------------------------------- /application-icons/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/application-icons/1.png -------------------------------------------------------------------------------- /application-icons/1.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://coebhp0xcl4qv" 6 | path="res://.godot/imported/1.png-9d829fa9133cd6ea21d74b045443ab0d.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://application-icons/1.png" 14 | dest_files=["res://.godot/imported/1.png-9d829fa9133cd6ea21d74b045443ab0d.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /application-icons/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/application-icons/2.png -------------------------------------------------------------------------------- /application-icons/2.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://l3oo4uunubsb" 6 | path="res://.godot/imported/2.png-3c908627227686fbcee1a6f7eec1f5e0.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://application-icons/2.png" 14 | dest_files=["res://.godot/imported/2.png-3c908627227686fbcee1a6f7eec1f5e0.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /application-icons/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/application-icons/3.png -------------------------------------------------------------------------------- /application-icons/3.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://coam0085xj3pj" 6 | path="res://.godot/imported/3.png-a45f07bca9874684e595d537d9c975e3.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://application-icons/3.png" 14 | dest_files=["res://.godot/imported/3.png-a45f07bca9874684e595d537d9c975e3.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /application-icons/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/application-icons/4.png -------------------------------------------------------------------------------- /application-icons/4.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bsaydxko7ra25" 6 | path="res://.godot/imported/4.png-fe8d7736751ad7e5fa1e12b64224c74d.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://application-icons/4.png" 14 | dest_files=["res://.godot/imported/4.png-fe8d7736751ad7e5fa1e12b64224c74d.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/Camera2D.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene format=2] 2 | 3 | [node name="Camera2D" type="Camera2D"] 4 | current = true 5 | zoom = Vector2( 2, 2 ) 6 | -------------------------------------------------------------------------------- /demo/FracturePolygon.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene format=2] 2 | 3 | [node name="FracturePolygon" type="Polygon2D"] 4 | show_behind_parent = true 5 | position = Vector2( 0, -330.18 ) 6 | color = Color( 0.545098, 0.545098, 0.545098, 1 ) 7 | polygon = PackedVector2Array( -387.17, -461.463, -88, -485.82, 312, -333.82, 592, -45.82, 520, 298.18, 0, 386.18, -552, 162.18 ) 8 | 9 | [node name="Line2D" type="Line2D" parent="."] 10 | points = PackedVector2Array( -552, 162.18, -384, -461.82, -88, -485.82, 312, -333.82, 592, -45.82, 520, 298.18, 0, 386.18, -552, 162.18 ) 11 | default_color = Color( 1.5, 1.5, 1.5, 1 ) 12 | joint_mode = 2 13 | begin_cap_mode = 2 14 | end_cap_mode = 2 15 | antialiased = true 16 | -------------------------------------------------------------------------------- /demo/WorldEnvironment.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://demo/src/2d_environment.tres" type="Environment" id=1] 4 | 5 | [node name="WorldEnvironment" type="WorldEnvironment"] 6 | environment = ExtResource( 1 ) 7 | -------------------------------------------------------------------------------- /demo/default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="Sky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /demo/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/demo/icon.png -------------------------------------------------------------------------------- /demo/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://baqb1krfxb8o2" 6 | path="res://.godot/imported/icon.png-a94488736806340ecddab5ffd0ccedbc.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://demo/icon.png" 14 | dest_files=["res://.godot/imported/icon.png-a94488736806340ecddab5ffd0ccedbc.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/smiley-face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/demo/smiley-face.png -------------------------------------------------------------------------------- /demo/smiley-face.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bsrwyn1s0ui61" 6 | path="res://.godot/imported/smiley-face.png-2e530511027117192be544245e4b63da.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://demo/smiley-face.png" 14 | dest_files=["res://.godot/imported/smiley-face.png-2e530511027117192be544245e4b63da.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/src/2d_environment.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" format=2] 2 | 3 | [resource] 4 | background_mode = 4 5 | glow_enabled = true 6 | glow_levels/4 = true 7 | glow_intensity = 0.5 8 | glow_blend_mode = 0 9 | glow_bicubic_upscale = true 10 | -------------------------------------------------------------------------------- /demo/src/Arrow.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene format=2] 2 | 3 | [node name="Arrow" type="Polygon2D"] 4 | color = Color( 1, 1, 1, 0.392157 ) 5 | polygon = PackedVector2Array( 0, 0, 0, -32, 96, 0, 0, 32 ) 6 | 7 | [node name="Line2D" type="Line2D" parent="."] 8 | points = PackedVector2Array( 0, -32, 96, 0, 0, 32, 0, -32 ) 9 | default_color = Color( 1, 1, 1, 1 ) 10 | joint_mode = 2 11 | begin_cap_mode = 2 12 | end_cap_mode = 2 13 | -------------------------------------------------------------------------------- /demo/src/BlobFracture.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | 4 | 5 | 6 | # MIT License 7 | # ----------------------------------------------------------------------- 8 | # This file is part of: 9 | # GODOT Polygon 2D Fracture 10 | # https://github.com/SoloByte/godot-polygon2d-fracture 11 | # ----------------------------------------------------------------------- 12 | # Copyright (c) 2021 David Grueneis 13 | # 14 | # Permission is hereby granted, free of charge, to any person obtaining a copy 15 | # of this software and associated documentation files (the "Software"), to deal 16 | # in the Software without restriction, including without limitation the rights 17 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | # copies of the Software, and to permit persons to whom the Software is 19 | # furnished to do so, subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be included in all 22 | # copies or substantial portions of the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | # SOFTWARE. 31 | 32 | 33 | 34 | 35 | 36 | @onready var _rng := RandomNumberGenerator.new() 37 | @onready var _blob_parent := $BlobParent 38 | @onready var _pool_cut_visualizer := $Pool_CutVisualizer 39 | @onready var _pool_fracture_shards := $Pool_FractureShards 40 | 41 | 42 | 43 | func _ready() -> void: 44 | for blob in _blob_parent.get_children(): 45 | blob.connect("Damaged", Callable(self, "On_Blob_Damaged")) 46 | blob.connect("Fractured", Callable(self, "On_Blob_Fractured")) 47 | 48 | 49 | 50 | func spawnShapeVisualizer(cut_pos : Vector2, cut_shape : PackedVector2Array, fade_speed : float) -> void: 51 | var instance = _pool_cut_visualizer.getInstance() 52 | instance.spawn(cut_pos, fade_speed) 53 | instance.setPolygon(cut_shape) 54 | 55 | 56 | func spawnFractureBody(fracture_shard : Dictionary, new_mass : float, color : Color, fracture_force : float, p : float) -> void: 57 | var instance = _pool_fracture_shards.getInstance() 58 | if not instance: 59 | return 60 | 61 | var dir : Vector2 = (fracture_shard.spawn_pos - fracture_shard.source_global_trans.get_origin()).normalized() 62 | instance.spawn(fracture_shard.spawn_pos, fracture_shard.spawn_rot, fracture_shard.source_global_trans.get_scale(), _rng.randf_range(2.0, 4.0)) 63 | instance.setPolygon(fracture_shard.centered_shape, color, {}) 64 | instance.setMass(new_mass) 65 | instance.addForce(dir * fracture_force * p) 66 | instance.addTorque(_rng.randf_range(-2, 2) * p) 67 | 68 | 69 | 70 | 71 | func On_Blob_Damaged(blob, pos : Vector2, shape : PackedVector2Array, color : Color, fade_speed : float) -> void: 72 | spawnShapeVisualizer(pos, shape, fade_speed) 73 | 74 | func On_Blob_Fractured(blob, fracture_shard : Dictionary, new_mass : float, color : Color, fracture_force : float, p : float) -> void: 75 | spawnFractureBody(fracture_shard, new_mass, color, fracture_force, p) 76 | -------------------------------------------------------------------------------- /demo/src/CutFracture.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | 4 | 5 | 6 | # MIT License 7 | # ----------------------------------------------------------------------- 8 | # This file is part of: 9 | # GODOT Polygon 2D Fracture 10 | # https://github.com/SoloByte/godot-polygon2d-fracture 11 | # ----------------------------------------------------------------------- 12 | # Copyright (c) 2021 David Grueneis 13 | # 14 | # Permission is hereby granted, free of charge, to any person obtaining a copy 15 | # of this software and associated documentation files (the "Software"), to deal 16 | # in the Software without restriction, including without limitation the rights 17 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | # copies of the Software, and to permit persons to whom the Software is 19 | # furnished to do so, subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be included in all 22 | # copies or substantial portions of the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | # SOFTWARE. 31 | 32 | 33 | 34 | 35 | const CUT_LINE_POINT_MIN_DISTANCE : float = 40.0 #distance before new point is added (the smaller the more detailed is the visual line (not the cut line) 36 | const CUT_LINE_STATIONARY_DELAY : float = 0.1 #after that amount of seconds remaining stationary will end the cut line process and cut the sources 37 | const CUT_LINE_DIRECTION_THRESHOLD : float = -0.7 #smaller than treshold will end the cut line process and cut the sources (start_dir.dot(cur_dir) < threshold = endLine) 38 | 39 | const CUT_LINE_MIN_LENGTH : float = 50.0 #the min length the cut line must have to be used for cutting (otherwise it will be discarded and no cutting occurs) 40 | const CUT_LINE_EPSILON : float = 10.0 # used in func simplifyLineRDP // how detailed the actual cut line is (opposed to CUT_LINE_POINT_MIN_DISTANCE which determines how detailed the visual cut line is) 41 | #const CUT_LINE_SEGMENT_MIN_LENGTH : float = 250.0 #used in my own simplifyLine func //how detailed the actual cut line is (opposed to CUT_LINE_POINT_MIN_DISTANCE which determines how detailed the visual cut line is) 42 | 43 | 44 | 45 | 46 | @export var fracture_body_color: Color 47 | @export var rigidbody_template: PackedScene 48 | 49 | 50 | 51 | 52 | @onready var polyFracture := PolygonFracture.new() 53 | @onready var _source_polygon_parent := $SourcePolygons 54 | @onready var _rng := RandomNumberGenerator.new() 55 | @onready var _cut_shape : PackedVector2Array = PolygonLib.createCirclePolygon(100.0, 1) 56 | @onready var _cut_line := $CutLine 57 | @onready var _pool_cut_visualizer := $Pool_CutVisualizer 58 | @onready var _pool_fracture_shards := $Pool_FractureShards 59 | 60 | 61 | 62 | 63 | var _input_disabled : bool = false 64 | 65 | var _cur_fracture_color : Color = fracture_body_color 66 | 67 | var _cut_line_enabled : bool = false 68 | var _cut_line_total_length : float = 0.0 69 | var _cut_line_points : PackedVector2Array = [] 70 | var _cut_line_start_direction := Vector2.ZERO 71 | var _cut_line_t : float = 0.0 72 | var _cut_line_last_end_point := Vector3.ZERO #z is used as bool -> 0 = not a valid point/ 1 = valid point 73 | 74 | 75 | 76 | 77 | func _ready() -> void: 78 | _rng.randomize() 79 | 80 | var color := Color.WHITE 81 | color.s = fracture_body_color.s 82 | color.v = fracture_body_color.v 83 | color.h = _rng.randf() 84 | _cur_fracture_color = color 85 | _source_polygon_parent.modulate = _cur_fracture_color 86 | 87 | _cut_line.clear_points() 88 | _cut_line.visible = false 89 | 90 | 91 | func _process(delta: float) -> void: 92 | if _cut_line_enabled: 93 | var cur_pos : Vector2 = get_global_mouse_position() 94 | if _cut_line_t < 1.0: 95 | _cut_line_t += delta * (1.0 / CUT_LINE_STATIONARY_DELAY) 96 | calculateCutLine(cur_pos, _cut_line_t) 97 | 98 | 99 | func _input(event: InputEvent) -> void: 100 | if _input_disabled: return 101 | 102 | #this system works with 1 button (instead of 2 with right mouse button) -> makes it work on touch screens 103 | if event is InputEventMouseButton: 104 | if event.button_index == 1: 105 | if _cut_line_enabled: 106 | if not event.pressed: 107 | if _cut_line.visible: 108 | endCutLine() 109 | _cut_line_enabled = false 110 | _cut_line.visible = false 111 | _cut_line_last_end_point = Vector3.ZERO 112 | else: 113 | simpleCut(get_global_mouse_position()) 114 | _cut_line_total_length = 0.0 115 | _cut_line_points = [] 116 | _cut_line.clear_points() 117 | _cut_line_start_direction = Vector2.ZERO 118 | _cut_line_t = 0.0 119 | _cut_line_enabled = false 120 | _cut_line.visible = false 121 | else: 122 | if event.pressed: 123 | _cut_line_enabled = true 124 | 125 | 126 | 127 | 128 | 129 | func calculateCutLine(cur_pos : Vector2, t : float) -> void: 130 | if _cut_line_points.size() <= 0: 131 | if _cut_line_last_end_point.z > 0.0:# last cut lines end point is new cut lines start point 132 | _cut_line_points.append(Vector2(_cut_line_last_end_point.x, _cut_line_last_end_point.y)) 133 | _cut_line_last_end_point = Vector3.ZERO 134 | else:#there was no cut line before 135 | _cut_line_points.append(cur_pos) 136 | 137 | elif _cut_line_points.size() == 1 and not _cut_line.visible: 138 | var dis : float = (cur_pos - _cut_line_points[_cut_line_points.size() - 1]).length() 139 | if dis > CUT_LINE_MIN_LENGTH: 140 | _cut_line.visible = true 141 | else: 142 | var last_pos : Vector2 = lerp(_cut_line_points[_cut_line_points.size() - 1], cur_pos, t) 143 | var vec : Vector2 = cur_pos - last_pos 144 | var dir : Vector2 = vec.normalized() 145 | 146 | if _cut_line_start_direction == Vector2.ZERO: 147 | _cut_line_start_direction = dir 148 | elif dir == Vector2.ZERO: 149 | endCutLine() 150 | return 151 | else: 152 | if _cut_line_start_direction.dot(dir) < CUT_LINE_DIRECTION_THRESHOLD: 153 | endCutLine() 154 | return 155 | 156 | var last_point : Vector2 = _cut_line_points[_cut_line_points.size() - 1] 157 | var dis : float = (cur_pos - last_point).length() 158 | if dis > CUT_LINE_POINT_MIN_DISTANCE: 159 | _cut_line_points.append(cur_pos) 160 | _cut_line.points = _cut_line_points 161 | _cut_line_t = 0.0 162 | _cut_line_total_length += dis 163 | else: 164 | _cut_line.points = _cut_line_points 165 | _cut_line.add_point(cur_pos, -1) 166 | 167 | 168 | func startCutLine() -> void: 169 | pass 170 | 171 | 172 | func endCutLine() -> void: 173 | if _cut_line_points.size() > 1 and _cut_line_total_length > CUT_LINE_MIN_LENGTH and not _input_disabled: 174 | 175 | # var final_line : PoolVector2Array = PolygonLib.simplifyLine(_cut_line_points, CUT_LINE_SEGMENT_MIN_LENGTH) 176 | var final_line : PackedVector2Array = PolygonLib.simplifyLineRDP(_cut_line_points, CUT_LINE_EPSILON) 177 | var final_shape : PackedVector2Array = [] 178 | 179 | final_shape = PolygonLib.offsetPolyline(final_line, 2.0, true)[0] 180 | final_shape = PolygonLib.translatePolygon(final_shape, -_cut_line_points[0]) 181 | cutSourcePolygons(_cut_line_points[0], final_shape, 0.0, 0.0, 0.25) 182 | 183 | 184 | if _cut_line_points.size() > 1: 185 | var end_point : Vector2 = _cut_line_points[_cut_line_points.size() - 1] 186 | _cut_line_last_end_point = Vector3(end_point.x, end_point.y, 1.0) 187 | else: 188 | _cut_line_last_end_point = Vector3.ZERO 189 | 190 | _cut_line_total_length = 0.0 191 | _cut_line_points = [] 192 | _cut_line.clear_points() 193 | _cut_line_start_direction = Vector2.ZERO 194 | _cut_line_t = 0.0 195 | 196 | _input_disabled = true 197 | set_deferred("_input_disabled", false) 198 | 199 | 200 | func simpleCut(pos : Vector2) -> void: 201 | if _input_disabled: return 202 | 203 | var cut_pos : Vector2 = pos 204 | cutSourcePolygons(cut_pos, _cut_shape, 0.0, _rng.randf_range(250.0, 400.0), 2.0) 205 | _input_disabled = true 206 | set_deferred("_input_disabled", false) 207 | 208 | 209 | 210 | 211 | func cutSourcePolygons(cut_pos : Vector2, cut_shape : PackedVector2Array, cut_rot : float, cut_force : float = 0.0, fade_speed : float = 2.0) -> void: 212 | var instance = _pool_cut_visualizer.getInstance() 213 | instance.spawn(cut_pos, fade_speed) 214 | instance.setPolygon(cut_shape) 215 | 216 | for source in _source_polygon_parent.get_children(): 217 | var source_polygon : PackedVector2Array = source.get_polygon() 218 | var total_area : float = PolygonLib.getPolygonArea(source_polygon) 219 | 220 | var source_trans : Transform2D = source.get_global_transform() 221 | var cut_trans := Transform2D(cut_rot, cut_pos) 222 | 223 | var s_lin_vel := Vector2.ZERO 224 | var s_ang_vel : float = 0.0 225 | var s_mass : float = 0.0 226 | 227 | if source is RigidBody2D: 228 | s_lin_vel = source.linear_velocity 229 | s_ang_vel = source.angular_velocity 230 | s_mass = source.mass 231 | 232 | 233 | var cut_fracture_info : Dictionary = polyFracture.cutFracture(source_polygon, cut_shape, source_trans, cut_trans, 5000, 3000, 250, 1) 234 | 235 | if cut_fracture_info.shapes.size() <= 0 and cut_fracture_info.fractures.size() <= 0: 236 | continue 237 | 238 | for fracture in cut_fracture_info.fractures: 239 | for fracture_shard in fracture: 240 | var area_p : float = fracture_shard.area / total_area 241 | 242 | spawnFractureBody(fracture_shard, source.getTextureInfo(), s_mass * area_p) 243 | 244 | 245 | for shape in cut_fracture_info.shapes: 246 | var area_p : float = shape.area / total_area 247 | var mass : float = s_mass * area_p 248 | var dir : Vector2 = (shape.spawn_pos - cut_pos).normalized() 249 | 250 | call_deferred("spawnRigibody2d", shape, source.modulate, s_lin_vel + dir * cut_force, s_ang_vel, mass, cut_pos, source.getTextureInfo()) 251 | 252 | source.queue_free() 253 | 254 | 255 | 256 | 257 | func spawnRigibody2d(shape_info : Dictionary, color : Color, lin_vel : Vector2, ang_vel : float, mass : float, cut_pos : Vector2, texture_info : Dictionary) -> void: 258 | var instance = rigidbody_template.instantiate() 259 | _source_polygon_parent.add_child(instance) 260 | instance.global_position = shape_info.spawn_pos 261 | instance.global_rotation = shape_info.spawn_rot 262 | instance.set_polygon(shape_info.centered_shape) 263 | instance.modulate = color 264 | instance.linear_velocity = lin_vel 265 | instance.angular_velocity = ang_vel 266 | instance.mass = mass 267 | instance.setTexture(PolygonLib.setTextureOffset(texture_info, shape_info.centroid)) 268 | 269 | 270 | func spawnFractureBody(fracture_shard : Dictionary, texture_info : Dictionary, new_mass : float) -> void: 271 | var instance = _pool_fracture_shards.getInstance() 272 | if not instance: 273 | return 274 | 275 | 276 | #fracture shard variant 277 | var dir : Vector2 = (fracture_shard.spawn_pos - fracture_shard.source_global_trans.get_origin()).normalized() 278 | instance.spawn(fracture_shard.spawn_pos, fracture_shard.spawn_rot, fracture_shard.source_global_trans.get_scale(), _rng.randf_range(0.5, 2.0)) 279 | instance.setPolygon(fracture_shard.centered_shape, _cur_fracture_color, PolygonLib.setTextureOffset(texture_info, fracture_shard.centroid)) 280 | instance.setMass(new_mass) 281 | instance.addForce(dir * 500.0) 282 | instance.addTorque(_rng.randf_range(-2, 2)) 283 | -------------------------------------------------------------------------------- /demo/src/CutShapeVisualizer.gd: -------------------------------------------------------------------------------- 1 | extends Polygon2D 2 | 3 | 4 | 5 | 6 | # MIT License 7 | # ----------------------------------------------------------------------- 8 | # This file is part of: 9 | # GODOT Polygon 2D Fracture 10 | # https://github.com/SoloByte/godot-polygon2d-fracture 11 | # ----------------------------------------------------------------------- 12 | # Copyright (c) 2021 David Grueneis 13 | # 14 | # Permission is hereby granted, free of charge, to any person obtaining a copy 15 | # of this software and associated documentation files (the "Software"), to deal 16 | # in the Software without restriction, including without limitation the rights 17 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | # copies of the Software, and to permit persons to whom the Software is 19 | # furnished to do so, subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be included in all 22 | # copies or substantial portions of the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | # SOFTWARE. 31 | 32 | 33 | 34 | 35 | signal Despawn(ref) 36 | 37 | 38 | 39 | 40 | 41 | @export var start_color: Color = Color(1.5, 1.5, 1.5, 1.0) 42 | @export var end_color: Color = Color(1.0, 1.0, 1.0, 0.1) 43 | 44 | 45 | 46 | var fade_speed = 1.0 47 | var t : float = 0.0 48 | 49 | 50 | 51 | func _ready() -> void: 52 | set_process(false) 53 | visible = false 54 | 55 | 56 | func spawn(pos : Vector2, fade_speed : float = 1.0) -> void: 57 | global_position = pos 58 | visible = true 59 | if fade_speed > 0.0: 60 | self.fade_speed = fade_speed 61 | set_process(true) 62 | 63 | 64 | func despawn() -> void: 65 | t = 0.0 66 | visible = false 67 | set_process(false) 68 | 69 | 70 | 71 | 72 | func _process(delta: float) -> void: 73 | if fade_speed > 0.0: 74 | t += delta * fade_speed 75 | 76 | color = lerp(start_color, end_color, t) 77 | 78 | if t >= 1.0: 79 | emit_signal("Despawn", self) 80 | # queue_free() 81 | 82 | 83 | 84 | func setPolygon(poly : PackedVector2Array) -> void: 85 | t = 0.0 86 | color = start_color 87 | set_polygon(poly) 88 | -------------------------------------------------------------------------------- /demo/src/CutShapeVisualizer.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://demo/src/CutShapeVisualizer.gd" type="Script" id=1] 4 | 5 | [node name="CutShapeVisualizer" type="Polygon2D"] 6 | modulate = Color( 1, 0.415686, 0.415686, 1 ) 7 | z_index = -10 8 | script = ExtResource( 1 ) 9 | start_color = Color( 3, 3, 3, 1 ) 10 | -------------------------------------------------------------------------------- /demo/src/Fracture.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | 4 | 5 | 6 | # MIT License 7 | # ----------------------------------------------------------------------- 8 | # This file is part of: 9 | # GODOT Polygon 2D Fracture 10 | # https://github.com/SoloByte/godot-polygon2d-fracture 11 | # ----------------------------------------------------------------------- 12 | # Copyright (c) 2021 David Grueneis 13 | # 14 | # Permission is hereby granted, free of charge, to any person obtaining a copy 15 | # of this software and associated documentation files (the "Software"), to deal 16 | # in the Software without restriction, including without limitation the rights 17 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | # copies of the Software, and to permit persons to whom the Software is 19 | # furnished to do so, subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be included in all 22 | # copies or substantial portions of the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | # SOFTWARE. 31 | 32 | 33 | 34 | 35 | enum DELAUNY_TYPES {DEFAULT = 0, CONVEX = 1, RECTANGLE = 2} 36 | 37 | 38 | 39 | 40 | @export var fracture_body_color: Color 41 | @export var fracture_body_template: PackedScene 42 | 43 | 44 | 45 | 46 | @export var delauny_fracture: bool = false 47 | @export var delauny_type: DELAUNY_TYPES = DELAUNY_TYPES.DEFAULT 48 | 49 | @export var simple_fracture: bool = true 50 | 51 | 52 | 53 | @onready var polyFracture := PolygonFracture.new() 54 | @onready var _source_polygon_parent := $SourcePolygons 55 | @onready var _parent := $Parent 56 | @onready var _visible_timer := $VisibleTimer 57 | @onready var _slowdown_timer := $SlowdownTimer 58 | @onready var _timer := $Timer 59 | 60 | @onready var _rng := RandomNumberGenerator.new() 61 | 62 | @onready var _fracture_slider := $CanvasLayer/FracturesSlider 63 | @onready var _fractures_label := $CanvasLayer/FracturesSlider/Label 64 | @onready var _min_area_slider := $CanvasLayer/MinAreaSlider 65 | @onready var _min_area_label := $CanvasLayer/MinAreaSlider/Label 66 | @onready var _pool_fracture_bodies := $Pool_FractureBodies 67 | 68 | 69 | 70 | var _cur_fracture_color : Color = fracture_body_color 71 | var _auto_active : bool = false 72 | var cuts : int = 3 73 | var min_area : int = 25 74 | 75 | 76 | 77 | func _ready() -> void: 78 | _rng.randomize() 79 | 80 | var color := Color.WHITE 81 | color.s = fracture_body_color.s 82 | color.v = fracture_body_color.v 83 | color.h = _rng.randf() 84 | _cur_fracture_color = color 85 | _source_polygon_parent.modulate = _cur_fracture_color 86 | _fracture_slider.grab_focus() 87 | 88 | _fracture_slider.value = 16 89 | _min_area_slider.value = 2000 90 | _on_FracturesSlider_value_changed(16) 91 | _on_MinAreaSlider_value_changed(2000) 92 | 93 | 94 | 95 | func _input(event: InputEvent) -> void: 96 | if event.is_action_pressed("fracture") and _source_polygon_parent.visible: 97 | fractureAll() 98 | 99 | if event.is_action_pressed("auto"): 100 | if _auto_active: 101 | _auto_active = false 102 | _timer.stop() 103 | else: 104 | _auto_active = true 105 | _timer.start(3.0) 106 | 107 | 108 | func fractureAll() -> void: 109 | _visible_timer.start(2.0) 110 | _slowdown_timer.start(0.25) 111 | Engine.time_scale = 0.1 112 | _source_polygon_parent.visible = false 113 | 114 | for source in _source_polygon_parent.get_children(): 115 | var fracture_info : Array 116 | 117 | if delauny_fracture: 118 | match delauny_type: 119 | DELAUNY_TYPES.DEFAULT: 120 | fracture_info = polyFracture.fractureDelaunay(source.polygon, source.get_global_transform(), cuts, min_area) 121 | DELAUNY_TYPES.CONVEX: 122 | fracture_info = polyFracture.fractureDelaunayConvex(source.polygon, source.get_global_transform(), cuts, min_area) 123 | DELAUNY_TYPES.RECTANGLE: 124 | fracture_info = polyFracture.fractureDelaunayRectangle(source.polygon, source.get_global_transform(), cuts, min_area) 125 | else: 126 | if simple_fracture: 127 | fracture_info = polyFracture.fractureSimple(source.polygon, source.get_global_transform(), cuts, min_area) 128 | else: 129 | fracture_info = polyFracture.fracture(source.polygon, source.get_global_transform(), cuts, min_area) 130 | 131 | for entry in fracture_info: 132 | var texture_info : Dictionary = {"texture" : source.texture, "rot" : source.texture_rotation, "offset" : source.texture_offset, "scale" : source.texture_scale} 133 | spawnFractureBody(entry, texture_info) 134 | 135 | 136 | 137 | func spawnFractureBody(fracture_shard : Dictionary, texture_info : Dictionary) -> void: 138 | var instance = _pool_fracture_bodies.getInstance() 139 | if not instance: 140 | return 141 | 142 | instance.spawn(fracture_shard.spawn_pos) 143 | instance.global_rotation = fracture_shard.spawn_rot 144 | if instance.has_method("setPolygon"): 145 | var s : Vector2 = fracture_shard.source_global_trans.get_scale() 146 | instance.setPolygon(fracture_shard.centered_shape, s) 147 | 148 | 149 | instance.setColor(_cur_fracture_color) 150 | var dir : Vector2 = (fracture_shard.spawn_pos - fracture_shard.source_global_trans.get_origin()).normalized() 151 | instance.linear_velocity = dir * _rng.randf_range(200, 400) 152 | instance.angular_velocity = _rng.randf_range(-1, 1) 153 | 154 | instance.setTexture(PolygonLib.setTextureOffset(texture_info, fracture_shard.centroid)) 155 | 156 | 157 | 158 | 159 | func _on_Timer_timeout() -> void: 160 | fractureAll() 161 | 162 | 163 | func _on_VisibleTimer_timeout() -> void: 164 | var color := Color.WHITE 165 | color.s = fracture_body_color.s 166 | color.v = fracture_body_color.v 167 | color.h = _rng.randf() 168 | _cur_fracture_color = color 169 | _source_polygon_parent.modulate = _cur_fracture_color 170 | _source_polygon_parent.visible = true 171 | 172 | 173 | func _on_SlowdownTimer_timeout() -> void: 174 | Engine.time_scale = 1.0 175 | 176 | 177 | 178 | func _on_FracturesSlider_value_changed(value: float) -> void: 179 | _fractures_label.text = "Fractures: %d" % value 180 | cuts = _fracture_slider.value 181 | 182 | 183 | func _on_MinAreaSlider_value_changed(value): 184 | _min_area_label.text = "Min Area: %d" % value 185 | min_area = _min_area_slider.value 186 | -------------------------------------------------------------------------------- /demo/src/FractureBody.gd: -------------------------------------------------------------------------------- 1 | extends RigidBody2D 2 | 3 | 4 | 5 | 6 | # MIT License 7 | # ----------------------------------------------------------------------- 8 | # This file is part of: 9 | # GODOT Polygon 2D Fracture 10 | # https://github.com/SoloByte/godot-polygon2d-fracture 11 | # ----------------------------------------------------------------------- 12 | # Copyright (c) 2021 David Grueneis 13 | # 14 | # Permission is hereby granted, free of charge, to any person obtaining a copy 15 | # of this software and associated documentation files (the "Software"), to deal 16 | # in the Software without restriction, including without limitation the rights 17 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | # copies of the Software, and to permit persons to whom the Software is 19 | # furnished to do so, subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be included in all 22 | # copies or substantial portions of the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | # SOFTWARE. 31 | 32 | 33 | 34 | 35 | const LINE_FADE_SPEED : float = 0.8 36 | 37 | 38 | 39 | 40 | signal Despawn(ref) 41 | 42 | 43 | 44 | 45 | @onready var _col_poly := $CollisionPolygon2D 46 | @onready var _poly := $Polygon2D 47 | @onready var _line := $Line2D 48 | @onready var _timer := $Timer 49 | @onready var _line_lerp_start_color : Color = _line.modulate 50 | 51 | 52 | 53 | 54 | var _t : float = 1.0 55 | 56 | 57 | 58 | 59 | func _process(delta: float) -> void: 60 | if _t < 1.0: 61 | _t += delta * LINE_FADE_SPEED 62 | _line.modulate = lerp(_line_lerp_start_color, _poly.modulate, min(_t, 1.0)) 63 | 64 | 65 | func spawn(pos : Vector2) -> void: 66 | global_position = pos 67 | # visible = true 68 | # _col_poly.set_deferred("disabled", false) 69 | _timer.start(5.0) 70 | set_process(true) 71 | _t = 0.0 72 | 73 | 74 | func despawn() -> void: 75 | # _col_poly.set_deferred("disabled", true) 76 | # visible = false 77 | global_rotation = 0.0 78 | linear_velocity = Vector2.ZERO 79 | angular_velocity = 0.0 80 | apply_force(Vector2.ZERO) 81 | set_process(false) 82 | _t = 1.0 83 | 84 | 85 | #func setScale(s : Vector2) -> void: 86 | # var t := Transform2D(0.0, Vector2.ZERO).scaled(s) 87 | # shape_owner_set_transform(get_shape_owners()[0],t) 88 | # _poly.scale = s 89 | # _line.scale = s 90 | 91 | 92 | func setPolygon(polygon : PackedVector2Array, new_scale : Vector2) -> void: 93 | _poly.set_polygon(polygon) 94 | polygon.append(polygon[0]) 95 | _line.points = polygon 96 | if new_scale != Vector2(1.0, 1.0): 97 | #physics objects (like the rigidbody2d) can not be scaled 98 | #thats why the polygon2d/line2d nodes are scale seperate 99 | #collision polygons can be scaled with "shape_owner_set_transform(owner_id,transform) 100 | #but here I just scale the polygon for the collision polygon 101 | polygon = PolygonLib.scalePolygon(polygon, new_scale) 102 | _poly.scale = new_scale 103 | _line.scale = new_scale 104 | _col_poly.set_polygon(polygon) 105 | 106 | 107 | func setTexture(texture_info : Dictionary) -> void: 108 | _poly.texture = texture_info.texture 109 | _poly.texture_scale = texture_info.scale 110 | _poly.texture_offset = texture_info.offset 111 | _poly.texture_rotation = texture_info.rot 112 | 113 | 114 | func setColor(color : Color) -> void: 115 | _poly.modulate = color 116 | 117 | 118 | func _on_Timer_timeout() -> void: 119 | emit_signal("Despawn", self) 120 | # queue_free() 121 | -------------------------------------------------------------------------------- /demo/src/FractureBody.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://demo/src/FractureBody.gd" type="Script" id=1] 4 | 5 | [sub_resource type="PhysicsMaterial" id=1] 6 | friction = 0.0 7 | bounce = 1.0 8 | 9 | [node name="FractureBody" type="RigidBody2D"] 10 | collision_layer = 2 11 | collision_mask = 3 12 | mass = 10.0 13 | physics_material_override = SubResource( 1 ) 14 | gravity_scale = 6.0 15 | script = ExtResource( 1 ) 16 | 17 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="."] 18 | 19 | [node name="Polygon2D" type="Polygon2D" parent="."] 20 | color = Color( 0.545098, 0.545098, 0.545098, 1 ) 21 | 22 | [node name="Line2D" type="Line2D" parent="."] 23 | modulate = Color( 1.35, 1.35, 1.35, 1 ) 24 | default_color = Color( 1, 1, 1, 1 ) 25 | 26 | [node name="Center" type="Polygon2D" parent="."] 27 | visible = false 28 | polygon = PackedVector2Array( -12.2946, -12.6935, 2.80278, -19.7125, 20.0853, -1.50295, -1.5675, 11.6741, -16.5324, 2.47004 ) 29 | 30 | [node name="Timer" type="Timer" parent="."] 31 | wait_time = 5.0 32 | one_shot = true 33 | [connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"] 34 | -------------------------------------------------------------------------------- /demo/src/FractureShard.gd: -------------------------------------------------------------------------------- 1 | extends Polygon2D 2 | 3 | 4 | 5 | 6 | # MIT License 7 | # ----------------------------------------------------------------------- 8 | # This file is part of: 9 | # GODOT Polygon 2D Fracture 10 | # https://github.com/SoloByte/godot-polygon2d-fracture 11 | # ----------------------------------------------------------------------- 12 | # Copyright (c) 2021 David Grueneis 13 | # 14 | # Permission is hereby granted, free of charge, to any person obtaining a copy 15 | # of this software and associated documentation files (the "Software"), to deal 16 | # in the Software without restriction, including without limitation the rights 17 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | # copies of the Software, and to permit persons to whom the Software is 19 | # furnished to do so, subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be included in all 22 | # copies or substantial portions of the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | # SOFTWARE. 31 | 32 | 33 | 34 | 35 | const LINE_FADE_SPEED : float = 1.5 36 | 37 | 38 | 39 | 40 | signal Despawn(ref) 41 | 42 | 43 | 44 | 45 | @export var lin_drag : float = 0.0 # (float, 0.0, 256.0, 0.05) 46 | @export var ang_drag : float = 0.0 # (float, 0.0, 256.0, 0.05) 47 | @export var gravity_direction: Vector2 = Vector2(0, 1) 48 | @export var gravity_scale: float = 10.0 49 | 50 | @export var lifetime_scale_curve: Curve 51 | 52 | 53 | 54 | 55 | @onready var _line := $Line2D 56 | @onready var _timer := $Timer 57 | @onready var _line_lerp_start_color : Color = _line.modulate 58 | 59 | 60 | 61 | 62 | var _t : float = 1.0 63 | var _lifetime : float = 0.0 64 | 65 | var _lin_accel := Vector2.ZERO 66 | var _ang_accel : float = 0.0 67 | var _lin_vel := Vector2.ZERO 68 | var _ang_vel : float = 0.0 69 | 70 | var _mass : float = 1.0 71 | 72 | var _scale_lerp_start := Vector2.ZERO 73 | 74 | 75 | 76 | func _ready() -> void: 77 | set_process(false) 78 | set_physics_process(false) 79 | visible = false 80 | 81 | 82 | func _process(delta: float) -> void: 83 | if _t < 1.0: 84 | _t += delta * LINE_FADE_SPEED 85 | _line.modulate = lerp(_line_lerp_start_color, self_modulate, min(_t, 1.0)) 86 | 87 | if not _timer.is_stopped() and _lifetime > 0.0: 88 | var p : float = clamp(1.0 - (_timer.time_left / _lifetime), 0.0, 1.0) 89 | if lifetime_scale_curve: 90 | p = lifetime_scale_curve.sample_baked(p) 91 | global_scale = lerp(_scale_lerp_start, Vector2.ZERO, p) 92 | 93 | 94 | func _physics_process(delta: float) -> void: 95 | addForce(gravity_direction.normalized() * gravity_scale * delta) 96 | _lin_vel += _lin_accel 97 | _ang_vel += _ang_accel 98 | 99 | var lin_drag_magnitude : float = _lin_vel.length() * lin_drag * delta 100 | var lin_drag_force : Vector2 = -1.0 * _lin_vel.normalized() * lin_drag_magnitude 101 | _lin_vel += lin_drag_force 102 | 103 | var ang_drag_magnitude : float = _ang_vel * ang_drag * delta 104 | var ang_drag_force : float = sign(_ang_vel) * -1.0 * ang_drag_magnitude 105 | _ang_vel += ang_drag_force 106 | 107 | global_position += _lin_vel * delta 108 | global_rotation += _ang_vel * delta 109 | 110 | _lin_accel = Vector2.ZERO 111 | _ang_accel = 0.0 112 | 113 | 114 | 115 | 116 | func spawn(pos : Vector2, rot : float, s : Vector2, lifetime : float = 3.0) -> void: 117 | visible = true 118 | 119 | global_position = pos 120 | global_rotation = rot 121 | global_scale = s 122 | _scale_lerp_start = s 123 | 124 | _lifetime = lifetime 125 | _timer.start(lifetime) 126 | _t = 0.0 127 | 128 | _lin_vel = Vector2.ZERO 129 | _ang_vel = 0.0 130 | _lin_accel = Vector2.ZERO 131 | _ang_accel = 0.0 132 | 133 | set_process(true) 134 | set_physics_process(true) 135 | 136 | 137 | func despawn() -> void: 138 | visible = false 139 | set_process(false) 140 | set_physics_process(false) 141 | _t = 1.0 142 | 143 | 144 | 145 | 146 | func addForce(force : Vector2) -> void: 147 | if _mass > 0.0: 148 | force /= _mass 149 | _lin_accel += force 150 | 151 | 152 | func addTorque(torque : float) -> void: 153 | _ang_accel += torque 154 | 155 | 156 | 157 | 158 | func setPolygon(poly : PackedVector2Array, c : Color, texture_info : Dictionary) -> void: 159 | set_polygon(poly) 160 | poly.append(poly[0]) 161 | _line.points = poly 162 | setColor(c) 163 | setTexture(texture_info) 164 | 165 | 166 | func setTexture(texture_info : Dictionary) -> void: 167 | if not texture_info: return 168 | texture = texture_info.texture 169 | texture_scale = texture_info.scale 170 | texture_offset = texture_info.offset 171 | texture_rotation = texture_info.rot 172 | 173 | 174 | func setColor(color : Color) -> void: 175 | self_modulate = color 176 | 177 | 178 | func setMass(new_mass : float) -> void: 179 | _mass = new_mass 180 | 181 | 182 | 183 | 184 | func _on_Timer_timeout() -> void: 185 | emit_signal("Despawn", self) 186 | -------------------------------------------------------------------------------- /demo/src/FractureShard.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://demo/src/FractureShard.gd" type="Script" id=1] 4 | 5 | [sub_resource type="Curve" id=1] 6 | _data = [ Vector2( 0.374545, 0 ), 0.0, 3.69565, 0, 0, Vector2( 1, 1 ), 0.0555559, 0.0, 0, 0 ] 7 | 8 | [node name="FractureShard" type="Polygon2D"] 9 | script = ExtResource( 1 ) 10 | lin_drag = 4.0 11 | ang_drag = 0.25 12 | gravity_scale = 0.0 13 | lifetime_scale_curve = SubResource( 1 ) 14 | 15 | [node name="Line2D" type="Line2D" parent="."] 16 | modulate = Color( 1.25, 1.25, 1.25, 1 ) 17 | width = 8.0 18 | default_color = Color( 1, 1, 1, 1 ) 19 | joint_mode = 2 20 | begin_cap_mode = 2 21 | end_cap_mode = 2 22 | 23 | [node name="Timer" type="Timer" parent="."] 24 | one_shot = true 25 | [connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"] 26 | -------------------------------------------------------------------------------- /demo/src/Main.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | 4 | 5 | # MIT License 6 | # ----------------------------------------------------------------------- 7 | # This file is part of: 8 | # GODOT Polygon 2D Fracture 9 | # https://github.com/SoloByte/godot-polygon2d-fracture 10 | # ----------------------------------------------------------------------- 11 | # Copyright (c) 2021 David Grueneis 12 | # 13 | # Permission is hereby granted, free of charge, to any person obtaining a copy 14 | # of this software and associated documentation files (the "Software"), to deal 15 | # in the Software without restriction, including without limitation the rights 16 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | # copies of the Software, and to permit persons to whom the Software is 18 | # furnished to do so, subject to the following conditions: 19 | # 20 | # The above copyright notice and this permission notice shall be included in all 21 | # copies or substantial portions of the Software. 22 | # 23 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | # SOFTWARE. 30 | 31 | 32 | 33 | 34 | @export var test_scenes : Array[PackedScene] # (Array, PackedScene) 35 | 36 | var _cur_test_scene_index : int = 0 37 | var _cur_test_scene = null 38 | 39 | 40 | 41 | 42 | func _ready(): 43 | _cur_test_scene_index = -1 44 | changeTest() 45 | 46 | 47 | 48 | func _input(event): 49 | if event.is_action_pressed("fullscreen"): 50 | get_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN if (not ((get_window().mode == Window.MODE_EXCLUSIVE_FULLSCREEN) or (get_window().mode == Window.MODE_FULLSCREEN))) else Window.MODE_WINDOWED 51 | 52 | if event.is_action_pressed("ui_cancel"): 53 | get_tree().quit() 54 | 55 | if event.is_action_pressed("change-test"): 56 | changeTest() 57 | 58 | 59 | func changeTest() -> void: 60 | Engine.time_scale = 1.0 61 | if _cur_test_scene: 62 | _cur_test_scene.queue_free() 63 | 64 | _cur_test_scene_index = wrapi(_cur_test_scene_index + 1, 0, test_scenes.size()) 65 | var instance = test_scenes[_cur_test_scene_index].instantiate() 66 | add_child(instance) 67 | 68 | #for some reason I need to do that in Godot v3.3.1... 69 | #the camera in the scene is already setup with current = true and zoom = Vector2(2,2) 70 | #but it does not work without setting it here again 71 | instance.get_node("Camera2D").make_current() 72 | instance.get_node("Camera2D").zoom = Vector2(0.5,0.5) 73 | 74 | 75 | _cur_test_scene = instance 76 | -------------------------------------------------------------------------------- /demo/src/Main.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=12 format=3 uid="uid://b785hkyo03kns"] 2 | 3 | [ext_resource type="Script" path="res://demo/src/Main.gd" id="1"] 4 | [ext_resource type="PackedScene" uid="uid://b5nk7e37xxhkg" path="res://demo/test-scenes/fracture-scenes/usable/Fracture_Simple.tscn" id="2"] 5 | [ext_resource type="PackedScene" uid="uid://cub2gj0u8g4qv" path="res://demo/test-scenes/fracture-scenes/usable/Fracture_Delaunay.tscn" id="3"] 6 | [ext_resource type="PackedScene" uid="uid://uw5nk55tgvrf" path="res://demo/test-scenes/fracture-scenes/usable/Fracture_DelaunayRectangle.tscn" id="4"] 7 | [ext_resource type="PackedScene" uid="uid://cy7qo4lnb6sc3" path="res://demo/test-scenes/fracture-scenes/usable/Fracture_DelaunayConvex.tscn" id="5"] 8 | [ext_resource type="PackedScene" uid="uid://dsamrisjqiixl" path="res://demo/test-scenes/fracture-scenes/usable/Fracture.tscn" id="6"] 9 | [ext_resource type="PackedScene" uid="uid://vdocs38e3ce0" path="res://demo/test-scenes/cut-scenes/usable/CutFracture.tscn" id="7"] 10 | [ext_resource type="PackedScene" path="res://demo/WorldEnvironment.tscn" id="8"] 11 | [ext_resource type="PackedScene" uid="uid://bls1gwiki0ia4" path="res://demo/test-scenes/point-fracture/usable/PointFractureScene.tscn" id="9"] 12 | [ext_resource type="PackedScene" uid="uid://pdw1oj7ma01k" path="res://demo/test-scenes/blob-scenes/usable/BlobRestoreAdvScene.tscn" id="10"] 13 | [ext_resource type="PackedScene" uid="uid://c1frwj2m6e4hx" path="res://demo/test-scenes/blob-scenes/usable/BlobRestoreSimpleScene.tscn" id="11"] 14 | 15 | [node name="Main" type="Node2D"] 16 | script = ExtResource("1") 17 | test_scenes = Array[PackedScene]([ExtResource("7"), ExtResource("9"), ExtResource("11"), ExtResource("10"), ExtResource("3"), ExtResource("5"), ExtResource("4"), ExtResource("6"), ExtResource("2")]) 18 | 19 | [node name="WorldEnvironment" parent="." instance=ExtResource("8")] 20 | -------------------------------------------------------------------------------- /demo/src/PointFracture.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | 4 | 5 | # MIT License 6 | # ----------------------------------------------------------------------- 7 | # This file is part of: 8 | # GODOT Polygon 2D Fracture 9 | # https://github.com/SoloByte/godot-polygon2d-fracture 10 | # ----------------------------------------------------------------------- 11 | # Copyright (c) 2021 David Grueneis 12 | # 13 | # Permission is hereby granted, free of charge, to any person obtaining a copy 14 | # of this software and associated documentation files (the "Software"), to deal 15 | # in the Software without restriction, including without limitation the rights 16 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | # copies of the Software, and to permit persons to whom the Software is 18 | # furnished to do so, subject to the following conditions: 19 | # 20 | # The above copyright notice and this permission notice shall be included in all 21 | # copies or substantial portions of the Software. 22 | # 23 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | # SOFTWARE. 30 | 31 | 32 | 33 | 34 | const FLICK_MAX_VELOCITY : float = 2000.0 35 | const FLICK_MIN_VELOCITY : float = 500.0 36 | const FLICK_MAGNITUDE_SCALE_FACTOR : float = 3.0 37 | const MAX_AMMO : int = 5 38 | 39 | 40 | 41 | 42 | @export var fracture_body_color: Color 43 | @export var no_ammo_line_color: Color 44 | 45 | 46 | 47 | 48 | var _cur_fracture_color : Color = fracture_body_color 49 | var _fracture_disabled : bool = false 50 | var _flick_start_point := Vector2.ZERO 51 | var _fracture_balls_count : int = 0 52 | 53 | 54 | 55 | 56 | @onready var rigidbody_template = preload("res://demo/src/RigidBody2D.tscn") 57 | @onready var polyFracture := PolygonFracture.new() 58 | 59 | @onready var _pool_fracture_shards := $Pool_FractureShards 60 | @onready var _pool_cut_visualizer := $Pool_CutVisualizer 61 | @onready var _pool_point_fracture_ball := $Pool_PointFractureBall 62 | 63 | @onready var _source_polygon_parent := $SourceParent 64 | @onready var _rng := RandomNumberGenerator.new() 65 | @onready var _flick_line := $FlickLine 66 | @onready var _flick_line_arrow := $FlickLine/Arrow 67 | @onready var _default_flick_line_color : Color = _flick_line.modulate 68 | 69 | 70 | 71 | 72 | func _ready() -> void: 73 | _rng.randomize() 74 | 75 | var color := Color.WHITE 76 | color.s = fracture_body_color.s 77 | color.v = fracture_body_color.v 78 | color.h = _rng.randf() 79 | _cur_fracture_color = color 80 | _source_polygon_parent.modulate = _cur_fracture_color 81 | 82 | _flick_line.points = [Vector2.ZERO, Vector2.ZERO] 83 | _flick_line.visible = false 84 | 85 | 86 | func _process(delta: float) -> void: 87 | if _flick_line.visible: 88 | var end_point : Vector2 = get_global_mouse_position() 89 | var flick_vec : Vector2 = end_point - _flick_start_point 90 | 91 | var line_point : Vector2 = _flick_start_point + flick_vec.normalized() * min(flick_vec.length(), FLICK_MAX_VELOCITY / FLICK_MAGNITUDE_SCALE_FACTOR) 92 | 93 | _flick_line_arrow.global_rotation = flick_vec.normalized().angle() + PI 94 | 95 | _flick_line.set_point_position(1, line_point) 96 | 97 | if _fracture_balls_count >= MAX_AMMO: 98 | _flick_line.modulate = no_ammo_line_color 99 | else: 100 | _flick_line.modulate = _default_flick_line_color 101 | 102 | 103 | func _input(event: InputEvent) -> void: 104 | if _fracture_disabled: return 105 | if event is InputEventMouseButton: 106 | if event.button_index == 1: 107 | if event.pressed: 108 | _flick_start_point = get_global_mouse_position() 109 | _flick_line_arrow.global_position = _flick_start_point 110 | _flick_line.set_point_position(0, _flick_start_point) 111 | _flick_line.set_point_position(1, _flick_start_point) 112 | _flick_line.visible = true 113 | else: 114 | if _fracture_balls_count >= MAX_AMMO: 115 | _flick_line.visible = false 116 | return 117 | 118 | var flick_end_point : Vector2 = get_global_mouse_position() 119 | if flick_end_point == _flick_start_point: 120 | _flick_line.visible = false 121 | return 122 | 123 | var flick_vec : Vector2 = flick_end_point - _flick_start_point 124 | var instance = _pool_point_fracture_ball.getInstance() 125 | if not instance: 126 | return 127 | 128 | var launch_vec : Vector2 = -flick_vec.normalized() 129 | var launch_magnitude : float = clamp(flick_vec.length() * FLICK_MAGNITUDE_SCALE_FACTOR, FLICK_MIN_VELOCITY, FLICK_MAX_VELOCITY) 130 | var rand_lifetime : float = _rng.randf_range(5.0, 10.0) 131 | instance.spawn(_flick_start_point, launch_vec * launch_magnitude, rand_lifetime, self) 132 | 133 | _flick_line.visible = false 134 | 135 | fractureBallSpawned() 136 | 137 | 138 | 139 | 140 | func cutSourcePolygons(source, cut_pos : Vector2, cut_shape : PackedVector2Array, cut_rot : float, cut_force : float = 0.0, fade_speed : float = 2.0) -> void: 141 | spawnVisualizer(cut_pos, cut_shape, fade_speed) 142 | 143 | var source_polygon : PackedVector2Array = source.get_polygon() 144 | var total_area : float = PolygonLib.getPolygonArea(source_polygon) 145 | 146 | var source_trans : Transform2D = source.get_global_transform() 147 | var cut_trans := Transform2D(cut_rot, cut_pos) 148 | 149 | var s_lin_vel := Vector2.ZERO 150 | var s_ang_vel : float = 0.0 151 | var s_mass : float = 0.0 152 | 153 | if source is RigidBody2D: 154 | s_lin_vel = source.linear_velocity 155 | s_ang_vel = source.angular_velocity 156 | s_mass = source.mass 157 | 158 | 159 | var cut_fracture_info : Dictionary = polyFracture.cutFracture(source_polygon, cut_shape, source_trans, cut_trans, 2500, 1500, 100, 1) 160 | 161 | if cut_fracture_info.shapes.size() <= 0 and cut_fracture_info.fractures.size() <= 0: 162 | return 163 | 164 | for fracture in cut_fracture_info.fractures: 165 | for fracture_shard in fracture: 166 | var area_p : float = fracture_shard.area / total_area 167 | var rand_lifetime : float = _rng.randf_range(1.0, 3.0) + 2.0 * area_p 168 | spawnFractureBody(fracture_shard, source.getTextureInfo(), s_mass * area_p, rand_lifetime) 169 | 170 | 171 | for shape in cut_fracture_info.shapes: 172 | var area_p : float = shape.area / total_area 173 | var mass : float = s_mass * area_p 174 | var dir : Vector2 = (shape.spawn_pos - cut_pos).normalized() 175 | 176 | call_deferred("spawnRigibody2d", shape, source.modulate, s_lin_vel + (dir * cut_force) / mass, s_ang_vel, mass, cut_pos, source.getTextureInfo()) 177 | 178 | source.queue_free() 179 | 180 | 181 | func fractureCollision(pos : Vector2, other_body, fracture_ball) -> void: 182 | if _fracture_disabled: return 183 | 184 | var p : float = fracture_ball.launch_velocity / FLICK_MAX_VELOCITY 185 | var cut_shape : PackedVector2Array = polyFracture.generateRandomPolygon(Vector2(25, 200) * p, Vector2(18,72), Vector2.ZERO) 186 | cutSourcePolygons(other_body, pos, cut_shape, 0.0, _rng.randf_range(400.0, 800.0), 2.0) 187 | 188 | _fracture_disabled = true 189 | set_deferred("_fracture_disabled", false) 190 | 191 | 192 | 193 | 194 | func spawnRigibody2d(shape_info : Dictionary, color : Color, lin_vel : Vector2, ang_vel : float, mass : float, cut_pos : Vector2, texture_info : Dictionary) -> void: 195 | var instance = rigidbody_template.instantiate() 196 | _source_polygon_parent.add_child(instance) 197 | instance.global_position = shape_info.spawn_pos 198 | instance.global_rotation = shape_info.spawn_rot 199 | instance.set_polygon(shape_info.centered_shape) 200 | instance.modulate = color 201 | instance.linear_velocity = lin_vel 202 | instance.angular_velocity = ang_vel 203 | instance.mass = mass 204 | instance.setTexture(PolygonLib.setTextureOffset(texture_info, shape_info.centroid)) 205 | 206 | 207 | func spawnFractureBody(fracture_shard : Dictionary, texture_info : Dictionary, new_mass : float, life_time : float) -> void: 208 | var instance = _pool_fracture_shards.getInstance() 209 | if not instance: 210 | return 211 | 212 | var dir : Vector2 = (fracture_shard.spawn_pos - fracture_shard.source_global_trans.get_origin()).normalized() 213 | instance.spawn(fracture_shard.spawn_pos, fracture_shard.spawn_rot, fracture_shard.source_global_trans.get_scale(), life_time) 214 | instance.setPolygon(fracture_shard.centered_shape, _cur_fracture_color, PolygonLib.setTextureOffset(texture_info, fracture_shard.centroid)) 215 | instance.setMass(new_mass) 216 | 217 | 218 | func spawnVisualizer(pos : Vector2, poly : PackedVector2Array, fade_speed : float) -> void: 219 | var instance = _pool_cut_visualizer.getInstance() 220 | instance.spawn(pos, fade_speed) 221 | instance.setPolygon(poly) 222 | 223 | 224 | 225 | 226 | func fractureBallDespawned(pos : Vector2, poly : PackedVector2Array) -> void: 227 | spawnVisualizer(pos, poly, 0.75) 228 | _fracture_balls_count -= 1 229 | 230 | 231 | func fractureBallSpawned() -> void: 232 | _fracture_balls_count += 1 233 | 234 | -------------------------------------------------------------------------------- /demo/src/RigidBody2D.gd: -------------------------------------------------------------------------------- 1 | extends RigidBody2D 2 | 3 | 4 | 5 | 6 | # MIT License 7 | # ----------------------------------------------------------------------- 8 | # This file is part of: 9 | # GODOT Polygon 2D Fracture 10 | # https://github.com/SoloByte/godot-polygon2d-fracture 11 | # ----------------------------------------------------------------------- 12 | # Copyright (c) 2021 David Grueneis 13 | # 14 | # Permission is hereby granted, free of charge, to any person obtaining a copy 15 | # of this software and associated documentation files (the "Software"), to deal 16 | # in the Software without restriction, including without limitation the rights 17 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | # copies of the Software, and to permit persons to whom the Software is 19 | # furnished to do so, subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be included in all 22 | # copies or substantial portions of the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | # SOFTWARE. 31 | 32 | 33 | 34 | 35 | @export var rand_linear_velocity_range: Vector2 = Vector2(750.0, 1000.0) 36 | #export(Vector2) var rand_angular_velocity_range = Vector2(-10.0, 10.0) 37 | @export var radius: float = 250.0 38 | @export var smoothing : int = 1 # (int, 0, 5, 1) 39 | 40 | @export var placed_in_level: bool = false 41 | @export var randomize_texture_properties: bool = true 42 | @export var poly_texture: Texture2D 43 | 44 | 45 | 46 | 47 | @onready var _polygon2d := $Polygon2D 48 | @onready var _line2d := $Polygon2D/Line2D 49 | @onready var _col_polygon2d := $CollisionPolygon2D 50 | @onready var _rng := RandomNumberGenerator.new() 51 | 52 | 53 | 54 | func _ready() -> void: 55 | _rng.randomize() 56 | if placed_in_level: 57 | var poly = PolygonLib.createCirclePolygon(radius, smoothing) 58 | setPolygon(poly) 59 | 60 | linear_velocity = Vector2.RIGHT.rotated(PI * 2.0 * _rng.randf()) * _rng.randf_range(rand_linear_velocity_range.x, rand_linear_velocity_range.y) 61 | 62 | _polygon2d.texture = poly_texture 63 | 64 | 65 | if randomize_texture_properties and is_instance_valid(poly_texture): 66 | var rand_scale : float = _rng.randf_range(0.25, 0.75) 67 | var t_size = poly_texture.get_size() / rand_scale 68 | var offset_range = t_size.x * 0.25 69 | _polygon2d.texture_offset = (t_size / 2) + Vector2(_rng.randf_range(-offset_range, offset_range), _rng.randf_range(-offset_range, offset_range)) 70 | _polygon2d.texture_scale = Vector2(rand_scale, rand_scale) 71 | _polygon2d.texture_rotation = _rng.randf_range(0.0, PI * 2.0) 72 | #_polygon2d.texture_offset = Vector2(_rng.randf_range(-500, 500), _rng.randf_range(-500, 500)) 73 | 74 | 75 | 76 | func getGlobalRotPolygon() -> float: 77 | return _polygon2d.global_rotation 78 | 79 | func setPolygon(poly : PackedVector2Array) -> void: 80 | _polygon2d.set_polygon(poly) 81 | _col_polygon2d.set_polygon(poly) 82 | poly.append(poly[0]) 83 | _line2d.points = poly 84 | 85 | 86 | func setTexture(texture_info : Dictionary) -> void: 87 | _polygon2d.texture = texture_info.texture 88 | _polygon2d.texture_scale = texture_info.scale 89 | _polygon2d.texture_offset = texture_info.offset 90 | _polygon2d.texture_rotation = texture_info.rot 91 | 92 | 93 | func getTextureInfo() -> Dictionary: 94 | return {"texture" : _polygon2d.texture, "rot" : _polygon2d.texture_rotation, "offset" : _polygon2d.texture_offset, "scale" : _polygon2d.texture_scale} 95 | 96 | 97 | func getPolygon() -> PackedVector2Array: 98 | return _polygon2d.get_polygon() 99 | 100 | 101 | func get_polygon() -> PackedVector2Array: 102 | return getPolygon() 103 | 104 | func set_polygon(poly : PackedVector2Array) -> void: 105 | setPolygon(poly) 106 | -------------------------------------------------------------------------------- /demo/src/RigidBody2D.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://demo/src/RigidBody2D.gd" type="Script" id=1] 4 | 5 | [sub_resource type="PhysicsMaterial" id=1] 6 | friction = 0.0 7 | bounce = 1.0 8 | 9 | [node name="RigidBody2D" type="RigidBody2D"] 10 | collision_layer = 4 11 | collision_mask = 5 12 | mass = 100.0 13 | physics_material_override = SubResource( 1 ) 14 | gravity_scale = 0.0 15 | script = ExtResource( 1 ) 16 | rand_linear_velocity_range = Vector2( 1500, 3000 ) 17 | 18 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="."] 19 | 20 | [node name="Polygon2D" type="Polygon2D" parent="."] 21 | show_behind_parent = true 22 | color = Color( 0.545098, 0.545098, 0.545098, 1 ) 23 | 24 | [node name="Line2D" type="Line2D" parent="Polygon2D"] 25 | default_color = Color( 1.25, 1.25, 1.25, 1 ) 26 | joint_mode = 2 27 | begin_cap_mode = 2 28 | end_cap_mode = 2 29 | antialiased = true 30 | 31 | [node name="Center" type="Polygon2D" parent="."] 32 | visible = false 33 | color = Color( 0, 0, 0, 1 ) 34 | polygon = PackedVector2Array( -10.3909, 0.536697, -0.314636, -12.5448, 10.6455, 0.183144, 0.0389175, 12.0272 ) 35 | -------------------------------------------------------------------------------- /demo/src/SourcePolygon.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene format=2] 2 | 3 | [node name="SourcePolygon" type="Polygon2D"] 4 | antialiased = true 5 | polygon = PackedVector2Array( -201.839, -106.241, -116.313, -181.077, -5.98975, -47.3778, 159.866, -168.604, 215.992, 49.6661, 98.7803, 16.8057, -25.8111, 47.9535, 11.9765, 163.701, -149.276, 77.284, -131.458, -23.3876 ) 6 | -------------------------------------------------------------------------------- /demo/src/blob/Blob.gd: -------------------------------------------------------------------------------- 1 | extends RigidBody2D 2 | class_name Blob 3 | 4 | 5 | 6 | 7 | # MIT License 8 | # ----------------------------------------------------------------------- 9 | # This file is part of: 10 | # GODOT Polygon 2D Fracture 11 | # https://github.com/SoloByte/godot-polygon2d-fracture 12 | # ----------------------------------------------------------------------- 13 | # Copyright (c) 2021 David Grueneis 14 | # 15 | # Permission is hereby granted, free of charge, to any person obtaining a copy 16 | # of this software and associated documentation files (the "Software"), to deal 17 | # in the Software without restriction, including without limitation the rights 18 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | # copies of the Software, and to permit persons to whom the Software is 20 | # furnished to do so, subject to the following conditions: 21 | # 22 | # The above copyright notice and this permission notice shall be included in all 23 | # copies or substantial portions of the Software. 24 | # 25 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | # SOFTWARE. 32 | 33 | 34 | 35 | 36 | #Blobs are the enemies of my game Fracture Hell. 37 | #This script is almost identical to the one I use in Fracture Hell. 38 | #Check the game out on itch.io [https://solobytegames.itch.io/fracture-hell]. 39 | #Comments, Feedback, a Rating, or any other form of interaction is greatly appreciated :D 40 | 41 | 42 | 43 | 44 | signal Died(ref, pos) 45 | signal Damaged(blob, pos, shape, color, fade_speed) 46 | signal Fractured(blob, fracture_shard, new_mass, color, fracture_force, p) 47 | 48 | @export var advanced_regeneration: bool = false 49 | @export var invincible_time: float = 0.5 50 | @export var color_default: Color 51 | @export var radius: float = 24.0 52 | @export var smoothing : int = 1 # (int, 0, 4) 53 | @export var knockback_resistance_base: float = 1.0 #1.0 = no change #2.0 means twice as fast over #0.5 means takes longer twice as much 54 | 55 | @export var shape_area_percent : float = 0.25#shape_area < start_area * shape_area_percent -> shape will be fractured # (float, 0.0, 1.0) 56 | 57 | @export var fractures: int = 2 58 | @export var fracture_force: float = 200.0 59 | @export var heal_treshold : float = 0.8 #if health percent is above that threshold the start poly is used instead of morphing polygons # (float, 0.0, 1.0) 60 | 61 | @export var collision_damage := Vector2(5, 10) 62 | @export var collision_knockback_force: float = 10000 63 | @export var collision_knockback_time: float = 0.15 64 | 65 | @export var find_new_target_pos_tolerance: float = 50.0 66 | @export var target_reached_tolerance: float = 10.0 67 | @export var target_pos_interval_range := Vector2(0.5, 1.5) 68 | @export var keep_distance_range := Vector2.ZERO 69 | 70 | #export(int) var score_points : int = 25 71 | 72 | @export var rotate_towards_velocity: bool = false 73 | @export var max_speed: float = 250.0 74 | @export var accel: float = 1000.0 75 | @export var decel: float = 1500.0 76 | 77 | 78 | @export var regeneration_interval_range: Vector2 = Vector2.ZERO 79 | @export var regeneration_start_threshold : float = 0.5 # (float, 0.0, 1.0) 80 | @export var regeneration_amount: float = 10.0 81 | @export var regeneration_color: Color = Color.WHITE 82 | 83 | 84 | var cur_area : float = 0.0 85 | var start_area : float = 0.0 86 | var target = null 87 | var target_pos := Vector3.ZERO 88 | var prev_target_pos := Vector2.ZERO 89 | 90 | var knockback_resistance : float = 1.0 91 | var knockback_timer : float = 0.0 92 | 93 | var start_poly : PackedVector2Array 94 | 95 | var total_frame_heal_amount : float = 0.0 96 | 97 | var regeneration_timer : Timer = null 98 | var regeneration_started : bool = false 99 | 100 | var polygon_restorer : PolygonRestorer = null 101 | 102 | 103 | @onready var find_new_target_pos_tolerance_sq : float = find_new_target_pos_tolerance * find_new_target_pos_tolerance 104 | @onready var target_reached_tolerance_sq : float = target_reached_tolerance * target_reached_tolerance 105 | @onready var max_speed_sq : float = max_speed * max_speed 106 | 107 | @onready var _col_polygon := $CollisionPolygon2D 108 | @onready var _polygon := $Shape3D/Polygon2D 109 | @onready var _line := $Shape3D/Line2D 110 | @onready var _drop_poly := $DropPoly 111 | @onready var _origin_poly := $OriginPoly 112 | 113 | @onready var _rng := RandomNumberGenerator.new() 114 | @onready var _target_pos_timer := $TargetPosTimer 115 | @onready var _poly_fracture := PolygonFracture.new() 116 | @onready var _hit_flash_poly := $FlashPolygon 117 | @onready var _hit_flash_anim_player := $AnimationPlayer 118 | @onready var _invincible_timer := $InvincibleTimer 119 | 120 | 121 | 122 | func isInvincible() -> bool: 123 | return not _invincible_timer.is_stopped() 124 | 125 | func isDead() -> bool: 126 | return cur_area <= 0.0 127 | 128 | func canBeHealed() -> bool: 129 | return getHealthPercent() < 1.0 and not isDead() 130 | 131 | func getHealthPercent() -> float: 132 | if start_area == 0.0: 133 | return 0.0 134 | return cur_area / start_area 135 | 136 | func hasRegeneration() -> bool: 137 | return regeneration_timer != null and regeneration_amount > 0.0 138 | 139 | func canRegenerate() -> bool: 140 | return (getHealthPercent() < regeneration_start_threshold or regeneration_started) and regeneration_timer.is_stopped() 141 | 142 | func isRegenerating() -> bool: 143 | return regeneration_started 144 | 145 | func setTarget(new_target) -> void: 146 | target = new_target 147 | # if target and is_instance_valid(target): 148 | startTargetPosTimer(target_pos_interval_range.x, target_pos_interval_range.y) 149 | setNewTargetPos() 150 | 151 | func isKnockbackActive() -> bool: 152 | return knockback_timer > 0.0 153 | 154 | func getCurColor() -> Color: 155 | return _origin_poly.modulate 156 | 157 | func getCurMaxSpeed() -> float: 158 | var factor : float = 1.0 159 | if isRegenerating(): 160 | factor = 2.0 161 | return lerp(max_speed, max_speed * 1.2, 1.0 - getHealthPercent()) * factor 162 | 163 | func getCurMaxSpeedSq() -> float: 164 | var speed : float = getCurMaxSpeed() 165 | return speed * speed 166 | 167 | func getCurAccel() -> float: 168 | var factor : float = 1.0 169 | return lerp(accel, accel * 1.3, 1.0 - getHealthPercent()) * factor 170 | 171 | func getCurDecel() -> float: 172 | var factor : float = 1.0 173 | return lerp(decel, decel * 1.3, 1.0 - getHealthPercent()) * factor 174 | 175 | func hasTarget() -> bool: 176 | return target != null 177 | 178 | 179 | 180 | 181 | 182 | func _ready() -> void: 183 | _rng.randomize() 184 | 185 | if radius > 0.0: 186 | var new_polygon : PackedVector2Array = PolygonLib.createCirclePolygon(radius, smoothing) 187 | start_poly = new_polygon 188 | setPolygon(start_poly) 189 | mass = radius 190 | start_area = PI * radius * radius 191 | else: 192 | start_poly = _polygon.get_polygon() 193 | setPolygon(start_poly, true) 194 | start_area = PolygonLib.getPolygonArea(start_poly) 195 | 196 | cur_area = start_area 197 | 198 | 199 | if not advanced_regeneration: 200 | polygon_restorer = PolygonRestorer.new() 201 | polygon_restorer.addShape(_polygon.get_polygon(), start_area) 202 | 203 | applyColor(color_default) 204 | 205 | _hit_flash_poly.visible = false 206 | 207 | 208 | _origin_poly.set_polygon(_polygon.get_polygon()) 209 | _drop_poly.set_polygon(_polygon.get_polygon()) 210 | # _drop_poly.set_polygon(PolygonLib.scalePolygon(_polygon.get_polygon(), Vector2(1.5, 1.5))) 211 | _drop_poly.modulate.a = lerp(0.2, 0.7, 1.0 - getHealthPercent()) 212 | 213 | 214 | if regeneration_interval_range != Vector2.ZERO: 215 | var timer := Timer.new() 216 | add_child(timer) 217 | timer.one_shot = true 218 | timer.autostart = false 219 | timer.connect("timeout", Callable(self, "On_Regeneration_Timer_Timeout")) 220 | regeneration_timer = timer 221 | 222 | setTarget(null) 223 | 224 | 225 | 226 | 227 | func _integrate_forces(state: PhysicsDirectBodyState2D) -> void: 228 | if isKnockbackActive(): return 229 | 230 | 231 | if state.get_contact_count() > 0: 232 | 233 | var collisions : Dictionary = {} 234 | #filtering the collisions 235 | for i in range(state.get_contact_count()): 236 | var id = state.get_contact_collider_id(i) 237 | if collisions.has(id): 238 | var shape = state.get_contact_collider_shape(i) 239 | collisions[id].shapes.append(shape) 240 | else: 241 | var body = state.get_contact_collider_object(i) 242 | var shape = state.get_contact_collider_shape(i) 243 | var pos = state.get_contact_collider_position(i) 244 | collisions[id] = {"body" : body, "shapes" : [shape], "pos" : pos} 245 | 246 | 247 | var count : int = collisions.values().size() 248 | if target == null and count > 0: 249 | if count == 1: 250 | setTarget(collisions.values()[0].body) 251 | else: 252 | var rand_index : int = _rng.randi_range(0, count - 1) 253 | setTarget(collisions.values()[rand_index].body) 254 | 255 | 256 | for col in collisions.values(): 257 | if col.body is RigidBody2D: 258 | if col.body.has_method("damage"): 259 | var force : Vector2 = (col.body.global_position - global_position).normalized() * collision_knockback_force 260 | col.body.call_deferred("damage", collision_damage, col.pos, force, collision_knockback_time, self, getCurColor()) 261 | 262 | var input := Vector2.ZERO 263 | 264 | if target and is_instance_valid(target): 265 | if find_new_target_pos_tolerance > 0.0: 266 | var dis : float = (prev_target_pos - target.global_position).length_squared() 267 | if dis > find_new_target_pos_tolerance_sq: 268 | setNewTargetPos() 269 | 270 | if target_pos.z == 1.0: 271 | var cur_target_pos := Vector2(target_pos.x, target_pos.y) 272 | var target_vec := cur_target_pos - global_position 273 | var dis : float = target_vec.length_squared() 274 | 275 | if dis > target_reached_tolerance_sq: 276 | input = target_vec.normalized() 277 | 278 | 279 | if input != Vector2.ZERO: 280 | var increase : Vector2 = input * getCurAccel() * state.step 281 | state.linear_velocity += increase 282 | if state.linear_velocity.length_squared() > getCurMaxSpeedSq(): 283 | state.linear_velocity = state.linear_velocity.normalized() * getCurMaxSpeed() 284 | else: 285 | var decrease : Vector2 = linear_velocity.normalized() * getCurDecel() * state.step 286 | if decrease.length_squared() >= state.linear_velocity.length_squared(): 287 | state.linear_velocity = Vector2.ZERO 288 | else: 289 | state.linear_velocity -= decrease 290 | 291 | if rotate_towards_velocity: 292 | global_rotation = state.linear_velocity.angle() 293 | 294 | 295 | 296 | func _process(delta: float) -> void: 297 | processKnockbackTimer(delta) 298 | 299 | if target != null and not is_instance_valid(target): 300 | setTarget(null) 301 | 302 | 303 | func applyColor(color : Color) -> void: 304 | _polygon.modulate = color 305 | _line.modulate = color 306 | _origin_poly.modulate = color 307 | 308 | 309 | func damage(damage : Vector2, point : Vector2, knockback_force : Vector2, knockback_time : float, damage_dealer, damage_color : Color) -> Dictionary: 310 | if isDead(): 311 | return {"percent_cut" : 0.0, "dead" : true} 312 | 313 | if isInvincible(): 314 | _hit_flash_anim_player.play("invincible-hit-flash") 315 | return {"percent_cut" : 0.0, "dead" : false} 316 | 317 | setTarget(damage_dealer) 318 | 319 | var percent_cut : float = 0.0 320 | var cut_shape : PackedVector2Array = _poly_fracture.generateRandomPolygon(damage, Vector2(12,72), Vector2.ZERO) 321 | var cut_shape_area : float = PolygonLib.getPolygonArea(cut_shape) 322 | emit_signal("Damaged", self, point, cut_shape, damage_color, 5.0) 323 | # spawnShapeVisualizer(point, 0.0, cut_shape, damage_color, 1.0) 324 | var fracture_info : Dictionary = _poly_fracture.cutFracture(_polygon.get_polygon(), cut_shape, get_global_transform(), Transform2D(0.0, point), start_area * shape_area_percent, 200, 50, fractures) 325 | 326 | 327 | var p : float = cut_shape_area / cur_area 328 | for fracture in fracture_info.fractures: 329 | for shard in fracture: 330 | emit_signal("Fractured", self, shard, mass * (shard.area / cur_area), getCurColor(), fracture_force, p) 331 | # spawnFractureBody(shard, mass * (shard.area / cur_area), p) 332 | 333 | 334 | if not fracture_info or not fracture_info.shapes or fracture_info.shapes.size() <= 0: 335 | # if not award_points: 336 | # score_points = 0 337 | 338 | if hasRegeneration(): 339 | regeneration_started = false 340 | regeneration_timer.stop() 341 | 342 | call_deferred("kill") 343 | percent_cut = 1.0 344 | cur_area = 0.0 345 | else: 346 | var cur_shape 347 | var biggest_area : float = -1 348 | for shape in fracture_info.shapes: 349 | if shape.area > biggest_area: 350 | biggest_area = shape.area 351 | cur_shape = shape 352 | 353 | # if polygon_restorer: 354 | # polygon_restorer.addShape(_polygon.get_polygon(), cur_area) 355 | 356 | if polygon_restorer: 357 | polygon_restorer.addShape(cur_shape.shape, cur_shape.area) 358 | 359 | setPolygon(cur_shape.shape) 360 | 361 | # SoundServer.play2D("hurt", global_position, "blob", 1.0, SoundServer.OVERRIDE_BEHAVIOUR.OLDEST, -1) 362 | 363 | if _rng.randf() > 0.1: 364 | apply_central_impulse(knockback_force) 365 | knockback_timer = knockback_time 366 | 367 | percent_cut = cur_shape.area / cur_area 368 | cur_area = cur_shape.area 369 | 370 | _hit_flash_anim_player.play("hit-flash") 371 | 372 | _drop_poly.modulate.a = lerp(0.2, 0.7, 1.0 - getHealthPercent()) 373 | 374 | _invincible_timer.start(invincible_time) 375 | if hasRegeneration(): 376 | if canRegenerate(): 377 | var rand_time : float = _rng.randf_range(abs(regeneration_interval_range.x), abs(regeneration_interval_range.y)) 378 | regeneration_timer.start(rand_time) 379 | if not regeneration_started: 380 | regeneration_started = true 381 | applyColor(regeneration_color) 382 | 383 | return {"percent_cut" : percent_cut , "dead" : isDead()} 384 | 385 | 386 | func setNewTargetPos() -> void: 387 | if not target or not is_instance_valid(target): 388 | prev_target_pos = Vector2.ZERO 389 | var rand_angle : float = _rng.randf() * 2.0 * PI 390 | var v := Vector2.RIGHT.rotated(rand_angle) * _rng.randf() * 1500 391 | target_pos = Vector3(v.x, v.y, 1.0) 392 | return 393 | if keep_distance_range.y <= 0.0: 394 | target_pos = Vector3(target.global_position.x, target.global_position.y, 1.0) 395 | prev_target_pos = target.global_position 396 | else: 397 | var random_rot : float = _rng.randf_range(0, PI * 2.0) 398 | var pos : Vector2 = target.global_position + Vector2.RIGHT.rotated(random_rot) * _rng.randf_range(keep_distance_range.x, keep_distance_range.y) 399 | target_pos = Vector3(pos.x, pos.y, 1.0) 400 | prev_target_pos = target.global_position 401 | 402 | 403 | func startTargetPosTimer(min_time : float, max_time : float) -> void: 404 | if max_time <= 0.0: return 405 | var time : float = _rng.randf_range(min_time, max_time) 406 | _target_pos_timer.start(time) 407 | 408 | 409 | func kill() -> void: 410 | # SoundServer.play2D("die", global_position, "blob", 1.0, SoundServer.OVERRIDE_BEHAVIOUR.OLDEST, -1) 411 | emit_signal("Died", self, global_position) 412 | queue_free() 413 | 414 | 415 | func heal(heal_amount : float) -> void: 416 | if canBeHealed(): 417 | if getHealthPercent() > heal_treshold: 418 | setPolygon(start_poly) 419 | cur_area = start_area 420 | _hit_flash_anim_player.play("heal") 421 | _drop_poly.modulate.a = lerp(0.2, 0.7, 1.0 - getHealthPercent()) 422 | if hasRegeneration(): 423 | regeneration_started = false 424 | regeneration_timer.stop() 425 | applyColor(color_default) 426 | else: 427 | if total_frame_heal_amount == 0.0: 428 | call_deferred("restore") 429 | total_frame_heal_amount += heal_amount 430 | 431 | 432 | func restore() -> void: 433 | if total_frame_heal_amount > 0.0: 434 | var poly : PackedVector2Array 435 | var area : float = 0.0 436 | if polygon_restorer: 437 | var shape_entry : Dictionary = polygon_restorer.popLast() 438 | poly = shape_entry.shape 439 | area = shape_entry.area 440 | else: 441 | poly = PolygonLib.restorePolygon(_polygon.get_polygon(), start_poly, total_frame_heal_amount) 442 | area = PolygonLib.getPolygonArea(poly) 443 | 444 | if area / start_area > heal_treshold: 445 | cur_area = start_area 446 | setPolygon(start_poly) 447 | else: 448 | cur_area = area 449 | setPolygon(poly) 450 | 451 | _hit_flash_anim_player.play("heal") 452 | _drop_poly.modulate.a = lerp(0.2, 0.7, 1.0 - getHealthPercent()) 453 | 454 | if hasRegeneration(): 455 | if getHealthPercent() < 1.0: 456 | if canRegenerate(): 457 | var rand_time : float = _rng.randf_range(abs(regeneration_interval_range.x), abs(regeneration_interval_range.y)) 458 | regeneration_timer.start(rand_time) 459 | else: 460 | regeneration_started = false 461 | applyColor(color_default) 462 | 463 | total_frame_heal_amount = 0.0 464 | 465 | 466 | func setPolygon(new_polygon : PackedVector2Array, exclude_main_poly : bool = false) -> void: 467 | _col_polygon.call_deferred("set_polygon", new_polygon) 468 | 469 | if not exclude_main_poly: 470 | _polygon.set_polygon(new_polygon) 471 | 472 | _hit_flash_poly.set_polygon(new_polygon) 473 | new_polygon.append(new_polygon[0]) 474 | _line.points = new_polygon 475 | 476 | 477 | func processKnockbackTimer(delta : float) -> void: 478 | if knockback_timer > 0.0: 479 | knockback_timer -= delta * knockback_resistance 480 | if knockback_timer <= 0.0: 481 | knockback_timer = 0.0 482 | 483 | 484 | 485 | 486 | func _on_TargetPosTimer_timeout() -> void: 487 | setNewTargetPos() 488 | startTargetPosTimer(target_pos_interval_range.x, target_pos_interval_range.y) 489 | 490 | 491 | func On_Regeneration_Timer_Timeout() -> void: 492 | heal(regeneration_amount) 493 | -------------------------------------------------------------------------------- /demo/src/blob/Blob.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=2] 2 | 3 | [ext_resource path="res://demo/src/blob/Blob.gd" type="Script" id=1] 4 | 5 | [sub_resource type="PhysicsMaterial" id=1] 6 | friction = 0.0 7 | bounce = 0.5 8 | 9 | [sub_resource type="Animation" id=2] 10 | resource_name = "heal" 11 | length = 0.35 12 | step = 0.01 13 | tracks/0/type = "value" 14 | tracks/0/path = NodePath("FlashPolygon:visible") 15 | tracks/0/interp = 1 16 | tracks/0/loop_wrap = true 17 | tracks/0/imported = false 18 | tracks/0/enabled = true 19 | tracks/0/keys = { 20 | "times": PackedFloat32Array( 0, 0.35 ), 21 | "transitions": PackedFloat32Array( 1, 1 ), 22 | "update": 1, 23 | "values": [ true, false ] 24 | } 25 | tracks/1/type = "value" 26 | tracks/1/path = NodePath("FlashPolygon:color") 27 | tracks/1/interp = 1 28 | tracks/1/loop_wrap = true 29 | tracks/1/imported = false 30 | tracks/1/enabled = true 31 | tracks/1/keys = { 32 | "times": PackedFloat32Array( 0, 0.15, 0.35 ), 33 | "transitions": PackedFloat32Array( 1, 1, 1 ), 34 | "update": 0, 35 | "values": [ Color( 1, 1.25, 1, 1 ), Color( 1, 1, 1, 1 ), Color( 1, 1, 1, 0 ) ] 36 | } 37 | tracks/2/type = "value" 38 | tracks/2/path = NodePath("Shape3D:scale") 39 | tracks/2/interp = 1 40 | tracks/2/loop_wrap = true 41 | tracks/2/imported = false 42 | tracks/2/enabled = true 43 | tracks/2/keys = { 44 | "times": PackedFloat32Array( 0, 0.15, 0.23, 0.3, 0.35 ), 45 | "transitions": PackedFloat32Array( 1, 1, 1, 1, 1 ), 46 | "update": 0, 47 | "values": [ Vector2( 1, 1 ), Vector2( 1, 1 ), Vector2( 2, 2 ), Vector2( 0.9, 0.9 ), Vector2( 1, 1 ) ] 48 | } 49 | tracks/3/type = "value" 50 | tracks/3/path = NodePath("Shape3D:modulate") 51 | tracks/3/interp = 1 52 | tracks/3/loop_wrap = true 53 | tracks/3/imported = false 54 | tracks/3/enabled = false 55 | tracks/3/keys = { 56 | "times": PackedFloat32Array( 0, 0.35 ), 57 | "transitions": PackedFloat32Array( 1, 1 ), 58 | "update": 0, 59 | "values": [ Color( 1, 1, 1, 1 ), Color( 1, 1, 1, 1 ) ] 60 | } 61 | 62 | [sub_resource type="Animation" id=3] 63 | resource_name = "hit-flash" 64 | length = 0.35 65 | step = 0.01 66 | tracks/0/type = "value" 67 | tracks/0/path = NodePath("FlashPolygon:visible") 68 | tracks/0/interp = 1 69 | tracks/0/loop_wrap = true 70 | tracks/0/imported = false 71 | tracks/0/enabled = true 72 | tracks/0/keys = { 73 | "times": PackedFloat32Array( 0, 0.35 ), 74 | "transitions": PackedFloat32Array( 1, 1 ), 75 | "update": 1, 76 | "values": [ true, false ] 77 | } 78 | tracks/1/type = "value" 79 | tracks/1/path = NodePath("FlashPolygon:color") 80 | tracks/1/interp = 1 81 | tracks/1/loop_wrap = true 82 | tracks/1/imported = false 83 | tracks/1/enabled = true 84 | tracks/1/keys = { 85 | "times": PackedFloat32Array( 0, 0.15, 0.35 ), 86 | "transitions": PackedFloat32Array( 1, 1, 1 ), 87 | "update": 0, 88 | "values": [ Color( 1.25, 1.25, 1.25, 1 ), Color( 1, 1, 1, 1 ), Color( 1, 1, 1, 0 ) ] 89 | } 90 | tracks/2/type = "value" 91 | tracks/2/path = NodePath("Shape3D:scale") 92 | tracks/2/interp = 1 93 | tracks/2/loop_wrap = true 94 | tracks/2/imported = false 95 | tracks/2/enabled = true 96 | tracks/2/keys = { 97 | "times": PackedFloat32Array( 0, 0.15, 0.23, 0.3, 0.35 ), 98 | "transitions": PackedFloat32Array( 1, 1, 1, 1, 1 ), 99 | "update": 0, 100 | "values": [ Vector2( 1, 1 ), Vector2( 1, 1 ), Vector2( 2, 2 ), Vector2( 0.9, 0.9 ), Vector2( 1, 1 ) ] 101 | } 102 | tracks/3/type = "value" 103 | tracks/3/path = NodePath("Shape3D:modulate") 104 | tracks/3/interp = 1 105 | tracks/3/loop_wrap = true 106 | tracks/3/imported = false 107 | tracks/3/enabled = false 108 | tracks/3/keys = { 109 | "times": PackedFloat32Array( 0, 0.35 ), 110 | "transitions": PackedFloat32Array( 1, 1 ), 111 | "update": 0, 112 | "values": [ Color( 1, 1, 1, 1 ), Color( 1, 1, 1, 1 ) ] 113 | } 114 | 115 | [sub_resource type="Animation" id=4] 116 | resource_name = "invincible-hit-flash" 117 | length = 0.35 118 | step = 0.01 119 | tracks/0/type = "value" 120 | tracks/0/path = NodePath("FlashPolygon:visible") 121 | tracks/0/interp = 1 122 | tracks/0/loop_wrap = true 123 | tracks/0/imported = false 124 | tracks/0/enabled = true 125 | tracks/0/keys = { 126 | "times": PackedFloat32Array( 0, 0.35 ), 127 | "transitions": PackedFloat32Array( 1, 1 ), 128 | "update": 1, 129 | "values": [ true, false ] 130 | } 131 | tracks/1/type = "value" 132 | tracks/1/path = NodePath("FlashPolygon:color") 133 | tracks/1/interp = 1 134 | tracks/1/loop_wrap = true 135 | tracks/1/imported = false 136 | tracks/1/enabled = true 137 | tracks/1/keys = { 138 | "times": PackedFloat32Array( 0, 0.15, 0.35 ), 139 | "transitions": PackedFloat32Array( 1, 1, 1 ), 140 | "update": 0, 141 | "values": [ Color( 1, 0.635294, 0.635294, 1 ), Color( 0.862745, 0.454902, 0.454902, 1 ), Color( 1, 1, 1, 0 ) ] 142 | } 143 | tracks/2/type = "value" 144 | tracks/2/path = NodePath("Shape3D:scale") 145 | tracks/2/interp = 1 146 | tracks/2/loop_wrap = true 147 | tracks/2/imported = false 148 | tracks/2/enabled = true 149 | tracks/2/keys = { 150 | "times": PackedFloat32Array( 0, 0.15, 0.23, 0.3, 0.35 ), 151 | "transitions": PackedFloat32Array( 1, 1, 1, 1, 1 ), 152 | "update": 0, 153 | "values": [ Vector2( 1, 1 ), Vector2( 1, 1 ), Vector2( 0.5, 0.5 ), Vector2( 1.1, 1.1 ), Vector2( 1, 1 ) ] 154 | } 155 | tracks/3/type = "value" 156 | tracks/3/path = NodePath("Shape3D:modulate") 157 | tracks/3/interp = 1 158 | tracks/3/loop_wrap = true 159 | tracks/3/imported = false 160 | tracks/3/enabled = false 161 | tracks/3/keys = { 162 | "times": PackedFloat32Array( 0, 0.35 ), 163 | "transitions": PackedFloat32Array( 1, 1 ), 164 | "update": 0, 165 | "values": [ Color( 1, 1, 1, 1 ), Color( 1, 1, 1, 1 ) ] 166 | } 167 | 168 | [node name="Blob" type="RigidBody2D"] 169 | collision_layer = 512 170 | collision_mask = 513 171 | mode = 2 172 | mass = 10.0 173 | physics_material_override = SubResource( 1 ) 174 | gravity_scale = 0.0 175 | max_contacts_reported = 12 176 | contact_monitor = true 177 | can_sleep = false 178 | linear_damp = 2.0 179 | script = ExtResource( 1 ) 180 | target_pos_interval_range = Vector2( 3, 5 ) 181 | keep_distance_range = Vector2( 350, 500 ) 182 | 183 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="."] 184 | 185 | [node name="DropPoly" type="Polygon2D" parent="."] 186 | scale = Vector2( 1.5, 1.5 ) 187 | color = Color( 1, 1, 1, 0.392157 ) 188 | 189 | [node name="OriginPoly" type="Polygon2D" parent="."] 190 | scale = Vector2( 1.05, 1.05 ) 191 | color = Color( 0.394531, 0.394531, 0.394531, 0.588235 ) 192 | 193 | [node name="Shape3D" type="Node2D" parent="."] 194 | 195 | [node name="Polygon2D" type="Polygon2D" parent="Shape3D"] 196 | color = Color( 0.772549, 0.772549, 0.772549, 1 ) 197 | 198 | [node name="Line2D" type="Line2D" parent="Shape3D"] 199 | width = 4.0 200 | default_color = Color( 1, 1, 1, 1 ) 201 | joint_mode = 2 202 | begin_cap_mode = 2 203 | end_cap_mode = 2 204 | antialiased = true 205 | 206 | [node name="FlashPolygon" type="Polygon2D" parent="."] 207 | scale = Vector2( 1.1, 1.1 ) 208 | color = Color( 1, 0.635294, 0.635294, 1 ) 209 | 210 | [node name="TargetPosTimer" type="Timer" parent="."] 211 | one_shot = true 212 | 213 | [node name="AnimationPlayer" type="AnimationPlayer" parent="."] 214 | anims/heal = SubResource( 2 ) 215 | anims/hit-flash = SubResource( 3 ) 216 | anims/invincible-hit-flash = SubResource( 4 ) 217 | 218 | [node name="InvincibleTimer" type="Timer" parent="."] 219 | one_shot = true 220 | 221 | [connection signal="timeout" from="TargetPosTimer" to="." method="_on_TargetPosTimer_timeout"] 222 | -------------------------------------------------------------------------------- /demo/src/circle-cast/CircleCast.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | class_name CircleCast 3 | 4 | 5 | 6 | 7 | # MIT License 8 | # ----------------------------------------------------------------------- 9 | # This file is part of: 10 | # GODOT Polygon 2D Fracture 11 | # https://github.com/SoloByte/godot-polygon2d-fracture 12 | # ----------------------------------------------------------------------- 13 | # Copyright (c) 2021 David Grueneis 14 | # 15 | # Permission is hereby granted, free of charge, to any person obtaining a copy 16 | # of this software and associated documentation files (the "Software"), to deal 17 | # in the Software without restriction, including without limitation the rights 18 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | # copies of the Software, and to permit persons to whom the Software is 20 | # furnished to do so, subject to the following conditions: 21 | # 22 | # The above copyright notice and this permission notice shall be included in all 23 | # copies or substantial portions of the Software. 24 | # 25 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | # SOFTWARE. 32 | 33 | 34 | 35 | 36 | @export var radius: float = 1.0 37 | @export var collision_layer: int 38 | 39 | @export var max_results: int = 6 40 | @export var margin: float = 0.0 41 | 42 | @export var collide_with_bodies: bool = true 43 | @export var collide_with_areas: bool = false 44 | 45 | 46 | 47 | 48 | var _query : PhysicsShapeQueryParameters2D = null 49 | var _circle_shape : CircleShape2D = null 50 | var _excluded : Array = [] 51 | 52 | 53 | 54 | 55 | func addExclusion(obj) -> void: 56 | _excluded.append(obj) 57 | 58 | func removeExclusion(obj) -> void: 59 | if not _excluded or _excluded.size() <= 0: return 60 | var index : int = _excluded.find(obj) 61 | removeExclusionIndex(index) 62 | 63 | func removeExclusionIndex(index : int) -> void: 64 | if not _excluded or _excluded.size() <= 0 or index < 0 or index >= _excluded.size(): return 65 | _excluded.remove_at(index) 66 | 67 | 68 | 69 | func setCircleShapeRadius(r : float) -> void: 70 | if r <= 0.0: return 71 | getCircleShape().radius = r 72 | 73 | 74 | func getCircleShape() -> CircleShape2D: 75 | if _circle_shape == null: 76 | _circle_shape = CircleShape2D.new() 77 | _circle_shape.radius = self.radius 78 | return _circle_shape 79 | 80 | 81 | func getQuery() -> PhysicsShapeQueryParameters2D: 82 | if not _query: 83 | setQuery(createQuerySimple()) 84 | return _query 85 | 86 | func setQuery(query : PhysicsShapeQueryParameters2D) -> void: 87 | _query = query 88 | 89 | 90 | func updateQuery(pos : Vector2, rot : float, r : float = -1.0) -> void: 91 | setCircleShapeRadius(r) 92 | updateCustomQuery(getQuery(), pos, rot, null) 93 | 94 | func updateCustomQuery(query : PhysicsShapeQueryParameters2D, pos : Vector2, rot : float, shape = null) -> void: 95 | if not query: return 96 | query.transform = Transform2D(rot, pos) 97 | if shape: 98 | query.set_shape(shape) 99 | 100 | 101 | 102 | func createQuerySimple() -> PhysicsShapeQueryParameters2D: 103 | return createQuery(global_position, global_rotation, Vector2.ZERO, getCircleShape(), collision_layer, _excluded, collide_with_bodies, collide_with_areas, margin) 104 | 105 | func createQuery(_pos : Vector2, _rot : float, _motion : Vector2, _shape, _collision_layer, _exclude : Array = [], _collide_with_bodies : bool = true, _collide_with_areas : bool = false, _margin : float = 0.0) -> PhysicsShapeQueryParameters2D: 106 | var query := PhysicsShapeQueryParameters2D.new() 107 | query.set_shape(_shape) 108 | query.motion = _motion 109 | query.collision_layer = _collision_layer 110 | query.exclude = _exclude 111 | query.collide_with_bodies = _collide_with_bodies 112 | query.collide_with_areas = _collide_with_areas 113 | query.transform = Transform2D(_rot, _pos) 114 | query.margin = _margin 115 | return query 116 | 117 | 118 | func getSpaceState() -> PhysicsDirectSpaceState2D: 119 | return get_world_2d().direct_space_state 120 | 121 | 122 | 123 | 124 | func cast(r : float = -1.0) -> Array: 125 | updateQuery(global_position, global_rotation, r) 126 | return intersectShape(getQuery(), max_results) 127 | 128 | func castStatic(r : float = -1.0) -> Array: 129 | setCircleShapeRadius(r) 130 | return intersectShape(getQuery(), max_results) 131 | 132 | func castCustom(_pos : Vector2, _rot : float, _shape, _collision_layer, _exclude : Array = [], _collide_with_bodies : bool = true, _collide_with_areas : bool = false, _margin : float = 0.0, max_results : int = 32) -> Array: 133 | var query = createQuery(_pos, _rot, Vector2.ZERO, _shape, _collision_layer, _exclude, _collide_with_bodies, _collide_with_areas, _margin) 134 | return intersectShape(query, max_results) 135 | 136 | 137 | static func filterResults(result : Array) -> Array: 138 | print("results: ", result) 139 | if not result or result.size() <= 0: 140 | return [] 141 | if result.size() == 1: 142 | return [result[0].collider] 143 | 144 | var filtered : Array = [] 145 | for i in range(result.size()): 146 | var body = result[i].collider 147 | if not body in filtered: 148 | filtered.append(body) 149 | 150 | return filtered 151 | 152 | static func filterResultsAdv(result : Array) -> Dictionary: 153 | if not result or result.size() <= 0: 154 | return {} 155 | 156 | var filtered : Dictionary = {} 157 | if result.size() == 1: 158 | filtered[result[0].collider_id] = {"body" : result[0].collider, "shapes" : [result[0].shape]} 159 | return filtered 160 | 161 | for r in result: 162 | if filtered.has(r.collider_id): 163 | filtered[r.collider_id].shapes.append(r.shape) 164 | else: 165 | filtered[r.collider_id] = {"body" : r.collider, "shapes" : [r.shape]} 166 | 167 | return filtered 168 | 169 | 170 | func castMotion(query : PhysicsShapeQueryParameters2D) -> Array: 171 | return getSpaceState().cast_motion(query) 172 | 173 | 174 | func getCollisionPoints(query : PhysicsShapeQueryParameters2D, max_results : int = 32) -> Array: 175 | return getSpaceState().collide_shape(query, max_results) 176 | 177 | 178 | func castNearest(query : PhysicsShapeQueryParameters2D) -> Dictionary: 179 | # -!!!- This method does not take into account the motion property of the object. 180 | #collider_id: The colliding object's ID. 181 | #linear_velocity: The colliding object's velocity Vector2. If the object is an Area2D, the result is (0, 0). 182 | #metadata: The intersecting shape's metadata. This metadata is different from Object.get_meta(), and is set with Physics2DServer.shape_set_data(). 183 | #normal: The object's surface normal at the intersection point. 184 | #point: The intersection point. 185 | #rid: The intersecting object's RID. 186 | #shape: The shape index of the colliding shape. 187 | return getSpaceState().get_rest_info(query) 188 | 189 | 190 | func intersectPoint(_point : PhysicsPointQueryParameters2D, _collision_layer : int, _max_results : int = 32, _exclude : Array = [], _collide_with_bodies : bool = true, _collide_with_areas : bool = false) -> Array: 191 | #collider: The colliding object. 192 | #collider_id: The colliding object's ID. 193 | #metadata: The intersecting shape's metadata. This metadata is different from Object.get_meta(), and is set with Physics2DServer.shape_set_data(). 194 | #rid: The intersecting object's RID. 195 | #shape: The shape index of the colliding shape. 196 | # -!!!- ConcavePolygonShape2Ds and CollisionPolygon2Ds in Segments build mode are not solid shapes. Therefore, they will not be detected. 197 | return getSpaceState().intersect_point(_point, _max_results) 198 | 199 | 200 | func intersectShape(query : PhysicsShapeQueryParameters2D, max_results: int = 32) -> Array: 201 | # -!!!- This method does not take into account the motion property of the object. 202 | #collider: The colliding object. 203 | #collider_id: The colliding object's ID. 204 | #metadata: The intersecting shape's metadata. This metadata is different from Object.get_meta(), and is set with Physics2DServer.shape_set_data(). 205 | #rid: The intersecting object's RID. 206 | #shape: The shape index of the colliding shape. 207 | 208 | #because of filtering only returns array of objects (no duplicate entries) 209 | return getSpaceState().intersect_shape(query, max_results) 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | ##public function for cast -> see pDoCast func for more information------------------------------------------------------- 244 | ##functions just differ in the parameters and what has to be set previously 245 | ##cast returns all objects inside the circle (motion vector is not used) 246 | #func castSimple(pos : Vector2, exclude : Array = [], max_results : int = 6, margin : float = 0.0) -> Array: 247 | # return pDoCast(pos, collision_layer, exclude, max_results, collide_with_bodies, collide_with_areas, margin) 248 | # 249 | #func cast(pos : Vector2, radius : float, exclude : Array = [], max_results : int = 6, margin : float = 0.0) -> Array: 250 | # setRadius(radius) 251 | # return pDoCast(pos, collision_layer, exclude, max_results, collide_with_bodies, collide_with_areas, margin) 252 | # 253 | # 254 | #func castQuery(pos : Vector2, rot : float, max_results : int = 6) -> Array: 255 | # if not _query: return [] 256 | # var space_state = get_world_2d().direct_space_state 257 | # updateQuery(pos, rot) 258 | # return space_state.intersect_shape(_query, max_results) 259 | # 260 | #func castCustomQuery(query : Physics2DShapeQueryParameters, pos : Vector2, rot : float, max_results : int = 6) -> Array: 261 | # if not query: return [] 262 | # var space_state = get_world_2d().direct_space_state 263 | # updateCustomQuery(query, pos, rot) 264 | # return space_state.intersect_shape(query, max_results) 265 | ##------------------------------------------------------------------------------------------------------------------------ 266 | # 267 | # 268 | # 269 | # 270 | ##public function for castSingle -> see pDoCastSingle func for more information------------------------------------------------------- 271 | ##functions just differ in the parameters and what has to be set previously 272 | ##returns the first object it hits (motion vector is not used) 273 | ##returns a collision normal 274 | #func castSingleSimple(pos : Vector2, exclude : Array = [], margin : float = 0.0) -> Dictionary: 275 | # return pDoCastSingle(pos, collision_layer, exclude, collide_with_bodies, collide_with_areas, margin) 276 | # 277 | #func castSingle(pos : Vector2, radius : float, exclude : Array = [], margin : float = 0.0) -> Dictionary: 278 | # setRadius(radius) 279 | # return pDoCastSingle(pos, collision_layer, exclude, collide_with_bodies, collide_with_areas, margin) 280 | # 281 | #func castSingleQuery(pos : Vector2) -> Dictionary: 282 | # if !_query: return {} 283 | # var space_state = get_world_2d().direct_space_state 284 | # updateQueryPos(pos) 285 | # return space_state.get_rest_info(_query) 286 | # 287 | #func castSingleCustomQuery(query : Physics2DShapeQueryParameters, pos : Vector2) -> Dictionary: 288 | # if !query: return {} 289 | # var space_state = get_world_2d().direct_space_state 290 | # updateCustomQueryPos(query, pos) 291 | # return space_state.get_rest_info(query) 292 | ##------------------------------------------------------------------------------------------------------------------------ 293 | # 294 | # 295 | # 296 | # 297 | ##public function for castMotion -> see pDoCastMotion func for more information------------------------------------------------------- 298 | ##functions just differ in the parameters and what has to be set previously 299 | ##casts along the given motion vector (does not return collision info, just motion factors) 300 | #func castMotionSimple(motion : Vector2, pos : Vector2, exclude : Array = [], margin : float = 0.0) -> Array: 301 | # return pDoCastMotion(motion, pos, collision_layer, exclude, collide_with_bodies, collide_with_areas, margin) 302 | # 303 | #func castMotion(motion : Vector2, pos : Vector2, radius : float, exclude : Array = [], margin : float = 0.0) -> Array: 304 | # setRadius(radius) 305 | # return pDoCastMotion(motion, pos, collision_layer, exclude, collide_with_bodies, collide_with_areas, margin) 306 | # 307 | #func castMotionQuery(motion : Vector2, pos : Vector2) -> Array: 308 | # if !_query: return [] 309 | # var space_state = get_world_2d().direct_space_state 310 | # updateQueryMotion(motion) 311 | # updateQueryPos(pos) 312 | # return space_state.cast_motion(_query) 313 | # 314 | #func castMotionCustomQuery(query : Physics2DShapeQueryParameters, motion : Vector2, pos : Vector2) -> Array: 315 | # if !query: return [] 316 | # var space_state = get_world_2d().direct_space_state 317 | # updateCustomQueryMotion(query, motion) 318 | # updateCustomQueryPos(query, pos) 319 | # return space_state.cast_motion(_query) 320 | ##------------------------------------------------------------------------------------------------------------------------ 321 | # 322 | # 323 | # 324 | # 325 | ##public function for castCollision -> see pDoCastCollision func for more information------------------------------------------------------- 326 | ##functions just differ in the parameters and what has to be set previously 327 | ##just returns collision points (no further collision info) 328 | #func castCollisionSimple(pos : Vector2, exclude : Array = [], max_results : int = 6, margin : float = 0.0) -> Array: 329 | # return pDoCastCollision(pos, collision_layer, exclude, max_results, collide_with_bodies, collide_with_areas, margin) 330 | # 331 | #func castCollision(pos : Vector2, radius : float, exclude : Array = [], max_results : int = 6, margin : float = 0.0) -> Array: 332 | # setRadius(radius) 333 | # return pDoCastCollision(pos, collision_layer, exclude, max_results, collide_with_bodies, collide_with_areas, margin) 334 | # 335 | #func castCollisionQuery(pos : Vector2,max_results : int = 6) -> Array: 336 | # if !_query: return [] 337 | # var space_state = get_world_2d().direct_space_state 338 | # updateQueryPos(pos) 339 | # return space_state.collide_shape(_query, max_results) 340 | # 341 | #func castCollisionCustomQuery(query : Physics2DShapeQueryParameters, pos : Vector2, max_results : int = 6) -> Array: 342 | # if !query: return [] 343 | # var space_state = get_world_2d().direct_space_state 344 | # updateCustomQueryPos(query, pos) 345 | # return space_state.collide_shape(_query, max_results) 346 | ##------------------------------------------------------------------------------------------------------------------------ 347 | # 348 | # 349 | # 350 | # 351 | ##public function for castSingle -> see pDoCastSingle func for more information------------------------------------------------------- 352 | ##functions just differ in the parameters and what has to be set previously 353 | ##Checks whether a point is inside any shape. 354 | #func intersectPoint(point: Vector2, exclude: Array = [], max_results: int = 6) -> Array: 355 | # #The shapes the point is inside of are returned in an array containing dictionaries with the following fields: 356 | # #collider: The colliding object. 357 | # #collider_id: The colliding object's ID. 358 | # #metadata: The intersecting shape's metadata. This metadata is different from Object.get_meta(), and is set with Physics2DServer.shape_set_data(). 359 | # #rid: The intersecting object's RID. 360 | # #shape: The shape index of the colliding shape. 361 | # var space_state = get_world_2d().direct_space_state 362 | # return space_state.intersect_point(point, max_results, exclude, collision_layer, collide_with_bodies, collide_with_areas) 363 | ##------------------------------------------------------------------------------------------------------------------------ 364 | # 365 | # 366 | # 367 | # 368 | # 369 | ##private funcs for doing the casting based on the parameters------------------------------------------------------------ 370 | #func pDoCast(_pos : Vector2, _collision_layer, _exclude : Array = [], _max_results : int = 6, _collide_with_bodies : bool = true, _collide_with_areas : bool = false, _margin : float = 0.0) -> Array: 371 | # #returns array of dictionaries 372 | # #collider: The colliding object. 373 | # #collider_id: The colliding object's ID. 374 | # #metadata: The intersecting shape's metadata. This metadata is different from Object.get_meta(), and is set with Physics2DServer.shape_set_data(). 375 | # #rid: The intersecting object's RID. 376 | # #shape: The shape index of the colliding shape. 377 | # 378 | # var space_state = get_world_2d().direct_space_state 379 | # var query := createQuery(_pos, Vector2.ZERO, _collision_layer, _exclude, _collide_with_bodies, _collide_with_areas, _margin) 380 | # return space_state.intersect_shape(query, _max_results) 381 | # 382 | # 383 | #func pDoCastSingle(_pos : Vector2, _collision_layer, _exclude : Array = [], _collide_with_bodies : bool = true, _collide_with_areas : bool = false, _margin : float = 0.0) -> Dictionary: 384 | # #returns dictionary 385 | # #collider_id: The colliding object's ID. 386 | # #linear_velocity: The colliding object's velocity Vector2. If the object is an Area2D, the result is (0, 0). 387 | # #metadata: The intersecting shape's metadata. This metadata is different from Object.get_meta(), and is set with Physics2DServer.shape_set_data(). 388 | # #normal: The object's surface normal at the intersection point. 389 | # #point: The intersection point. 390 | # #rid: The intersecting object's RID. 391 | # #shape: The shape index of the colliding shape. 392 | # var space_state = get_world_2d().direct_space_state 393 | # var query := createQuery(_pos, Vector2.ZERO, _collision_layer, _exclude, _collide_with_bodies, _collide_with_areas, _margin) 394 | # return space_state.get_rest_info(query) 395 | # 396 | # 397 | #func pDoCastMotion(_motion : Vector2, _pos : Vector2, _collision_layer, _exclude : Array = [], _collide_with_bodies : bool = true, _collide_with_areas : bool = false, _margin : float = 0.0) -> Array: 398 | # # The method will return an array with two floats between 0 and 1, 399 | # #both representing a fraction of motion. 400 | # #The first is how far the shape can move without triggering a collision, 401 | # #and the second is the point at which a collision will occur. 402 | # #If no collision is detected, the returned array will be [1, 1]. 403 | # var space_state = get_world_2d().direct_space_state 404 | # var query := createQuery(_pos, _motion, _collision_layer, _exclude, _collide_with_bodies, _collide_with_areas, _margin) 405 | # return space_state.cast_motion(query) 406 | # 407 | # 408 | #func pDoCastCollision(_pos : Vector2, _collision_layer, _exclude : Array = [], _max_results : int = 6, _collide_with_bodies : bool = true, _collide_with_areas : bool = false, _margin : float = 0.0) -> Array: 409 | # # The resulting array contains a list of points where the shape intersects another. 410 | # #Like with intersect_shape(), the number of returned results can be limited to save processing time. 411 | # var space_state = get_world_2d().direct_space_state 412 | # var query := createQuery(_pos, Vector2.ZERO, _collision_layer, _exclude, _collide_with_bodies, _collide_with_areas, _margin) 413 | # return space_state.collide_shape(query, _max_results) 414 | ##------------------------------------------------------------------------------------------------------------------------ 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | -------------------------------------------------------------------------------- /demo/src/circle-cast/CircleCast.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://demo/src/circle-cast/CircleCast.gd" type="Script" id=1] 4 | 5 | [node name="CircleCast" type="Node2D"] 6 | script = ExtResource( 1 ) 7 | -------------------------------------------------------------------------------- /demo/src/default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=3 uid="uid://pg84qr53ebf4"] 2 | 3 | [sub_resource type="Sky" id="1"] 4 | 5 | [resource] 6 | background_mode = 2 7 | sky = SubResource("1") 8 | -------------------------------------------------------------------------------- /demo/src/healing-aura/HealingAura.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | 4 | 5 | 6 | # MIT License 7 | # ----------------------------------------------------------------------- 8 | # This file is part of: 9 | # GODOT Polygon 2D Fracture 10 | # https://github.com/SoloByte/godot-polygon2d-fracture 11 | # ----------------------------------------------------------------------- 12 | # Copyright (c) 2021 David Grueneis 13 | # 14 | # Permission is hereby granted, free of charge, to any person obtaining a copy 15 | # of this software and associated documentation files (the "Software"), to deal 16 | # in the Software without restriction, including without limitation the rights 17 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | # copies of the Software, and to permit persons to whom the Software is 19 | # furnished to do so, subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be included in all 22 | # copies or substantial portions of the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | # SOFTWARE. 31 | 32 | 33 | 34 | 35 | 36 | enum HEAL_METHOD {DISABLED = 0, INTERVAL = 1, TRIGGER = 2} 37 | 38 | 39 | 40 | @export var mode_static: bool = false 41 | @export var radius : float = 1.0 # (float, 1.0, 1000.0) 42 | @export var smoothing : int = 0 # (int, 0, 4) 43 | @export var collision_layer: int # (int, LAYERS_2D_PHYSICS) 44 | 45 | 46 | @export var heal_method: HEAL_METHOD = HEAL_METHOD.DISABLED 47 | @export var heal_amount: float = 0.1 48 | @export var heal_interval: float = 1.0 49 | @export var autostart: bool = false 50 | @export var enabled: bool = true 51 | 52 | var _heal_timer : Timer = null 53 | var _heal_enabled : bool = false 54 | 55 | 56 | @onready var _polygon2d := $Polygon2D 57 | @onready var _circle_cast := $CircleCast 58 | @onready var _anim_player := $AnimationPlayer 59 | 60 | 61 | func isEnabled() -> bool: 62 | return _heal_enabled and not heal_method == HEAL_METHOD.DISABLED 63 | 64 | 65 | 66 | func _ready() -> void: 67 | _heal_enabled = enabled 68 | 69 | var poly : PackedVector2Array = PolygonLib.createCirclePolygon(radius, smoothing, Vector2.ZERO) 70 | setPolygon(poly) 71 | 72 | _circle_cast.radius = radius 73 | _circle_cast.collision_layer = collision_layer 74 | 75 | if heal_method == HEAL_METHOD.INTERVAL: 76 | var timer := Timer.new() 77 | add_child(timer) 78 | timer.wait_time = heal_interval 79 | timer.one_shot = false 80 | timer.autostart = false 81 | timer.connect("timeout", Callable(self, "On_Heal_Timer_Timeout")) 82 | _heal_timer = timer 83 | 84 | 85 | if autostart and isEnabled(): 86 | enable() 87 | 88 | 89 | 90 | 91 | func enable() -> void: 92 | _heal_enabled = true 93 | if _heal_timer and _heal_timer.is_stopped(): 94 | _heal_timer.start() 95 | 96 | _anim_player.play("idle") 97 | 98 | func disable() -> void: 99 | _heal_enabled = false 100 | if _heal_timer and not _heal_timer.is_stopped(): 101 | _heal_timer.stop() 102 | 103 | _anim_player.stop(true) 104 | 105 | 106 | 107 | func setPolygon(polygon : PackedVector2Array) -> void: 108 | _polygon2d.set_polygon(polygon) 109 | 110 | 111 | func cast() -> void: 112 | var results : Array 113 | if mode_static: 114 | results = _circle_cast.castStatic() 115 | else: 116 | results = _circle_cast.cast() 117 | 118 | var filtered : Dictionary = CircleCast.filterResultsAdv(results) 119 | 120 | for f in filtered.values(): 121 | heal(f.body) 122 | 123 | if filtered.size() > 0: 124 | _anim_player.play("heal") 125 | 126 | 127 | func heal(body) -> void: 128 | if not body: return 129 | if not body.has_method("heal"): return 130 | 131 | body.heal(heal_amount) 132 | 133 | 134 | 135 | func On_Heal_Timer_Timeout() -> void: 136 | cast() 137 | 138 | 139 | func _on_AnimationPlayer_animation_finished(anim_name: String) -> void: 140 | if anim_name == "heal": 141 | _anim_player.play("idle") 142 | -------------------------------------------------------------------------------- /demo/src/healing-aura/HealingAura.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://cb3kfowy2hsl3"] 2 | 3 | [ext_resource type="Script" path="res://demo/src/healing-aura/HealingAura.gd" id="1"] 4 | [ext_resource type="PackedScene" path="res://demo/src/circle-cast/CircleCast.tscn" id="2"] 5 | 6 | [sub_resource type="Animation" id="1"] 7 | resource_name = "heal" 8 | length = 0.3 9 | step = 0.01 10 | tracks/0/type = "value" 11 | tracks/0/imported = false 12 | tracks/0/enabled = true 13 | tracks/0/path = NodePath("Polygon2D:scale") 14 | tracks/0/interp = 1 15 | tracks/0/loop_wrap = true 16 | tracks/0/keys = { 17 | "times": PackedFloat32Array(0.16, 0.25, 0.3), 18 | "transitions": PackedFloat32Array(1, 1, 1), 19 | "update": 0, 20 | "values": [Vector2(0.8, 0.8), Vector2(1.1, 1.1), Vector2(1, 1)] 21 | } 22 | tracks/1/type = "value" 23 | tracks/1/imported = false 24 | tracks/1/enabled = true 25 | tracks/1/path = NodePath("Polygon2D:color") 26 | tracks/1/interp = 1 27 | tracks/1/loop_wrap = true 28 | tracks/1/keys = { 29 | "times": PackedFloat32Array(0, 0.3), 30 | "transitions": PackedFloat32Array(1, 1), 31 | "update": 0, 32 | "values": [Color(1, 1, 1, 0.196078), Color(1, 1, 1, 0.196078)] 33 | } 34 | 35 | [sub_resource type="Animation" id="2"] 36 | resource_name = "idle" 37 | loop_mode = 1 38 | step = 0.01 39 | tracks/0/type = "value" 40 | tracks/0/imported = false 41 | tracks/0/enabled = true 42 | tracks/0/path = NodePath("Polygon2D:color") 43 | tracks/0/interp = 1 44 | tracks/0/loop_wrap = true 45 | tracks/0/keys = { 46 | "times": PackedFloat32Array(0, 0.5, 0.98), 47 | "transitions": PackedFloat32Array(1, 1, 1), 48 | "update": 0, 49 | "values": [Color(1, 1, 1, 0.196078), Color(1, 1, 1, 0.0980392), Color(1, 1, 1, 0.196078)] 50 | } 51 | tracks/1/type = "value" 52 | tracks/1/imported = false 53 | tracks/1/enabled = true 54 | tracks/1/path = NodePath("Polygon2D:scale") 55 | tracks/1/interp = 1 56 | tracks/1/loop_wrap = true 57 | tracks/1/keys = { 58 | "times": PackedFloat32Array(0, 1), 59 | "transitions": PackedFloat32Array(1, 1), 60 | "update": 0, 61 | "values": [Vector2(1, 1), Vector2(1, 1)] 62 | } 63 | 64 | [sub_resource type="AnimationLibrary" id="AnimationLibrary_vgnvi"] 65 | _data = { 66 | "heal": SubResource("1"), 67 | "idle": SubResource("2") 68 | } 69 | 70 | [node name="HealingAura" type="Node2D"] 71 | z_index = -5 72 | script = ExtResource("1") 73 | 74 | [node name="Polygon2D" type="Polygon2D" parent="."] 75 | color = Color(1, 1, 1, 0.196078) 76 | 77 | [node name="CircleCast" parent="." instance=ExtResource("2")] 78 | max_results = 32 79 | 80 | [node name="AnimationPlayer" type="AnimationPlayer" parent="."] 81 | libraries = { 82 | "": SubResource("AnimationLibrary_vgnvi") 83 | } 84 | 85 | [connection signal="animation_finished" from="AnimationPlayer" to="." method="_on_AnimationPlayer_animation_finished"] 86 | -------------------------------------------------------------------------------- /demo/src/point-fracture-ball/PointFractureBall.gd: -------------------------------------------------------------------------------- 1 | extends RigidBody2D 2 | 3 | 4 | 5 | # MIT License 6 | # ----------------------------------------------------------------------- 7 | # This file is part of: 8 | # GODOT Polygon 2D Fracture 9 | # https://github.com/SoloByte/godot-polygon2d-fracture 10 | # ----------------------------------------------------------------------- 11 | # Copyright (c) 2021 David Grueneis 12 | # 13 | # Permission is hereby granted, free of charge, to any person obtaining a copy 14 | # of this software and associated documentation files (the "Software"), to deal 15 | # in the Software without restriction, including without limitation the rights 16 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | # copies of the Software, and to permit persons to whom the Software is 18 | # furnished to do so, subject to the following conditions: 19 | # 20 | # The above copyright notice and this permission notice shall be included in all 21 | # copies or substantial portions of the Software. 22 | # 23 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | # SOFTWARE. 30 | 31 | 32 | 33 | 34 | signal Despawn(ref) 35 | 36 | 37 | 38 | 39 | @export var radius: float = 50.0 40 | 41 | 42 | 43 | 44 | @onready var _poly := $Polygon2D 45 | @onready var _col_poly := $CollisionPolygon2D 46 | @onready var _timer := $Timer 47 | 48 | 49 | 50 | 51 | var point_fracture = null 52 | var launch_velocity : float = 0.0 53 | 54 | 55 | 56 | 57 | func _ready() -> void: 58 | setPolygon(PolygonLib.createCirclePolygon(radius, 1)) 59 | 60 | 61 | func _integrate_forces(state: PhysicsDirectBodyState2D) -> void: 62 | if state.get_contact_count() > 0: 63 | var body = state.get_contact_collider_object(0) 64 | if body is RigidBody2D: 65 | var pos : Vector2 = state.get_contact_collider_position(0) 66 | point_fracture.fractureCollision(pos, body, self) 67 | 68 | 69 | func spawn(pos : Vector2, launch_vector : Vector2, lifetime : float, point_fracture) -> void: 70 | launch_velocity = launch_vector.length() 71 | self.point_fracture = point_fracture 72 | global_position = pos 73 | _timer.start(lifetime) 74 | 75 | linear_velocity = launch_vector 76 | 77 | 78 | func despawn() -> void: 79 | global_rotation = 0.0 80 | linear_velocity = Vector2.ZERO 81 | angular_velocity = 0.0 82 | apply_force(Vector2.ZERO) 83 | 84 | 85 | func destroy() -> void: 86 | _timer.stop() 87 | emit_signal("Despawn", self) 88 | 89 | 90 | func setPolygon(polygon : PackedVector2Array) -> void: 91 | _poly.set_polygon(polygon) 92 | _col_poly.set_polygon(polygon) 93 | 94 | 95 | func _on_Timer_timeout() -> void: 96 | point_fracture.fractureBallDespawned(global_position, _poly.get_polygon()) 97 | destroy() 98 | -------------------------------------------------------------------------------- /demo/src/point-fracture-ball/PointFractureBall.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://demo/src/point-fracture-ball/PointFractureBall.gd" type="Script" id=1] 4 | 5 | [sub_resource type="PhysicsMaterial" id=1] 6 | friction = 0.0 7 | bounce = 1.0 8 | 9 | [node name="PointFractureBall" type="RigidBody2D"] 10 | modulate = Color( 1.66, 0.56, 0, 1 ) 11 | collision_layer = 8 12 | collision_mask = 2147483653 13 | mass = 2.0 14 | physics_material_override = SubResource( 1 ) 15 | gravity_scale = 10.0 16 | max_contacts_reported = 1 17 | contact_monitor = true 18 | can_sleep = false 19 | script = ExtResource( 1 ) 20 | radius = 25.0 21 | 22 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="."] 23 | 24 | [node name="Polygon2D" type="Polygon2D" parent="."] 25 | 26 | [node name="Timer" type="Timer" parent="."] 27 | one_shot = true 28 | [connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"] 29 | -------------------------------------------------------------------------------- /demo/test-scenes/blob-scenes/BlobSceneBasic.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://cphwtf8sbqbit"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://c5k3aj7yblg0f" path="res://demo/test-scenes/test-scene-basic/TestSceneBasic.tscn" id="1"] 4 | [ext_resource type="PackedScene" path="res://demo/src/FractureShard.tscn" id="2"] 5 | [ext_resource type="PackedScene" path="res://demo/src/CutShapeVisualizer.tscn" id="3"] 6 | [ext_resource type="PackedScene" uid="uid://dhrqqo38wiuqi" path="res://pool-manager/Pool2DBasic.tscn" id="4"] 7 | [ext_resource type="Script" path="res://demo/src/BlobFracture.gd" id="5"] 8 | 9 | [sub_resource type="PhysicsMaterial" id="1"] 10 | friction = 0.0 11 | bounce = 1.0 12 | 13 | [node name="CutSceneBasic" instance=ExtResource("1")] 14 | 15 | [node name="BlobFracture" type="Node2D" parent="." index="0"] 16 | script = ExtResource("5") 17 | 18 | [node name="BlobParent" type="Node2D" parent="BlobFracture" index="0"] 19 | 20 | [node name="Edge" type="StaticBody2D" parent="BlobFracture" index="1"] 21 | modulate = Color(0.537255, 0.266667, 0.266667, 1) 22 | position = Vector2(0, 933.82) 23 | physics_material_override = SubResource("1") 24 | 25 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="BlobFracture/Edge" index="0"] 26 | polygon = PackedVector2Array(-1888.94, -360.544, -1652.27, -611.544, -1891.09, -1005.08, -1888, -1893.82, 1888, -1893.82, 1903.04, -70.0054, 1492.68, 114.992, 320, -325.82, -256, -325.82, -1719.55, 114.992, -1888, -69.82, -1888.94, -357.922, -2024.51, -319.195, -2069.36, 219.263, 2037.58, 313.443, 2081.48, -2057.26, -1996.06, -2091.24, -2025.33, -322.011) 27 | 28 | [node name="Polygon2D" type="Polygon2D" parent="BlobFracture/Edge" index="1"] 29 | color = Color(0.603922, 0.603922, 0.603922, 1) 30 | polygon = PackedVector2Array(-1888, -69.82, -1715.91, 115.684, -256, -325.82, 320, -325.82, 1489.84, 111.195, 1902.91, -72.8892, 1888, -1893.82, -1888, -1893.82, -1891.02, -1006.78, -1644.07, -611.671, -2025.71, -225.544, -1998.77, -2097.81, 2082.5, -2052.91, 2037.6, 322.217, -2066.12, 223.441, -2084.08, -369.219, -1888, -453.82) 31 | 32 | [node name="Line2D" type="Line2D" parent="BlobFracture/Edge/Polygon2D" index="0"] 33 | points = PackedVector2Array(-1649.77, -613.744, -1891.67, -1001.8, -1888, -1893.82, 1888, -1893.82, 1900.69, -74.4978, 1495, 109.451, 320, -325.82, -256, -325.82, -1717.37, 115.107, -1888, -69.82, -1888, -357.82, -1649.31, -613.507) 34 | width = 15.0 35 | joint_mode = 2 36 | begin_cap_mode = 2 37 | end_cap_mode = 2 38 | antialiased = true 39 | 40 | [node name="Pool_CutVisualizer" parent="BlobFracture" index="2" instance=ExtResource("4")] 41 | placed_in_level = true 42 | instance_template = ExtResource("3") 43 | max_amount = 30 44 | instantiate_new_on_empty = true 45 | keep_instances_in_tree = true 46 | 47 | [node name="Pool_FractureShards" parent="BlobFracture" index="3" instance=ExtResource("4")] 48 | placed_in_level = true 49 | instance_template = ExtResource("2") 50 | max_amount = 250 51 | keep_instances_in_tree = true 52 | 53 | [node name="Camera2D" parent="." index="1"] 54 | zoom = Vector2(0.5, 0.5) 55 | 56 | [node name="Title" parent="CanvasLayer" index="0"] 57 | text = "POLYGON RESTORING" 58 | 59 | [node name="InfoLabel" parent="CanvasLayer" index="1"] 60 | text = "CHANGE TEST [W] 61 | RALLY POINT [LMB] 62 | CLEAR [RMB] 63 | FULLSCREEN [F] 64 | QUIT [ESC]" 65 | -------------------------------------------------------------------------------- /demo/test-scenes/blob-scenes/usable/BlobRestoreAdvScene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://pdw1oj7ma01k"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://cphwtf8sbqbit" path="res://demo/test-scenes/blob-scenes/BlobSceneBasic.tscn" id="1"] 4 | [ext_resource type="PackedScene" path="res://demo/src/blob/Blob.tscn" id="2"] 5 | 6 | [node name="BlobRestoreAdvScene" instance=ExtResource("1")] 7 | 8 | [node name="Blob" parent="BlobFracture/BlobParent" index="0" instance=ExtResource("2")] 9 | advanced_regeneration = true 10 | invincible_time = 0.2 11 | color_default = Color(0.47451, 0.576471, 0.721569, 1) 12 | radius = 150.0 13 | shape_area_percent = 0.2 14 | fractures = 3 15 | fracture_force = 300.0 16 | collision_damage = Vector2(40, 150) 17 | collision_knockback_force = 150000.0 18 | collision_knockback_time = 3.0 19 | target_pos_interval_range = Vector2(2, 4) 20 | keep_distance_range = Vector2(0, 250) 21 | max_speed = 750.0 22 | accel = 1500.0 23 | decel = 200.0 24 | regeneration_interval_range = Vector2(2, 3) 25 | regeneration_amount = 25.0 26 | 27 | [node name="Blob2" parent="BlobFracture/BlobParent" index="1" instance=ExtResource("2")] 28 | position = Vector2(-1579.27, -808.661) 29 | advanced_regeneration = true 30 | invincible_time = 0.2 31 | color_default = Color(0.568627, 0.47451, 0.721569, 1) 32 | radius = 150.0 33 | shape_area_percent = 0.2 34 | fractures = 3 35 | fracture_force = 300.0 36 | collision_damage = Vector2(40, 150) 37 | collision_knockback_force = 150000.0 38 | collision_knockback_time = 3.0 39 | target_pos_interval_range = Vector2(2, 4) 40 | keep_distance_range = Vector2(0, 250) 41 | max_speed = 500.0 42 | accel = 1500.0 43 | decel = 200.0 44 | regeneration_interval_range = Vector2(2, 3) 45 | regeneration_amount = 25.0 46 | 47 | [node name="Blob3" parent="BlobFracture/BlobParent" index="2" instance=ExtResource("2")] 48 | position = Vector2(-1617.32, 718.281) 49 | advanced_regeneration = true 50 | invincible_time = 0.2 51 | color_default = Color(0.721569, 0.47451, 0.682353, 1) 52 | radius = 150.0 53 | shape_area_percent = 0.2 54 | fractures = 3 55 | fracture_force = 300.0 56 | collision_damage = Vector2(40, 150) 57 | collision_knockback_force = 150000.0 58 | collision_knockback_time = 3.0 59 | target_pos_interval_range = Vector2(2, 4) 60 | keep_distance_range = Vector2(0, 250) 61 | max_speed = 500.0 62 | accel = 1500.0 63 | decel = 200.0 64 | regeneration_interval_range = Vector2(2, 3) 65 | regeneration_amount = 25.0 66 | 67 | [node name="Blob4" parent="BlobFracture/BlobParent" index="3" instance=ExtResource("2")] 68 | position = Vector2(1446.08, 684.983) 69 | advanced_regeneration = true 70 | invincible_time = 0.2 71 | color_default = Color(0.721569, 0.47451, 0.47451, 1) 72 | radius = 150.0 73 | shape_area_percent = 0.2 74 | fractures = 3 75 | fracture_force = 300.0 76 | collision_damage = Vector2(40, 150) 77 | collision_knockback_force = 150000.0 78 | collision_knockback_time = 3.0 79 | target_pos_interval_range = Vector2(2, 4) 80 | keep_distance_range = Vector2(0, 250) 81 | max_speed = 500.0 82 | accel = 1500.0 83 | decel = 200.0 84 | regeneration_interval_range = Vector2(2, 3) 85 | regeneration_amount = 25.0 86 | 87 | [node name="Blob5" parent="BlobFracture/BlobParent" index="4" instance=ExtResource("2")] 88 | position = Vector2(1569.75, -675.47) 89 | advanced_regeneration = true 90 | invincible_time = 0.2 91 | color_default = Color(0.721569, 0.592157, 0.47451, 1) 92 | radius = 150.0 93 | shape_area_percent = 0.2 94 | fractures = 3 95 | fracture_force = 300.0 96 | collision_damage = Vector2(40, 150) 97 | collision_knockback_force = 150000.0 98 | collision_knockback_time = 3.0 99 | target_pos_interval_range = Vector2(2, 4) 100 | keep_distance_range = Vector2(0, 250) 101 | max_speed = 500.0 102 | accel = 1500.0 103 | decel = 200.0 104 | regeneration_interval_range = Vector2(2, 3) 105 | regeneration_amount = 25.0 106 | 107 | [node name="Blob6" parent="BlobFracture/BlobParent" index="5" instance=ExtResource("2")] 108 | position = Vector2(-1232.02, -14.2706) 109 | advanced_regeneration = true 110 | invincible_time = 0.2 111 | color_default = Color(0.643137, 0.721569, 0.47451, 1) 112 | radius = 150.0 113 | shape_area_percent = 0.2 114 | fractures = 3 115 | fracture_force = 300.0 116 | collision_damage = Vector2(40, 150) 117 | collision_knockback_force = 150000.0 118 | collision_knockback_time = 3.0 119 | target_pos_interval_range = Vector2(2, 4) 120 | keep_distance_range = Vector2(0, 250) 121 | max_speed = 500.0 122 | accel = 1500.0 123 | decel = 200.0 124 | regeneration_interval_range = Vector2(2, 3) 125 | regeneration_amount = 25.0 126 | 127 | [node name="Blob7" parent="BlobFracture/BlobParent" index="6" instance=ExtResource("2")] 128 | position = Vector2(-256.868, -642.172) 129 | advanced_regeneration = true 130 | invincible_time = 0.2 131 | color_default = Color(0.47451, 0.721569, 0.501961, 1) 132 | radius = 150.0 133 | shape_area_percent = 0.2 134 | fractures = 3 135 | fracture_force = 300.0 136 | collision_damage = Vector2(40, 150) 137 | collision_knockback_force = 150000.0 138 | collision_knockback_time = 3.0 139 | target_pos_interval_range = Vector2(2, 4) 140 | keep_distance_range = Vector2(0, 250) 141 | max_speed = 500.0 142 | accel = 1500.0 143 | decel = 200.0 144 | regeneration_interval_range = Vector2(2, 3) 145 | regeneration_amount = 25.0 146 | 147 | [node name="Blob8" parent="BlobFracture/BlobParent" index="7" instance=ExtResource("2")] 148 | position = Vector2(960.879, 109.407) 149 | advanced_regeneration = true 150 | invincible_time = 0.2 151 | color_default = Color(0.47451, 0.721569, 0.662745, 1) 152 | radius = 150.0 153 | shape_area_percent = 0.2 154 | fractures = 3 155 | fracture_force = 300.0 156 | collision_damage = Vector2(40, 150) 157 | collision_knockback_force = 150000.0 158 | collision_knockback_time = 3.0 159 | target_pos_interval_range = Vector2(2, 4) 160 | keep_distance_range = Vector2(0, 250) 161 | max_speed = 500.0 162 | accel = 1500.0 163 | decel = 200.0 164 | regeneration_interval_range = Vector2(2, 3) 165 | regeneration_amount = 25.0 166 | 167 | [node name="Blob9" parent="BlobFracture/BlobParent" index="8" instance=ExtResource("2")] 168 | position = Vector2(-451.898, 390.06) 169 | advanced_regeneration = true 170 | invincible_time = 0.2 171 | color_default = Color(0.247059, 0.247059, 0.247059, 1) 172 | radius = 150.0 173 | shape_area_percent = 0.2 174 | fractures = 3 175 | fracture_force = 300.0 176 | collision_damage = Vector2(40, 150) 177 | collision_knockback_force = 150000.0 178 | collision_knockback_time = 3.0 179 | target_pos_interval_range = Vector2(2, 4) 180 | keep_distance_range = Vector2(0, 250) 181 | max_speed = 500.0 182 | accel = 1500.0 183 | decel = 200.0 184 | regeneration_interval_range = Vector2(2, 3) 185 | regeneration_amount = 25.0 186 | 187 | [node name="Title" parent="CanvasLayer/ColorRect" index="0"] 188 | text = "DELAUNY FRACTURE" 189 | -------------------------------------------------------------------------------- /demo/test-scenes/blob-scenes/usable/BlobRestoreSimpleScene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://c1frwj2m6e4hx"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://cphwtf8sbqbit" path="res://demo/test-scenes/blob-scenes/BlobSceneBasic.tscn" id="1"] 4 | [ext_resource type="PackedScene" path="res://demo/src/blob/Blob.tscn" id="2"] 5 | 6 | [node name="BlobRestoreSimpleScene" instance=ExtResource("1")] 7 | 8 | [node name="Blob" parent="BlobFracture/BlobParent" index="0" instance=ExtResource("2")] 9 | position = Vector2(565.686, -475.176) 10 | invincible_time = 0.2 11 | color_default = Color(0.47451, 0.576471, 0.721569, 1) 12 | radius = 150.0 13 | shape_area_percent = 0.2 14 | fractures = 3 15 | fracture_force = 300.0 16 | collision_damage = Vector2(40, 150) 17 | collision_knockback_force = 150000.0 18 | collision_knockback_time = 3.0 19 | target_pos_interval_range = Vector2(2, 4) 20 | keep_distance_range = Vector2(0, 250) 21 | max_speed = 750.0 22 | accel = 1500.0 23 | decel = 200.0 24 | regeneration_interval_range = Vector2(1, 1.5) 25 | regeneration_amount = 25.0 26 | 27 | [node name="Blob2" parent="BlobFracture/BlobParent" index="1" instance=ExtResource("2")] 28 | position = Vector2(-821.251, -327.828) 29 | invincible_time = 0.2 30 | color_default = Color(0.568627, 0.47451, 0.721569, 1) 31 | radius = 150.0 32 | shape_area_percent = 0.2 33 | fractures = 3 34 | fracture_force = 300.0 35 | collision_damage = Vector2(40, 150) 36 | collision_knockback_force = 150000.0 37 | collision_knockback_time = 3.0 38 | target_pos_interval_range = Vector2(2, 4) 39 | keep_distance_range = Vector2(0, 250) 40 | max_speed = 500.0 41 | accel = 1500.0 42 | decel = 200.0 43 | regeneration_interval_range = Vector2(1, 1.5) 44 | regeneration_amount = 25.0 45 | 46 | [node name="Blob3" parent="BlobFracture/BlobParent" index="2" instance=ExtResource("2")] 47 | position = Vector2(-1702.17, -379.149) 48 | invincible_time = 0.2 49 | color_default = Color(0.721569, 0.47451, 0.682353, 1) 50 | radius = 150.0 51 | shape_area_percent = 0.2 52 | fractures = 3 53 | fracture_force = 300.0 54 | collision_damage = Vector2(40, 150) 55 | collision_knockback_force = 150000.0 56 | collision_knockback_time = 3.0 57 | target_pos_interval_range = Vector2(2, 4) 58 | keep_distance_range = Vector2(0, 250) 59 | max_speed = 500.0 60 | accel = 1500.0 61 | decel = 200.0 62 | regeneration_interval_range = Vector2(1, 1.5) 63 | regeneration_amount = 25.0 64 | 65 | [node name="Blob4" parent="BlobFracture/BlobParent" index="3" instance=ExtResource("2")] 66 | position = Vector2(-1291.84, 481.336) 67 | invincible_time = 0.2 68 | color_default = Color(0.721569, 0.47451, 0.47451, 1) 69 | radius = 150.0 70 | shape_area_percent = 0.2 71 | fractures = 3 72 | fracture_force = 300.0 73 | collision_damage = Vector2(40, 150) 74 | collision_knockback_force = 150000.0 75 | collision_knockback_time = 3.0 76 | target_pos_interval_range = Vector2(2, 4) 77 | keep_distance_range = Vector2(0, 250) 78 | max_speed = 500.0 79 | accel = 1500.0 80 | decel = 200.0 81 | regeneration_interval_range = Vector2(1, 1.5) 82 | regeneration_amount = 25.0 83 | 84 | [node name="Blob5" parent="BlobFracture/BlobParent" index="4" instance=ExtResource("2")] 85 | position = Vector2(1513.18, 580.352) 86 | invincible_time = 0.2 87 | color_default = Color(0.721569, 0.592157, 0.47451, 1) 88 | radius = 150.0 89 | shape_area_percent = 0.2 90 | fractures = 3 91 | fracture_force = 300.0 92 | collision_damage = Vector2(40, 150) 93 | collision_knockback_force = 150000.0 94 | collision_knockback_time = 3.0 95 | target_pos_interval_range = Vector2(2, 4) 96 | keep_distance_range = Vector2(0, 250) 97 | max_speed = 500.0 98 | accel = 1500.0 99 | decel = 200.0 100 | regeneration_interval_range = Vector2(1, 1.5) 101 | regeneration_amount = 25.0 102 | 103 | [node name="Blob6" parent="BlobFracture/BlobParent" index="5" instance=ExtResource("2")] 104 | position = Vector2(1432.36, -664.809) 105 | invincible_time = 0.2 106 | color_default = Color(0.643137, 0.721569, 0.47451, 1) 107 | radius = 150.0 108 | shape_area_percent = 0.2 109 | fractures = 3 110 | fracture_force = 300.0 111 | collision_damage = Vector2(40, 150) 112 | collision_knockback_force = 150000.0 113 | collision_knockback_time = 3.0 114 | target_pos_interval_range = Vector2(2, 4) 115 | keep_distance_range = Vector2(0, 250) 116 | max_speed = 500.0 117 | accel = 1500.0 118 | decel = 200.0 119 | regeneration_interval_range = Vector2(1, 1.5) 120 | regeneration_amount = 25.0 121 | 122 | [node name="Blob7" parent="BlobFracture/BlobParent" index="6" instance=ExtResource("2")] 123 | position = Vector2(-228.584, -331.045) 124 | invincible_time = 0.2 125 | color_default = Color(0.47451, 0.721569, 0.501961, 1) 126 | radius = 150.0 127 | shape_area_percent = 0.2 128 | fractures = 3 129 | fracture_force = 300.0 130 | collision_damage = Vector2(40, 150) 131 | collision_knockback_force = 150000.0 132 | collision_knockback_time = 3.0 133 | target_pos_interval_range = Vector2(2, 4) 134 | keep_distance_range = Vector2(0, 250) 135 | max_speed = 500.0 136 | accel = 1500.0 137 | decel = 200.0 138 | regeneration_interval_range = Vector2(1, 1.5) 139 | regeneration_amount = 25.0 140 | 141 | [node name="Blob8" parent="BlobFracture/BlobParent" index="7" instance=ExtResource("2")] 142 | position = Vector2(157.606, 403.563) 143 | invincible_time = 0.2 144 | color_default = Color(0.47451, 0.721569, 0.662745, 1) 145 | radius = 150.0 146 | shape_area_percent = 0.2 147 | fractures = 3 148 | fracture_force = 300.0 149 | collision_damage = Vector2(40, 150) 150 | collision_knockback_force = 150000.0 151 | collision_knockback_time = 3.0 152 | target_pos_interval_range = Vector2(2, 4) 153 | keep_distance_range = Vector2(0, 250) 154 | max_speed = 500.0 155 | accel = 1500.0 156 | decel = 200.0 157 | regeneration_interval_range = Vector2(1, 1.5) 158 | regeneration_amount = 25.0 159 | 160 | [node name="Blob9" parent="BlobFracture/BlobParent" index="8" instance=ExtResource("2")] 161 | position = Vector2(1273.44, -73.8019) 162 | invincible_time = 0.2 163 | color_default = Color(0.247059, 0.247059, 0.247059, 1) 164 | radius = 150.0 165 | shape_area_percent = 0.2 166 | fractures = 3 167 | fracture_force = 300.0 168 | collision_damage = Vector2(40, 150) 169 | collision_knockback_force = 150000.0 170 | collision_knockback_time = 3.0 171 | target_pos_interval_range = Vector2(2, 4) 172 | keep_distance_range = Vector2(0, 250) 173 | max_speed = 500.0 174 | accel = 1500.0 175 | decel = 200.0 176 | regeneration_interval_range = Vector2(1, 1.5) 177 | regeneration_amount = 25.0 178 | 179 | [node name="Title" parent="CanvasLayer/ColorRect" index="0"] 180 | text = "DELAUNY FRACTURE" 181 | -------------------------------------------------------------------------------- /demo/test-scenes/cut-scenes/CutSceneBasic.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://ku50560na3tw"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://c5k3aj7yblg0f" path="res://demo/test-scenes/test-scene-basic/TestSceneBasic.tscn" id="1"] 4 | 5 | [node name="CutSceneBasic" instance=ExtResource("1")] 6 | -------------------------------------------------------------------------------- /demo/test-scenes/cut-scenes/usable/CutFracture.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=12 format=3 uid="uid://vdocs38e3ce0"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://ku50560na3tw" path="res://demo/test-scenes/cut-scenes/CutSceneBasic.tscn" id="1"] 4 | [ext_resource type="PackedScene" path="res://demo/src/RigidBody2D.tscn" id="2"] 5 | [ext_resource type="PackedScene" path="res://demo/src/FractureShard.tscn" id="3"] 6 | [ext_resource type="Script" path="res://demo/src/CutFracture.gd" id="4"] 7 | [ext_resource type="PackedScene" path="res://demo/src/CutShapeVisualizer.tscn" id="5"] 8 | [ext_resource type="PackedScene" uid="uid://dhrqqo38wiuqi" path="res://pool-manager/Pool2DBasic.tscn" id="6"] 9 | [ext_resource type="Texture2D" uid="uid://bsrwyn1s0ui61" path="res://demo/smiley-face.png" id="7"] 10 | [ext_resource type="Texture2D" uid="uid://3igvussru6ef" path="res://demo/tile-test-01.png" id="8"] 11 | [ext_resource type="Script" path="res://demo/src/RigidBody2D.gd" id="9"] 12 | 13 | [sub_resource type="PhysicsMaterial" id="1"] 14 | friction = 0.0 15 | bounce = 1.0 16 | 17 | [sub_resource type="PhysicsMaterial" id="2"] 18 | friction = 0.0 19 | bounce = 1.0 20 | 21 | [node name="CutFracture" instance=ExtResource("1")] 22 | 23 | [node name="CutFracture" type="Node2D" parent="." index="0"] 24 | script = ExtResource("4") 25 | fracture_body_color = Color(0.917647, 0.313726, 0.313726, 1) 26 | rigidbody_template = ExtResource("2") 27 | 28 | [node name="SourcePolygons" type="Node2D" parent="CutFracture" index="0"] 29 | show_behind_parent = true 30 | 31 | [node name="RigidBody2D" type="RigidBody2D" parent="CutFracture/SourcePolygons" index="0"] 32 | position = Vector2(1139.98, -682.293) 33 | collision_layer = 4 34 | collision_mask = 5 35 | mass = 100.0 36 | physics_material_override = SubResource("1") 37 | gravity_scale = 0.0 38 | script = ExtResource("9") 39 | rand_linear_velocity_range = Vector2(500, 1000) 40 | placed_in_level = true 41 | randomize_texture_properties = false 42 | poly_texture = ExtResource("7") 43 | 44 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="CutFracture/SourcePolygons/RigidBody2D" index="0"] 45 | 46 | [node name="Polygon2D" type="Polygon2D" parent="CutFracture/SourcePolygons/RigidBody2D" index="1"] 47 | show_behind_parent = true 48 | color = Color(0.545098, 0.545098, 0.545098, 1) 49 | texture_offset = Vector2(256, 256) 50 | 51 | [node name="Line2D" type="Line2D" parent="CutFracture/SourcePolygons/RigidBody2D/Polygon2D" index="0"] 52 | default_color = Color(1.25, 1.25, 1.25, 1) 53 | joint_mode = 2 54 | begin_cap_mode = 2 55 | end_cap_mode = 2 56 | antialiased = true 57 | 58 | [node name="Center" type="Polygon2D" parent="CutFracture/SourcePolygons/RigidBody2D" index="2"] 59 | visible = false 60 | color = Color(0, 0, 0, 1) 61 | polygon = PackedVector2Array(-10.3909, 0.536697, -0.314636, -12.5448, 10.6455, 0.183144, 0.0389175, 12.0272) 62 | 63 | [node name="RigidBody2D2" parent="CutFracture/SourcePolygons" index="1" instance=ExtResource("2")] 64 | position = Vector2(1241.69, 542.444) 65 | mass = 40.0 66 | physics_material_override = SubResource("1") 67 | rand_linear_velocity_range = Vector2(500, 1000) 68 | radius = 100.0 69 | placed_in_level = true 70 | 71 | [node name="RigidBody2D3" parent="CutFracture/SourcePolygons" index="2" instance=ExtResource("2")] 72 | position = Vector2(-1271.35, 589.061) 73 | mass = 120.0 74 | physics_material_override = SubResource("1") 75 | rand_linear_velocity_range = Vector2(500, 1000) 76 | radius = 300.0 77 | placed_in_level = true 78 | poly_texture = ExtResource("8") 79 | 80 | [node name="RigidBody2D4" parent="CutFracture/SourcePolygons" index="3" instance=ExtResource("2")] 81 | position = Vector2(-1322.21, -597.536) 82 | mass = 20.0 83 | physics_material_override = SubResource("1") 84 | rand_linear_velocity_range = Vector2(500, 1000) 85 | radius = 50.0 86 | placed_in_level = true 87 | 88 | [node name="RigidBody2D5" parent="CutFracture/SourcePolygons" index="4" instance=ExtResource("2")] 89 | position = Vector2(-546.682, 76.2813) 90 | mass = 50.0 91 | physics_material_override = SubResource("1") 92 | rand_linear_velocity_range = Vector2(500, 1000) 93 | radius = 125.0 94 | placed_in_level = true 95 | poly_texture = ExtResource("8") 96 | 97 | [node name="RigidBody2D6" parent="CutFracture/SourcePolygons" index="5" instance=ExtResource("2")] 98 | position = Vector2(402.595, -606.012) 99 | mass = 30.0 100 | physics_material_override = SubResource("1") 101 | rand_linear_velocity_range = Vector2(500, 1000) 102 | radius = 75.0 103 | placed_in_level = true 104 | 105 | [node name="RigidBody2D7" parent="CutFracture/SourcePolygons" index="6" instance=ExtResource("2")] 106 | position = Vector2(317.839, 385.644) 107 | mass = 80.0 108 | physics_material_override = SubResource("1") 109 | rand_linear_velocity_range = Vector2(500, 1000) 110 | radius = 200.0 111 | placed_in_level = true 112 | poly_texture = ExtResource("8") 113 | 114 | [node name="Parent" type="Node2D" parent="CutFracture" index="1"] 115 | 116 | [node name="Edge" type="StaticBody2D" parent="CutFracture" index="2"] 117 | modulate = Color(0.537255, 0.266667, 0.266667, 1) 118 | position = Vector2(0, 933.82) 119 | physics_material_override = SubResource("2") 120 | 121 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="CutFracture/Edge" index="0"] 122 | polygon = PackedVector2Array(-1888.94, -360.544, -1652.27, -611.544, -1891.09, -1005.08, -1888, -1893.82, 1888, -1893.82, 1903.04, -70.0054, 1492.68, 114.992, 320, -325.82, -256, -325.82, -1719.55, 114.992, -1888, -69.82, -1888.94, -357.922, -2024.51, -319.195, -2069.36, 219.263, 2037.58, 313.443, 2081.48, -2057.26, -1996.06, -2091.24, -2025.33, -322.011) 123 | 124 | [node name="Polygon2D" type="Polygon2D" parent="CutFracture/Edge" index="1"] 125 | color = Color(0.603922, 0.603922, 0.603922, 1) 126 | polygon = PackedVector2Array(-1888, -69.82, -1715.91, 115.684, -256, -325.82, 320, -325.82, 1489.84, 111.195, 1902.91, -72.8892, 1888, -1893.82, -1888, -1893.82, -1891.02, -1006.78, -1644.07, -611.671, -2025.71, -225.544, -1998.77, -2097.81, 2082.5, -2052.91, 2037.6, 322.217, -2066.12, 223.441, -2084.08, -369.219, -1888, -453.82) 127 | 128 | [node name="Line2D" type="Line2D" parent="CutFracture/Edge/Polygon2D" index="0"] 129 | points = PackedVector2Array(-1649.77, -613.744, -1891.67, -1001.8, -1888, -1893.82, 1888, -1893.82, 1900.69, -74.4978, 1495, 109.451, 320, -325.82, -256, -325.82, -1717.37, 115.107, -1888, -69.82, -1888, -357.82, -1649.31, -613.507) 130 | width = 15.0 131 | joint_mode = 2 132 | begin_cap_mode = 2 133 | end_cap_mode = 2 134 | antialiased = true 135 | 136 | [node name="CutLine" type="Line2D" parent="CutFracture" index="3"] 137 | default_color = Color(2, 0.57, 0.57, 1) 138 | joint_mode = 2 139 | begin_cap_mode = 2 140 | end_cap_mode = 2 141 | 142 | [node name="Pool_CutVisualizer" parent="CutFracture" index="4" instance=ExtResource("6")] 143 | placed_in_level = true 144 | instance_template = ExtResource("5") 145 | max_amount = 30 146 | instantiate_new_on_empty = true 147 | keep_instances_in_tree = true 148 | 149 | [node name="Pool_FractureShards" parent="CutFracture" index="5" instance=ExtResource("6")] 150 | placed_in_level = true 151 | instance_template = ExtResource("3") 152 | max_amount = 250 153 | keep_instances_in_tree = true 154 | 155 | [node name="Title" parent="CanvasLayer/ColorRect" index="0"] 156 | texture_filter = 0 157 | 158 | [node name="InfoLabel" parent="CanvasLayer/ColorRect" index="1"] 159 | texture_filter = 0 160 | -------------------------------------------------------------------------------- /demo/test-scenes/fracture-scenes/FractureSceneBasic.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=8 format=3 uid="uid://bfk6v7unlow4m"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://c5k3aj7yblg0f" path="res://demo/test-scenes/test-scene-basic/TestSceneBasic.tscn" id="1"] 4 | [ext_resource type="PackedScene" path="res://demo/src/FractureBody.tscn" id="2"] 5 | [ext_resource type="PackedScene" uid="uid://nhnl6rlnpoqk" path="res://demo/ui/FracturesSlider.tscn" id="3"] 6 | [ext_resource type="PackedScene" uid="uid://dkj004hbc30ml" path="res://demo/ui/MinAreaSlider.tscn" id="4"] 7 | [ext_resource type="Script" path="res://demo/src/Fracture.gd" id="5"] 8 | [ext_resource type="PackedScene" uid="uid://dhrqqo38wiuqi" path="res://pool-manager/Pool2DBasic.tscn" id="6"] 9 | 10 | [sub_resource type="PhysicsMaterial" id="1"] 11 | friction = 0.25 12 | bounce = 1.0 13 | absorbent = true 14 | 15 | [node name="FractureSceneBasic" instance=ExtResource("1")] 16 | 17 | [node name="FractureParent" type="Node2D" parent="." index="0"] 18 | script = ExtResource("5") 19 | fracture_body_color = Color(0.137255, 0.576471, 0.886275, 1) 20 | fracture_body_template = ExtResource("2") 21 | delauny_fracture = true 22 | delauny_type = 1 23 | 24 | [node name="SourcePolygons" type="Node2D" parent="FractureParent" index="0"] 25 | show_behind_parent = true 26 | 27 | [node name="Parent" type="Node2D" parent="FractureParent" index="1"] 28 | 29 | [node name="Timer" type="Timer" parent="FractureParent" index="2"] 30 | wait_time = 3.0 31 | 32 | [node name="VisibleTimer" type="Timer" parent="FractureParent" index="3"] 33 | one_shot = true 34 | 35 | [node name="SlowdownTimer" type="Timer" parent="FractureParent" index="4"] 36 | one_shot = true 37 | 38 | [node name="Floor" type="StaticBody2D" parent="FractureParent" index="5"] 39 | modulate = Color(0.537255, 0.266667, 0.266667, 1) 40 | position = Vector2(0, 933.82) 41 | physics_material_override = SubResource("1") 42 | 43 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="FractureParent/Floor" index="0"] 44 | polygon = PackedVector2Array(-1829.84, 455.127, -15.1694, -546.559, 1313.25, 105.895, 2018.73, -48.1862, 2014.24, 270.593) 45 | 46 | [node name="Polygon2D" type="Polygon2D" parent="FractureParent/Floor" index="1"] 47 | color = Color(0.603922, 0.603922, 0.603922, 1) 48 | polygon = PackedVector2Array(-1819.15, 451.564, -10.6794, -542.069, 1309.69, 109.458, 2023.22, -48.1862, 2023.22, 270.593) 49 | 50 | [node name="Line2D" type="Line2D" parent="FractureParent/Floor/Polygon2D" index="0"] 51 | points = PackedVector2Array(-1815.59, 451.564, -10.6794, -546.559, 1316.81, 105.895, 2014.24, -43.6963, 2014.24, 252.634, -1815.59, 451.564) 52 | width = 15.0 53 | joint_mode = 2 54 | begin_cap_mode = 2 55 | end_cap_mode = 2 56 | antialiased = true 57 | 58 | [node name="Pool_FractureBodies" parent="FractureParent" index="6" instance=ExtResource("6")] 59 | placed_in_level = true 60 | instance_template = ExtResource("2") 61 | max_amount = 300 62 | instantiate_new_on_empty = true 63 | 64 | [node name="CanvasLayer" type="CanvasLayer" parent="FractureParent" index="7"] 65 | 66 | [node name="FracturesSlider" parent="FractureParent/CanvasLayer" index="0" instance=ExtResource("3")] 67 | 68 | [node name="MinAreaSlider" parent="FractureParent/CanvasLayer" index="1" instance=ExtResource("4")] 69 | 70 | [connection signal="timeout" from="FractureParent/Timer" to="FractureParent" method="_on_Timer_timeout"] 71 | [connection signal="timeout" from="FractureParent/VisibleTimer" to="FractureParent" method="_on_VisibleTimer_timeout"] 72 | [connection signal="timeout" from="FractureParent/SlowdownTimer" to="FractureParent" method="_on_SlowdownTimer_timeout"] 73 | [connection signal="value_changed" from="FractureParent/CanvasLayer/FracturesSlider" to="FractureParent" method="_on_FracturesSlider_value_changed"] 74 | [connection signal="value_changed" from="FractureParent/CanvasLayer/MinAreaSlider" to="FractureParent" method="_on_MinAreaSlider_value_changed"] 75 | -------------------------------------------------------------------------------- /demo/test-scenes/fracture-scenes/usable/Fracture.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://dsamrisjqiixl"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://bfk6v7unlow4m" path="res://demo/test-scenes/fracture-scenes/FractureSceneBasic.tscn" id="1"] 4 | 5 | [node name="Fracture_DelaunayConvex" instance=ExtResource("1")] 6 | 7 | [node name="FractureParent" parent="." index="0"] 8 | delauny_fracture = false 9 | delauny_type = 0 10 | simple_fracture = false 11 | 12 | [node name="FracturePolygon" type="Polygon2D" parent="FractureParent/SourcePolygons" index="0"] 13 | show_behind_parent = true 14 | position = Vector2(-1344, -608) 15 | color = Color(0.545098, 0.545098, 0.545098, 1) 16 | polygon = PackedVector2Array(-256, -256, 256, -256, 256, 256, -256, 256) 17 | 18 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon" index="0"] 19 | points = PackedVector2Array(-256, -256, 256, -256, 256, 256, -256, 256, -256, -256) 20 | default_color = Color(1.2, 1.2, 1.2, 1) 21 | joint_mode = 2 22 | begin_cap_mode = 2 23 | end_cap_mode = 2 24 | antialiased = true 25 | 26 | [node name="FracturePolygon2" type="Polygon2D" parent="FractureParent/SourcePolygons" index="1"] 27 | show_behind_parent = true 28 | position = Vector2(1508.07, -608) 29 | color = Color(0.545098, 0.545098, 0.545098, 1) 30 | polygon = PackedVector2Array(-256, -256, 39.1215, -347.94, 256, -256, 256, 256, 146.756, 469.412, -256, 256, -448.599, 129.69) 31 | 32 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon2" index="0"] 33 | points = PackedVector2Array(-256, -256, 38.0496, -347.456, 256, -256, 256, 256, 141.877, 470.449, -256, 256, -445.066, 129.302, -256, -256) 34 | default_color = Color(1.2, 1.2, 1.2, 1) 35 | joint_mode = 2 36 | begin_cap_mode = 2 37 | end_cap_mode = 2 38 | antialiased = true 39 | 40 | [node name="FracturePolygon3" type="Polygon2D" parent="FractureParent/SourcePolygons" index="2"] 41 | show_behind_parent = true 42 | position = Vector2(0, -8) 43 | color = Color(0.545098, 0.545098, 0.545098, 1) 44 | polygon = PackedVector2Array(-768, -120, -490.611, -589.428, -402.93, -555.486, -289.793, -165.163, 768, -120, 768, 136, 224.981, 69.5962, 323.976, 225.16, 219.324, 244.959, -768, 136) 45 | 46 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon3" index="0"] 47 | points = PackedVector2Array(-768, -120, -490.612, -586.599, -402.93, -555.486, -286.965, -165.163, 768, -120, 768, 136, 230.637, 61.1109, 321.147, 216.674, 224.981, 244.959, -768, 136, -768, -120) 48 | default_color = Color(1.2, 1.2, 1.2, 1) 49 | joint_mode = 2 50 | begin_cap_mode = 2 51 | end_cap_mode = 2 52 | antialiased = true 53 | 54 | [node name="FracturesSlider" parent="FractureParent/CanvasLayer" index="0"] 55 | grow_horizontal = 0 56 | grow_vertical = 2 57 | 58 | [node name="MinAreaSlider" parent="FractureParent/CanvasLayer" index="1"] 59 | grow_vertical = 2 60 | -------------------------------------------------------------------------------- /demo/test-scenes/fracture-scenes/usable/Fracture_Delaunay.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cub2gj0u8g4qv"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://bfk6v7unlow4m" path="res://demo/test-scenes/fracture-scenes/FractureSceneBasic.tscn" id="1"] 4 | 5 | [node name="Fracture_DelaunayConvex" instance=ExtResource("1")] 6 | 7 | [node name="FractureParent" parent="." index="0"] 8 | delauny_type = 0 9 | 10 | [node name="FracturePolygon" type="Polygon2D" parent="FractureParent/SourcePolygons" index="0"] 11 | show_behind_parent = true 12 | position = Vector2(759.046, -369.379) 13 | color = Color(0.545098, 0.545098, 0.545098, 1) 14 | polygon = PackedVector2Array(-387.17, -461.463, -133.81, -420.722, -68.7074, -218.679, 146.805, -90.718, 402.727, -299.496, 499.163, 131.206, 598.035, 504.187, -138.3, 436.839, -261.695, 296.726, -147.28, 156.224, -645.653, -66.0238) 15 | 16 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon" index="0"] 17 | points = PackedVector2Array(-647.49, -67.6194, -390.331, -462.882, -136.346, -420.022, -71.2631, -220.01, 147.798, -91.4304, 403.37, -297.792, 595.445, 497.495, -137.934, 435.587, -260.164, 300.658, -150.633, 157.792, -647.49, -67.6194) 18 | default_color = Color(1.2, 1.2, 1.2, 1) 19 | joint_mode = 2 20 | begin_cap_mode = 2 21 | end_cap_mode = 2 22 | antialiased = true 23 | 24 | [node name="FracturePolygon2" type="Polygon2D" parent="FractureParent/SourcePolygons" index="1"] 25 | show_behind_parent = true 26 | position = Vector2(-1104.26, -515.271) 27 | color = Color(0.545098, 0.545098, 0.545098, 1) 28 | polygon = PackedVector2Array(-310.623, 183.663, -61.7179, 600.748, 533.637, -176.24, -290.442, -256.966) 29 | 30 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon2" index="0"] 31 | points = PackedVector2Array(-291.652, -256.658, 532.336, -178.543, -62.3463, 597.568, -311.906, 182.812, -292.084, -257.034) 32 | default_color = Color(1.2, 1.2, 1.2, 1) 33 | joint_mode = 2 34 | begin_cap_mode = 2 35 | end_cap_mode = 2 36 | antialiased = true 37 | 38 | [node name="FracturesSlider" parent="FractureParent/CanvasLayer" index="0"] 39 | grow_horizontal = 0 40 | grow_vertical = 2 41 | 42 | [node name="MinAreaSlider" parent="FractureParent/CanvasLayer" index="1"] 43 | grow_vertical = 2 44 | -------------------------------------------------------------------------------- /demo/test-scenes/fracture-scenes/usable/Fracture_DelaunayConvex.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://cy7qo4lnb6sc3"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://bfk6v7unlow4m" path="res://demo/test-scenes/fracture-scenes/FractureSceneBasic.tscn" id="1"] 4 | [ext_resource type="Texture2D" uid="uid://bsrwyn1s0ui61" path="res://demo/smiley-face.png" id="2"] 5 | 6 | [node name="Fracture_DelaunayConvex" instance=ExtResource("1")] 7 | 8 | [node name="FracturePolygon" type="Polygon2D" parent="FractureParent/SourcePolygons" index="0"] 9 | show_behind_parent = true 10 | position = Vector2(0, -228.982) 11 | rotation = -0.610865 12 | scale = Vector2(0.8, 0.8) 13 | color = Color(0.545098, 0.545098, 0.545098, 1) 14 | polygon = PackedVector2Array(-387.17, -461.463, -88, -485.82, 312, -333.82, 592, -45.82, 520, 298.18, 0, 386.18, -552, 162.18) 15 | 16 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon" index="0"] 17 | points = PackedVector2Array(-552, 162.18, -384, -461.82, -88, -485.82, 312, -333.82, 592, -45.82, 520, 298.18, 0, 386.18, -552, 162.18) 18 | default_color = Color(1.2, 1.2, 1.2, 1) 19 | joint_mode = 2 20 | begin_cap_mode = 2 21 | end_cap_mode = 2 22 | antialiased = true 23 | 24 | [node name="FracturePolygon2" type="Polygon2D" parent="FractureParent/SourcePolygons" index="1"] 25 | show_behind_parent = true 26 | position = Vector2(-1286.53, -478.345) 27 | scale = Vector2(2, 2) 28 | color = Color(0.545098, 0.545098, 0.545098, 1) 29 | texture = ExtResource("2") 30 | texture_offset = Vector2(256, 256) 31 | polygon = PackedVector2Array(-2.09875, -254.711, 131.385, -222.675, 206.136, -149.259, 258.195, -2.42639, 224.824, 135.062, 138.059, 213.818, 1.90576, 255.198, -140.922, 216.487, -233.026, 105.696, -255.718, 0.243286, -214.338, -141.25, -122.234, -228.014) 32 | 33 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon2" index="0"] 34 | points = PackedVector2Array(-1.68506, -254.794, 127.919, -221.831, 205.081, -148.414, 259.021, -3.07852, 231.302, 109.295, 139.156, 213.427, 2.06116, 256.129, -142.526, 215.674, -233.922, 107.796, -257.895, -0.0819092, -213.695, -140.923, -123.797, -224.828, -1.68457, -254.794) 35 | default_color = Color(1.2, 1.2, 1.2, 1) 36 | joint_mode = 2 37 | begin_cap_mode = 2 38 | end_cap_mode = 2 39 | antialiased = true 40 | 41 | [node name="FracturePolygon3" type="Polygon2D" parent="FractureParent/SourcePolygons" index="2"] 42 | show_behind_parent = true 43 | position = Vector2(1313.09, -545.693) 44 | rotation = 2.15723 45 | color = Color(0.545098, 0.545098, 0.545098, 1) 46 | texture = ExtResource("2") 47 | texture_offset = Vector2(256, 256) 48 | polygon = PackedVector2Array(-2.09875, -254.711, 131.385, -222.675, 206.136, -149.259, 258.195, -2.42639, 224.824, 135.062, 138.059, 213.818, 1.90576, 255.198, -140.922, 216.487, -233.026, 105.696, -255.718, 0.243286, -214.338, -141.25, -122.234, -228.014) 49 | 50 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon3" index="0"] 51 | points = PackedVector2Array(-1.68506, -254.794, 127.919, -221.831, 205.081, -148.414, 259.021, -3.07852, 231.302, 109.295, 139.156, 213.427, 2.06116, 256.129, -142.526, 215.674, -233.922, 107.796, -257.895, -0.0819092, -213.695, -140.923, -123.797, -224.828, -1.68457, -254.794) 52 | default_color = Color(1.2, 1.2, 1.2, 1) 53 | joint_mode = 2 54 | begin_cap_mode = 2 55 | end_cap_mode = 2 56 | antialiased = true 57 | 58 | [node name="ColorRect" parent="CanvasLayer" index="0"] 59 | offset_left = 0.0 60 | offset_right = 0.0 61 | 62 | [node name="Title" parent="CanvasLayer/ColorRect" index="0"] 63 | offset_left = -425.0 64 | offset_top = -5.0 65 | offset_right = 425.0 66 | offset_bottom = 45.0 67 | grow_vertical = 1 68 | -------------------------------------------------------------------------------- /demo/test-scenes/fracture-scenes/usable/Fracture_DelaunayRectangle.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://uw5nk55tgvrf"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://bfk6v7unlow4m" path="res://demo/test-scenes/fracture-scenes/FractureSceneBasic.tscn" id="1"] 4 | 5 | [node name="Fracture_DelaunayConvex" instance=ExtResource("1")] 6 | 7 | [node name="FractureParent" parent="." index="0"] 8 | delauny_type = 2 9 | 10 | [node name="FracturePolygon" type="Polygon2D" parent="FractureParent/SourcePolygons" index="0"] 11 | show_behind_parent = true 12 | position = Vector2(-1344, -608) 13 | color = Color(0.545098, 0.545098, 0.545098, 1) 14 | polygon = PackedVector2Array(-256, -256, 256, -256, 256, 256, -256, 256) 15 | 16 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon" index="0"] 17 | points = PackedVector2Array(-256, -256, 256, -256, 256, 256, -256, 256, -256, -256) 18 | default_color = Color(1.2, 1.2, 1.2, 1) 19 | joint_mode = 2 20 | begin_cap_mode = 2 21 | end_cap_mode = 2 22 | antialiased = true 23 | 24 | [node name="FracturePolygon2" type="Polygon2D" parent="FractureParent/SourcePolygons" index="1"] 25 | show_behind_parent = true 26 | position = Vector2(1508.07, -608) 27 | color = Color(0.545098, 0.545098, 0.545098, 1) 28 | polygon = PackedVector2Array(-256, -256, 256, -256, 256, 256, -256, 256) 29 | 30 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon2" index="0"] 31 | points = PackedVector2Array(-256, -256, 256, -256, 256, 256, -256, 256, -256, -256) 32 | default_color = Color(1.2, 1.2, 1.2, 1) 33 | joint_mode = 2 34 | begin_cap_mode = 2 35 | end_cap_mode = 2 36 | antialiased = true 37 | 38 | [node name="FracturePolygon3" type="Polygon2D" parent="FractureParent/SourcePolygons" index="2"] 39 | show_behind_parent = true 40 | position = Vector2(0, -8) 41 | color = Color(0.545098, 0.545098, 0.545098, 1) 42 | polygon = PackedVector2Array(-768, -120, 768, -120, 768, 136, -768, 136) 43 | 44 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon3" index="0"] 45 | points = PackedVector2Array(-768, -120, 768, -120, 768, 136, -768, 136, -768, -120) 46 | default_color = Color(1.2, 1.2, 1.2, 1) 47 | joint_mode = 2 48 | begin_cap_mode = 2 49 | end_cap_mode = 2 50 | antialiased = true 51 | 52 | [node name="ColorRect" parent="CanvasLayer" index="0"] 53 | offset_left = 0.0 54 | offset_right = 0.0 55 | 56 | [node name="Title" parent="CanvasLayer/ColorRect" index="0"] 57 | offset_left = -425.0 58 | offset_top = -5.0 59 | offset_right = 425.0 60 | offset_bottom = 45.0 61 | grow_vertical = 1 62 | -------------------------------------------------------------------------------- /demo/test-scenes/fracture-scenes/usable/Fracture_Simple.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://b5nk7e37xxhkg"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://bfk6v7unlow4m" path="res://demo/test-scenes/fracture-scenes/FractureSceneBasic.tscn" id="1"] 4 | 5 | [node name="Fracture_DelaunayConvex" instance=ExtResource("1")] 6 | 7 | [node name="FractureParent" parent="." index="0"] 8 | delauny_fracture = false 9 | delauny_type = 0 10 | 11 | [node name="FracturePolygon" type="Polygon2D" parent="FractureParent/SourcePolygons" index="0"] 12 | show_behind_parent = true 13 | position = Vector2(-1344, -608) 14 | color = Color(0.545098, 0.545098, 0.545098, 1) 15 | polygon = PackedVector2Array(-256, -256, 256, -256, 256, 256, -317.54, 705.882, -416.318, 615.113, 104.269, 57.1505, -256.137, -87.0122) 16 | 17 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon" index="0"] 18 | points = PackedVector2Array(-256, -256, 256, -256, 256, 256, -314.87, 705.883, -408.309, 612.444, 98.9299, 57.1505, -256.137, -81.6729, -256, -256) 19 | default_color = Color(1.2, 1.2, 1.2, 1) 20 | joint_mode = 2 21 | begin_cap_mode = 2 22 | end_cap_mode = 2 23 | antialiased = true 24 | 25 | [node name="FracturePolygon2" type="Polygon2D" parent="FractureParent/SourcePolygons" index="1"] 26 | show_behind_parent = true 27 | position = Vector2(1508.07, -608) 28 | color = Color(0.545098, 0.545098, 0.545098, 1) 29 | polygon = PackedVector2Array(-256, -256, 24.527, -137.685, 256, -256, 71.6703, 120.481, 256, 256, -85.4742, 104.767, -256, 256, -123.638, -112.991) 30 | 31 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon2" index="0"] 32 | points = PackedVector2Array(-256, -256, 22.5247, -137.292, 256, -256, 76.2648, 122.923, 256, 256, -83.5414, 107.367, -256, 256, -127.382, -116.079, -256, -256) 33 | default_color = Color(1.2, 1.2, 1.2, 1) 34 | joint_mode = 2 35 | begin_cap_mode = 2 36 | end_cap_mode = 2 37 | antialiased = true 38 | 39 | [node name="FracturePolygon3" type="Polygon2D" parent="FractureParent/SourcePolygons" index="2"] 40 | show_behind_parent = true 41 | position = Vector2(0, -8) 42 | color = Color(0.545098, 0.545098, 0.545098, 1) 43 | polygon = PackedVector2Array(-768, -120, 768, -120, 768, 136, -768, 136) 44 | 45 | [node name="Line2D" type="Line2D" parent="FractureParent/SourcePolygons/FracturePolygon3" index="0"] 46 | points = PackedVector2Array(-768, -120, 768, -120, 768, 136, -768, 136, -768, -120) 47 | default_color = Color(1.2, 1.2, 1.2, 1) 48 | joint_mode = 2 49 | begin_cap_mode = 2 50 | end_cap_mode = 2 51 | antialiased = true 52 | 53 | [node name="Title" parent="CanvasLayer" index="0"] 54 | offset_left = -315.0 55 | offset_right = 315.0 56 | grow_horizontal = 2 57 | text = "FRACTURE SIMPLE (fractures limited to 32)" 58 | 59 | [node name="InfoLabel" parent="CanvasLayer" index="1"] 60 | offset_left = -87.0 61 | offset_top = -94.0 62 | offset_right = 87.0 63 | offset_bottom = 0.0 64 | grow_horizontal = 2 65 | grow_vertical = 0 66 | -------------------------------------------------------------------------------- /demo/test-scenes/point-fracture/PointFractureSceneBasic.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=9 format=3 uid="uid://clve0s0mtn7bx"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://c5k3aj7yblg0f" path="res://demo/test-scenes/test-scene-basic/TestSceneBasic.tscn" id="1"] 4 | [ext_resource type="PackedScene" uid="uid://dhrqqo38wiuqi" path="res://pool-manager/Pool2DBasic.tscn" id="2"] 5 | [ext_resource type="PackedScene" path="res://demo/src/FractureShard.tscn" id="3"] 6 | [ext_resource type="PackedScene" path="res://demo/src/point-fracture-ball/PointFractureBall.tscn" id="4"] 7 | [ext_resource type="Script" path="res://demo/src/PointFracture.gd" id="5"] 8 | [ext_resource type="PackedScene" path="res://demo/src/CutShapeVisualizer.tscn" id="6"] 9 | [ext_resource type="PackedScene" path="res://demo/src/Arrow.tscn" id="7"] 10 | 11 | [sub_resource type="PhysicsMaterial" id="1"] 12 | friction = 0.0 13 | bounce = 1.0 14 | 15 | [node name="PointFractureSceneBasic" instance=ExtResource("1")] 16 | 17 | [node name="PointFracture" type="Node2D" parent="." index="0"] 18 | script = ExtResource("5") 19 | fracture_body_color = Color(0.529412, 0.807843, 0.819608, 1) 20 | no_ammo_line_color = Color(0.843137, 0.384314, 0.384314, 1) 21 | 22 | [node name="SourceParent" type="Node2D" parent="PointFracture" index="0"] 23 | 24 | [node name="Edge" type="StaticBody2D" parent="PointFracture" index="1"] 25 | modulate = Color(0.537255, 0.266667, 0.266667, 1) 26 | physics_material_override = SubResource("1") 27 | 28 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="PointFracture/Edge" index="0"] 29 | polygon = PackedVector2Array(1888, -928, 1888, 960, 576, 960, 448, 672, -384, 672, -512, 960, -1888, 960, -1888, -928, -1952, -928, -1952, 1120, 1952, 1120, 1952, -1120, -1952, -1088, -1952, -928) 30 | 31 | [node name="Polygon2D" type="Polygon2D" parent="PointFracture/Edge" index="1"] 32 | color = Color(0.596078, 0.596078, 0.596078, 1) 33 | polygon = PackedVector2Array(1888, -928, 1888, 960, 576, 960, 448, 672, -384, 672, -512, 960, -1888, 960, -1888, -928, -1952, -928, -1952, 1120, 1952, 1120, 1952, -1120, -1952, -1120, -1952, -928) 34 | 35 | [node name="Line2D" type="Line2D" parent="PointFracture/Edge" index="2"] 36 | points = PackedVector2Array(-1888, -928, 1888, -928, 1888, 960, 576, 960, 448, 672, -384, 672, -512, 960, -1888, 960, -1888, -928) 37 | width = 20.0 38 | joint_mode = 2 39 | begin_cap_mode = 2 40 | end_cap_mode = 2 41 | 42 | [node name="Pool_FractureShards" parent="PointFracture" index="2" instance=ExtResource("2")] 43 | placed_in_level = true 44 | instance_template = ExtResource("3") 45 | max_amount = 100 46 | instantiate_new_on_empty = true 47 | keep_instances_in_tree = true 48 | 49 | [node name="Pool_CutVisualizer" parent="PointFracture" index="3" instance=ExtResource("2")] 50 | placed_in_level = true 51 | instance_template = ExtResource("6") 52 | max_amount = 25 53 | instantiate_new_on_empty = true 54 | keep_instances_in_tree = true 55 | 56 | [node name="Pool_PointFractureBall" parent="PointFracture" index="4" instance=ExtResource("2")] 57 | placed_in_level = true 58 | instance_template = ExtResource("4") 59 | max_amount = 5 60 | 61 | [node name="FlickLine" type="Line2D" parent="PointFracture" index="5"] 62 | modulate = Color(1.66, 0.56, 0, 1) 63 | joint_mode = 2 64 | begin_cap_mode = 2 65 | end_cap_mode = 2 66 | antialiased = true 67 | 68 | [node name="Arrow" parent="PointFracture/FlickLine" index="0" instance=ExtResource("7")] 69 | 70 | [node name="Camera2D" parent="." index="1"] 71 | zoom = Vector2(0.5, 0.5) 72 | 73 | [node name="Title" parent="CanvasLayer" index="0"] 74 | text = "POINT FRACTURE (max ammo: 5)" 75 | 76 | [node name="InfoLabel" parent="CanvasLayer" index="1"] 77 | text = "CHANGE TEST [W] 78 | FLICK [LMB HOLD] 79 | FULLSCREEN [F] 80 | QUIT [ESC]" 81 | -------------------------------------------------------------------------------- /demo/test-scenes/point-fracture/usable/PointFractureScene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://bls1gwiki0ia4"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://clve0s0mtn7bx" path="res://demo/test-scenes/point-fracture/PointFractureSceneBasic.tscn" id="1"] 4 | [ext_resource type="Texture2D" uid="uid://bsrwyn1s0ui61" path="res://demo/smiley-face.png" id="2"] 5 | [ext_resource type="Script" path="res://demo/src/RigidBody2D.gd" id="3"] 6 | [ext_resource type="PackedScene" path="res://demo/src/RigidBody2D.tscn" id="4"] 7 | 8 | [sub_resource type="PhysicsMaterial" id="1"] 9 | friction = 0.0 10 | bounce = 1.0 11 | 12 | [sub_resource type="PhysicsMaterial" id="2"] 13 | friction = 0.0 14 | bounce = 1.0 15 | 16 | [node name="PointFractureScene" instance=ExtResource("1")] 17 | 18 | [node name="RigidBody2D" type="RigidBody2D" parent="PointFracture/SourceParent" index="0"] 19 | position = Vector2(-1344, -576) 20 | collision_layer = 4 21 | collision_mask = 5 22 | mass = 500.0 23 | physics_material_override = SubResource("1") 24 | gravity_scale = 0.0 25 | can_sleep = false 26 | script = ExtResource("3") 27 | rand_linear_velocity_range = Vector2(25, 50) 28 | radius = 256.0 29 | placed_in_level = true 30 | randomize_texture_properties = false 31 | poly_texture = ExtResource("2") 32 | 33 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="PointFracture/SourceParent/RigidBody2D" index="0"] 34 | 35 | [node name="Polygon2D" type="Polygon2D" parent="PointFracture/SourceParent/RigidBody2D" index="1"] 36 | show_behind_parent = true 37 | color = Color(0.545098, 0.545098, 0.545098, 1) 38 | texture_offset = Vector2(256, 256) 39 | 40 | [node name="Line2D" type="Line2D" parent="PointFracture/SourceParent/RigidBody2D/Polygon2D" index="0"] 41 | default_color = Color(1.25, 1.25, 1.25, 1) 42 | joint_mode = 2 43 | begin_cap_mode = 2 44 | end_cap_mode = 2 45 | antialiased = true 46 | 47 | [node name="Center" type="Polygon2D" parent="PointFracture/SourceParent/RigidBody2D" index="2"] 48 | visible = false 49 | color = Color(0, 0, 0, 1) 50 | polygon = PackedVector2Array(-10.3909, 0.536697, -0.314636, -12.5448, 10.6455, 0.183144, 0.0389175, 12.0272) 51 | 52 | [node name="RigidBody2D3" type="RigidBody2D" parent="PointFracture/SourceParent" index="1"] 53 | position = Vector2(1376, -480) 54 | collision_layer = 4 55 | collision_mask = 5 56 | mass = 500.0 57 | physics_material_override = SubResource("1") 58 | gravity_scale = 0.0 59 | can_sleep = false 60 | script = ExtResource("3") 61 | rand_linear_velocity_range = Vector2(100, 200) 62 | radius = 256.0 63 | placed_in_level = true 64 | randomize_texture_properties = false 65 | poly_texture = ExtResource("2") 66 | 67 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="PointFracture/SourceParent/RigidBody2D3" index="0"] 68 | 69 | [node name="Polygon2D" type="Polygon2D" parent="PointFracture/SourceParent/RigidBody2D3" index="1"] 70 | show_behind_parent = true 71 | color = Color(0.545098, 0.545098, 0.545098, 1) 72 | texture_offset = Vector2(256, 256) 73 | 74 | [node name="Line2D" type="Line2D" parent="PointFracture/SourceParent/RigidBody2D3/Polygon2D" index="0"] 75 | default_color = Color(1.25, 1.25, 1.25, 1) 76 | joint_mode = 2 77 | begin_cap_mode = 2 78 | end_cap_mode = 2 79 | antialiased = true 80 | 81 | [node name="Center" type="Polygon2D" parent="PointFracture/SourceParent/RigidBody2D3" index="2"] 82 | visible = false 83 | color = Color(0, 0, 0, 1) 84 | polygon = PackedVector2Array(-10.3909, 0.536697, -0.314636, -12.5448, 10.6455, 0.183144, 0.0389175, 12.0272) 85 | 86 | [node name="RigidBody2D2" parent="PointFracture/SourceParent" index="2" instance=ExtResource("4")] 87 | position = Vector2(0, 96) 88 | mass = 1000.0 89 | rand_linear_velocity_range = Vector2(0, 0) 90 | radius = 500.0 91 | placed_in_level = true 92 | randomize_texture_properties = false 93 | 94 | [node name="Obstacle" type="StaticBody2D" parent="PointFracture" index="2"] 95 | modulate = Color(0.537255, 0.266667, 0.266667, 1) 96 | position = Vector2(-1280, 512) 97 | physics_material_override = SubResource("2") 98 | 99 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="PointFracture/Obstacle" index="0"] 100 | polygon = PackedVector2Array(-192, 0, 0, -192, 192, 0, 0, 192) 101 | 102 | [node name="Polygon2D" type="Polygon2D" parent="PointFracture/Obstacle" index="1"] 103 | color = Color(0.596078, 0.596078, 0.596078, 1) 104 | polygon = PackedVector2Array(-192, 0, 0, -192, 192, 0, 0, 192) 105 | 106 | [node name="Line2D" type="Line2D" parent="PointFracture/Obstacle" index="2"] 107 | points = PackedVector2Array(-192, 0, 0, -192, 192, 0, 0, 192, -192, 0) 108 | width = 20.0 109 | joint_mode = 2 110 | begin_cap_mode = 2 111 | end_cap_mode = 2 112 | 113 | [node name="Obstacle3" type="StaticBody2D" parent="PointFracture" index="3"] 114 | modulate = Color(0.537255, 0.266667, 0.266667, 1) 115 | position = Vector2(0, -640) 116 | physics_material_override = SubResource("2") 117 | 118 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="PointFracture/Obstacle3" index="0"] 119 | polygon = PackedVector2Array(-192, 0, 0, -192, 192, 0, 0, 192) 120 | 121 | [node name="Polygon2D" type="Polygon2D" parent="PointFracture/Obstacle3" index="1"] 122 | color = Color(0.596078, 0.596078, 0.596078, 1) 123 | polygon = PackedVector2Array(-192, 0, 0, -192, 192, 0, 0, 192) 124 | 125 | [node name="Line2D" type="Line2D" parent="PointFracture/Obstacle3" index="2"] 126 | points = PackedVector2Array(-192, 0, 0, -192, 192, 0, 0, 192, -192, 0) 127 | width = 20.0 128 | joint_mode = 2 129 | begin_cap_mode = 2 130 | end_cap_mode = 2 131 | 132 | [node name="Obstacle2" type="StaticBody2D" parent="PointFracture" index="4"] 133 | modulate = Color(0.537255, 0.266667, 0.266667, 1) 134 | position = Vector2(1280, 512) 135 | physics_material_override = SubResource("2") 136 | 137 | [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="PointFracture/Obstacle2" index="0"] 138 | polygon = PackedVector2Array(-192, 0, 0, -192, 192, 0, 0, 192) 139 | 140 | [node name="Polygon2D" type="Polygon2D" parent="PointFracture/Obstacle2" index="1"] 141 | color = Color(0.596078, 0.596078, 0.596078, 1) 142 | polygon = PackedVector2Array(-192, 0, 0, -192, 192, 0, 0, 192) 143 | 144 | [node name="Line2D" type="Line2D" parent="PointFracture/Obstacle2" index="2"] 145 | points = PackedVector2Array(-192, 0, 0, -192, 192, 0, 0, 192, -192, 0) 146 | width = 20.0 147 | joint_mode = 2 148 | begin_cap_mode = 2 149 | end_cap_mode = 2 150 | 151 | [node name="Title" parent="CanvasLayer/ColorRect" index="0"] 152 | text = "DELAUNY FRACTURE" 153 | -------------------------------------------------------------------------------- /demo/test-scenes/test-scene-basic/TestSceneBasic.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://c5k3aj7yblg0f"] 2 | 3 | [ext_resource type="PackedScene" path="res://demo/ui/Title.tscn" id="1"] 4 | [ext_resource type="PackedScene" path="res://demo/ui/InfoLabel.tscn" id="2"] 5 | [ext_resource type="PackedScene" path="res://demo/ui/BackgroundLayer.tscn" id="4"] 6 | 7 | [sub_resource type="LabelSettings" id="LabelSettings_8sgia"] 8 | font_size = 26 9 | 10 | [sub_resource type="LabelSettings" id="LabelSettings_jqqx1"] 11 | font_size = 20 12 | 13 | [node name="TestSceneBasic" type="Node2D"] 14 | 15 | [node name="Camera2D" type="Camera2D" parent="."] 16 | zoom = Vector2(0.5, 0.5) 17 | 18 | [node name="BackgroundLayer" parent="." instance=ExtResource("4")] 19 | 20 | [node name="CanvasLayer" type="CanvasLayer" parent="."] 21 | 22 | [node name="ColorRect" type="ColorRect" parent="CanvasLayer"] 23 | anchors_preset = 15 24 | anchor_right = 1.0 25 | anchor_bottom = 1.0 26 | grow_horizontal = 2 27 | grow_vertical = 2 28 | mouse_filter = 2 29 | color = Color(1, 1, 1, 0) 30 | 31 | [node name="Title" parent="CanvasLayer/ColorRect" instance=ExtResource("1")] 32 | texture_filter = 6 33 | layout_mode = 1 34 | anchors_preset = 5 35 | offset_left = -425.0 36 | offset_top = -5.0 37 | offset_right = 425.0 38 | offset_bottom = 45.0 39 | grow_horizontal = 2 40 | scale = Vector2(1, 1) 41 | label_settings = SubResource("LabelSettings_8sgia") 42 | horizontal_alignment = 1 43 | vertical_alignment = 1 44 | 45 | [node name="InfoLabel" parent="CanvasLayer/ColorRect" instance=ExtResource("2")] 46 | texture_filter = 6 47 | layout_mode = 1 48 | anchors_preset = 7 49 | offset_left = -150.0 50 | offset_top = -175.0 51 | offset_right = 150.0 52 | offset_bottom = 0.0 53 | grow_horizontal = 2 54 | grow_vertical = 0 55 | scale = Vector2(1, 1) 56 | label_settings = SubResource("LabelSettings_jqqx1") 57 | horizontal_alignment = 1 58 | -------------------------------------------------------------------------------- /demo/tile-test-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/demo/tile-test-01.png -------------------------------------------------------------------------------- /demo/tile-test-01.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://3igvussru6ef" 6 | path="res://.godot/imported/tile-test-01.png-1c21d1c8fcc88a909d68f7011b7512d1.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://demo/tile-test-01.png" 14 | dest_files=["res://.godot/imported/tile-test-01.png-1c21d1c8fcc88a909d68f7011b7512d1.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/ui/BackgroundLayer.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene format=2] 2 | 3 | [node name="BackgroundLayer" type="CanvasLayer"] 4 | layer = -100 5 | 6 | [node name="ColorRect" type="ColorRect" parent="."] 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | color = Color( 0.117647, 0.117647, 0.117647, 1 ) 10 | __meta__ = { 11 | "_edit_use_anchors_": false 12 | } 13 | -------------------------------------------------------------------------------- /demo/ui/FracturesSlider.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=8 format=3 uid="uid://nhnl6rlnpoqk"] 2 | 3 | [sub_resource type="ImageTexture" id="4"] 4 | 5 | [sub_resource type="ImageTexture" id="3"] 6 | 7 | [sub_resource type="ImageTexture" id="2"] 8 | 9 | [sub_resource type="ImageTexture" id="1"] 10 | 11 | [sub_resource type="StyleBoxFlat" id="5"] 12 | draw_center = false 13 | border_width_left = 12 14 | border_width_right = 12 15 | border_color = Color(0.384314, 0.384314, 0.384314, 1) 16 | 17 | [sub_resource type="StyleBoxFlat" id="7"] 18 | draw_center = false 19 | border_width_left = 12 20 | border_width_right = 12 21 | border_color = Color(0.815686, 1, 0.768627, 1) 22 | 23 | [sub_resource type="StyleBoxFlat" id="6"] 24 | bg_color = Color(0.803922, 0.972549, 1, 1) 25 | draw_center = false 26 | border_width_left = 12 27 | border_width_right = 12 28 | 29 | [node name="FracturesSlider" type="VSlider"] 30 | anchors_preset = 11 31 | anchor_left = 1.0 32 | anchor_right = 1.0 33 | anchor_bottom = 1.0 34 | offset_left = -65.0 35 | offset_top = 46.0 36 | offset_right = -31.0 37 | offset_bottom = -53.0 38 | theme_override_icons/grabber = SubResource("4") 39 | theme_override_icons/grabber_highlight = SubResource("3") 40 | theme_override_icons/grabber_disabled = SubResource("2") 41 | theme_override_icons/tick = SubResource("1") 42 | theme_override_styles/slider = SubResource("5") 43 | theme_override_styles/grabber_area = SubResource("7") 44 | theme_override_styles/grabber_area_highlight = SubResource("6") 45 | min_value = 1.0 46 | max_value = 512.0 47 | value = 32.0 48 | scrollable = false 49 | 50 | [node name="Label" type="Label" parent="."] 51 | layout_mode = 0 52 | offset_left = -306.0 53 | offset_top = 2.0 54 | offset_right = -155.0 55 | offset_bottom = 37.0 56 | scale = Vector2(2, 2) 57 | text = "Fractures: 100" 58 | -------------------------------------------------------------------------------- /demo/ui/InfoLabel.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [sub_resource type="StyleBoxFlat" id=1] 4 | bg_color = Color( 0.117647, 0.117647, 0.117647, 1 ) 5 | border_width_left = 2 6 | border_width_top = 2 7 | border_width_right = 2 8 | border_width_bottom = 2 9 | border_color = Color( 1, 0.976471, 0.72549, 0.588235 ) 10 | corner_radius_top_left = 20 11 | corner_radius_top_right = 20 12 | expand_margin_bottom = 20.0 13 | 14 | [node name="InfoLabel" type="Label"] 15 | anchor_left = 0.5 16 | anchor_top = 1.0 17 | anchor_right = 0.5 18 | anchor_bottom = 1.0 19 | offset_left = -154.0 20 | offset_top = -208.0 21 | offset_right = 20.0 22 | offset_bottom = -114.0 23 | scale = Vector2( 2, 2 ) 24 | theme_override_styles/normal = SubResource( 1 ) 25 | text = "CHANGE TEST [W] 26 | FRACTURE [S/ SPACE] 27 | AUTO FRACTURE [Q] 28 | FULLSCREEN [F] 29 | QUIT [ESC]" 30 | align = 1 31 | valign = 1 32 | __meta__ = { 33 | "_edit_use_anchors_": false 34 | } 35 | -------------------------------------------------------------------------------- /demo/ui/MinAreaSlider.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=8 format=3 uid="uid://dkj004hbc30ml"] 2 | 3 | [sub_resource type="ImageTexture" id="4"] 4 | 5 | [sub_resource type="ImageTexture" id="3"] 6 | 7 | [sub_resource type="ImageTexture" id="2"] 8 | 9 | [sub_resource type="ImageTexture" id="1"] 10 | 11 | [sub_resource type="StyleBoxFlat" id="5"] 12 | draw_center = false 13 | border_width_left = 12 14 | border_width_right = 12 15 | border_color = Color(0.384314, 0.384314, 0.384314, 1) 16 | 17 | [sub_resource type="StyleBoxFlat" id="7"] 18 | draw_center = false 19 | border_width_left = 12 20 | border_width_right = 12 21 | border_color = Color(0.815686, 1, 0.768627, 1) 22 | 23 | [sub_resource type="StyleBoxFlat" id="6"] 24 | bg_color = Color(0.803922, 0.972549, 1, 1) 25 | draw_center = false 26 | border_width_left = 12 27 | border_width_right = 12 28 | 29 | [node name="MinAreaSlider" type="VSlider"] 30 | anchors_preset = 9 31 | anchor_bottom = 1.0 32 | offset_left = 19.0 33 | offset_top = 51.0 34 | offset_right = 43.0 35 | offset_bottom = -38.0 36 | theme_override_icons/grabber = SubResource("4") 37 | theme_override_icons/grabber_highlight = SubResource("3") 38 | theme_override_icons/grabber_disabled = SubResource("2") 39 | theme_override_icons/tick = SubResource("1") 40 | theme_override_styles/slider = SubResource("5") 41 | theme_override_styles/grabber_area = SubResource("7") 42 | theme_override_styles/grabber_area_highlight = SubResource("6") 43 | min_value = 100.0 44 | max_value = 10000.0 45 | step = 100.0 46 | value = 800.0 47 | scrollable = false 48 | 49 | [node name="Label" type="Label" parent="."] 50 | layout_mode = 0 51 | offset_left = 33.7948 52 | offset_top = 2.0 53 | offset_right = 184.795 54 | offset_bottom = 37.0 55 | scale = Vector2(2, 2) 56 | text = "Min Area3D: 100" 57 | -------------------------------------------------------------------------------- /demo/ui/Title.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [sub_resource type="StyleBoxFlat" id=1] 4 | bg_color = Color( 1, 0.976471, 0.72549, 0.588235 ) 5 | corner_radius_bottom_right = 20 6 | corner_radius_bottom_left = 20 7 | 8 | [node name="Title" type="Label"] 9 | anchor_left = 0.5 10 | anchor_right = 0.5 11 | offset_left = -585.0 12 | offset_right = 45.0 13 | offset_bottom = 36.0 14 | scale = Vector2( 2, 2 ) 15 | theme_override_styles/normal = SubResource( 1 ) 16 | text = "DELAUNY FRACTURE" 17 | align = 1 18 | valign = 1 19 | clip_text = true 20 | __meta__ = { 21 | "_edit_use_anchors_": false 22 | } 23 | -------------------------------------------------------------------------------- /gifs/godot-polygon2d-fracture(v1.2.0)-readme02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/gifs/godot-polygon2d-fracture(v1.2.0)-readme02.gif -------------------------------------------------------------------------------- /gifs/godot-polygon2d-pointfracture-05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/gifs/godot-polygon2d-pointfracture-05.gif -------------------------------------------------------------------------------- /gifs/polygon-fracture-restore-simple01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/gifs/polygon-fracture-restore-simple01.gif -------------------------------------------------------------------------------- /gifs/polygon2d-cutfracture-showcase-02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/gifs/polygon2d-cutfracture-showcase-02.gif -------------------------------------------------------------------------------- /gifs/polygon2d-delauny-fracture-01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/gifs/polygon2d-delauny-fracture-01.gif -------------------------------------------------------------------------------- /gifs/polygon2d-fracture-simple-01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoloByte/godot-polygon2d-fracture/2c2bfcc07af920ef69a9c7ed3b6323949a1afe6d/gifs/polygon2d-fracture-simple-01.gif -------------------------------------------------------------------------------- /polygon2d-fracture/PolygonFracture.gd: -------------------------------------------------------------------------------- 1 | class_name PolygonFracture 2 | 3 | 4 | 5 | 6 | # MIT License 7 | # ----------------------------------------------------------------------- 8 | # This file is part of: 9 | # GODOT Polygon 2D Fracture 10 | # https://github.com/SoloByte/godot-polygon2d-fracture 11 | # ----------------------------------------------------------------------- 12 | # Copyright (c) 2021 David Grueneis 13 | # 14 | # Permission is hereby granted, free of charge, to any person obtaining a copy 15 | # of this software and associated documentation files (the "Software"), to deal 16 | # in the Software without restriction, including without limitation the rights 17 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | # copies of the Software, and to permit persons to whom the Software is 19 | # furnished to do so, subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be included in all 22 | # copies or substantial portions of the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | # SOFTWARE. 31 | 32 | 33 | 34 | 35 | var _rng : RandomNumberGenerator 36 | 37 | 38 | 39 | 40 | func _init(new_seed : int = -1) -> void: 41 | _rng = RandomNumberGenerator.new() 42 | 43 | if new_seed == -1: 44 | _rng.randomize() 45 | else: 46 | _rng.seed = new_seed 47 | 48 | 49 | 50 | 51 | #all fracture functions return an array of dictionaries -> where the dictionary is 1 fracture shard (see func makeShapeInfo) ------------------------------------- 52 | 53 | #fracture simple generates random cut lines around the bounding box of the polygon -> cut_number is the amount of lines generated 54 | #cut_number is capped at 32 because cut_number is not equal to the amount of shards generated -> 32 cuts can produce a lot of fracture shards 55 | func fractureSimple(source_polygon : PackedVector2Array, source_global_trans : Transform2D, cut_number : int, min_discard_area : float) -> Array: 56 | cut_number = clamp(cut_number, 0, 32) 57 | # source_polygon = PolygonLib.rotatePolygon(source_polygon, world_rot_rad) 58 | var bounding_rect : Rect2 = PolygonLib.getBoundingRect(source_polygon) 59 | var cut_lines : Array = getCutLines(bounding_rect, cut_number) 60 | 61 | var polygons : Array = [source_polygon] 62 | 63 | for line in cut_lines: 64 | var poly_line = PolygonLib.offsetPolyline(line, 0.1, true)[0] 65 | var new_polies : Array = [] 66 | for poly in polygons: 67 | var result : Array = PolygonLib.clipPolygons(poly, poly_line, true) 68 | new_polies += result 69 | 70 | polygons.clear() 71 | polygons += new_polies 72 | 73 | var fracture_info : Array = [] 74 | for poly in polygons: 75 | var triangulation : Dictionary = PolygonLib.triangulatePolygon(poly, true, true) 76 | 77 | if triangulation.area > min_discard_area: 78 | var shape_info : Dictionary = PolygonLib.getShapeInfoSimple(source_global_trans, poly, triangulation) 79 | fracture_info.append(shape_info) 80 | 81 | return fracture_info 82 | 83 | #fracture generates random points (cut_number * 2) and then connects 2 random points to form a cut line (each point is only used once) 84 | #lines are extended to make sure the whole polygon is cut 85 | #cut_number is capped at 32 because cut_number is not equal to the amount of shards generated -> 32 cuts can produce a lot of fracture shards 86 | func fracture(source_polygon : PackedVector2Array, source_global_trans : Transform2D, cut_number : int, min_discard_area : float) -> Array: 87 | cut_number = clamp(cut_number, 0, 32) 88 | # source_polygon = PolygonLib.rotatePolygon(source_polygon, world_rot_rad) 89 | var bounding_rect : Rect2 = PolygonLib.getBoundingRect(source_polygon) 90 | var points : Array = getRandomPointsInPolygon(source_polygon, cut_number * 2) 91 | var cut_lines : Array = getCutLinesFromPoints(points, cut_number, PolygonLib.getBoundingRectMaxSize(bounding_rect)) 92 | 93 | var polygons : Array = [source_polygon] 94 | 95 | for line in cut_lines: 96 | var poly_line = PolygonLib.offsetPolyline(line, 0.1, true)[0] 97 | var new_polies : Array = [] 98 | for poly in polygons: 99 | var result : Array = PolygonLib.clipPolygons(poly, poly_line, true) 100 | new_polies += result 101 | 102 | polygons.clear() 103 | polygons += new_polies 104 | 105 | var fracture_info : Array = [] 106 | for poly in polygons: 107 | var triangulation : Dictionary = PolygonLib.triangulatePolygon(poly, true, true) 108 | 109 | if triangulation.area > min_discard_area: 110 | var shape_info : Dictionary = PolygonLib.getShapeInfoSimple(source_global_trans, poly, triangulation) 111 | fracture_info.append(shape_info) 112 | 113 | return fracture_info 114 | 115 | #fracture delaunay uses delaunay triangulation to produce triangles -> random points (amount = fracture_number) inside the polygon are added to the source polygons points 116 | #to produce more triangles 117 | #is this func all produced triangles are clipped with the source polygon, to make sure there are no triangles outside 118 | #if you only use convex polygons use fractureDelaunyConvex 119 | func fractureDelaunay(source_polygon : PackedVector2Array, source_global_trans : Transform2D, fracture_number : int, min_discard_area : float) -> Array: 120 | # source_polygon = PolygonLib.rotatePolygon(source_polygon, world_rot_rad) 121 | var points = getRandomPointsInPolygon(source_polygon, fracture_number) 122 | var triangulation : Dictionary = PolygonLib.triangulatePolygonDelaunay(points + source_polygon, true, true) 123 | 124 | var fracture_info : Array = [] 125 | for triangle in triangulation.triangles: 126 | if triangle.area < min_discard_area: 127 | continue 128 | 129 | var results : Array = PolygonLib.intersectPolygons(triangle.points, source_polygon, true) 130 | for r in results: 131 | if r.size() > 0: 132 | if r.size() == 3: 133 | var area : float = PolygonLib.getTriangleArea(r) 134 | if area >= min_discard_area: 135 | var centroid : Vector2 = PolygonLib.getTriangleCentroid(r) 136 | # var source_global_trans := Transform2D(world_rot_rad, world_pos) 137 | var centered_shape = PolygonLib.translatePolygon(r, -centroid) 138 | var spawn_pos : Vector2 = PolygonLib.getShapeSpawnPos(source_global_trans, centroid) 139 | fracture_info.append(PolygonLib.makeShapeInfo(r, centered_shape, centroid, spawn_pos, triangle.area, source_global_trans)) 140 | else: 141 | var t : Dictionary = PolygonLib.triangulatePolygon(r, true, true) 142 | if t.area >= min_discard_area: 143 | var shape_info : Dictionary = PolygonLib.getShapeInfoSimple(source_global_trans, r, t) 144 | fracture_info.append(shape_info) 145 | 146 | return fracture_info 147 | 148 | #fractureDelaunayConvex works the same as the default fractureDelaunay but it asumes the source polygon is convex 149 | #makes the fracturing simpler and faster because the final triangles dont have to be clipped with the source polygon 150 | func fractureDelaunayConvex(concave_polygon : PackedVector2Array, source_global_trans : Transform2D, fracture_number : int, min_discard_area : float) -> Array: 151 | # concave_polygon = PolygonLib.rotatePolygon(concave_polygon, world_rot_rad) 152 | var points = getRandomPointsInPolygon(concave_polygon, fracture_number) 153 | var triangulation : Dictionary = PolygonLib.triangulatePolygonDelaunay(points + concave_polygon, true, true) 154 | 155 | var fracture_info : Array = [] 156 | for triangle in triangulation.triangles: 157 | if triangle.area < min_discard_area: 158 | continue 159 | 160 | var centroid : Vector2 = PolygonLib.getTriangleCentroid(triangle.points) 161 | # var source_global_trans := Transform2D(world_rot_rad, world_pos) 162 | var centered_shape = PolygonLib.translatePolygon(triangle.points, -centroid) 163 | var spawn_pos : Vector2 = PolygonLib.getShapeSpawnPos(source_global_trans, centroid) 164 | fracture_info.append(PolygonLib.makeShapeInfo(triangle.points, centered_shape, centroid, spawn_pos, triangle.area, source_global_trans)) 165 | 166 | return fracture_info 167 | 168 | #fractureDelaunayRectangle is the same as fractureDelaunayConvex but it asumes the source polygon is a rectangle 169 | #the rectangle makes the random point generation easier on top of the simplification of fractureDelaunayConvex 170 | func fractureDelaunayRectangle(rectangle_polygon : PackedVector2Array, source_global_trans : Transform2D, fracture_number : int, min_discard_area : float) -> Array: 171 | # rectangle_polygon = PolygonLib.rotatePolygon(rectangle_polygon, world_rot_rad) 172 | var points = getRandomPointsInRectangle(PolygonLib.getBoundingRect(rectangle_polygon), fracture_number) 173 | var triangulation : Dictionary = PolygonLib.triangulatePolygonDelaunay(points + rectangle_polygon, true, true) 174 | 175 | var fracture_info : Array = [] 176 | for triangle in triangulation.triangles: 177 | if triangle.area < min_discard_area: 178 | continue 179 | 180 | var centroid : Vector2 = PolygonLib.getTriangleCentroid(triangle.points) 181 | # var source_global_trans := Transform2D(world_rot_rad, world_pos) 182 | var centered_shape = PolygonLib.translatePolygon(triangle.points, -centroid) 183 | var spawn_pos : Vector2 = PolygonLib.getShapeSpawnPos(source_global_trans, centroid) 184 | fracture_info.append(PolygonLib.makeShapeInfo(triangle.points, centered_shape, centroid, spawn_pos, triangle.area, source_global_trans)) 185 | 186 | return fracture_info 187 | #----------------------------------------------------------------------------------------------------------------------------------------------------------------------- 188 | 189 | 190 | 191 | #returns a dictionary containing shapes and fractures 192 | #-> shapes is an array containing all shape infos generated -> the clipped shapes (the non overlapping areas of the source polygon and cut polygon) can be used for new source polygons 193 | #-> fractures is an array containing all fracture infos generated -> the intersected shapes (the overlapping areas of the source polygon and cut polygon) and the shapes smaller than cut_min_area are fractured 194 | #-> intersected shapes smaller than fracture_min_area are discarded 195 | #-> fracture pieces smaller than shard_min_area are discarded 196 | func cutFracture(source_polygon : PackedVector2Array, cut_polygon : PackedVector2Array, source_trans_global : Transform2D, cut_trans_global : Transform2D, cut_min_area : float, fracture_min_area : float, shard_min_area : float, fractures : int = 3) -> Dictionary: 197 | var cut_info : Dictionary = PolygonLib.cutShape(source_polygon, cut_polygon, source_trans_global, cut_trans_global) 198 | 199 | var fracture_infos : Array = [] 200 | if cut_info.intersected and cut_info.intersected.size() > 0: 201 | for shape in cut_info.intersected: 202 | var area : float = PolygonLib.getPolygonArea(shape) 203 | if area < fracture_min_area: 204 | continue 205 | 206 | var fracture_info : Array = fractureDelaunay(shape, source_trans_global, fractures, shard_min_area) 207 | fracture_infos.append(fracture_info) 208 | 209 | var shape_infos : Array = [] 210 | if cut_info.final and cut_info.final.size() > 0: 211 | for shape in cut_info.final: 212 | var triangulation : Dictionary = PolygonLib.triangulatePolygon(shape) 213 | var shape_area : float = triangulation.area#PolygonLib.getPolygonArea(shape) 214 | if shape_area < cut_min_area: 215 | var fracture_info : Array = fractureDelaunay(shape, source_trans_global, fractures, shard_min_area) 216 | fracture_infos.append(fracture_info) 217 | continue 218 | 219 | var shape_info : Dictionary = PolygonLib.getShapeInfoSimple(source_trans_global, shape, triangulation) 220 | shape_infos.append(shape_info) 221 | 222 | return {"shapes" : shape_infos, "fractures" : fracture_infos} 223 | 224 | 225 | #returns an array of PoolVector2Arrays -> each PoolVector2Array consists of two Vector2 [start, end] 226 | #is used in the func fracture 227 | func getCutLinesFromPoints(points : Array, cuts : int, max_size : float) -> Array: 228 | var cut_lines : Array = [] 229 | if cuts <= 0 or not points or points.size() <= 2: return cut_lines 230 | 231 | for i in range(cuts): 232 | var start : Vector2 = points[(i * 2)] 233 | var end : Vector2 = points[(i * 2) + 1] 234 | var dir : Vector2 = (end - start).normalized() 235 | 236 | #extend the line so it will always be bigger than the polygon 237 | start -= dir * max_size 238 | end += dir * max_size 239 | 240 | var line : PackedVector2Array = [start, end] 241 | cut_lines.append(line) 242 | return cut_lines 243 | 244 | #returns an array of PoolVector2Arrays -> each PoolVector2Array consists of two Vector2 [start, end] 245 | #is used in the func fractureSimple 246 | func getCutLines(bounding_rect : Rect2, number : int) -> Array: 247 | var corners : Dictionary = PolygonLib.getBoundingRectCorners(bounding_rect) 248 | 249 | var horizontal_pair : Dictionary = {"left" : [corners.A, corners.D], "right" : [corners.B, corners.C]} 250 | var vertical_pair : Dictionary = {"top" : [corners.A, corners.B], "bottom" : [corners.D, corners.C]} 251 | 252 | var lines : Array = [] 253 | for i in range(number): 254 | 255 | if _rng.randf() < 0.5:#horizontal 256 | var start : Vector2 = lerp(horizontal_pair.left[0], horizontal_pair.left[1], _rng.randf()) 257 | var end : Vector2 = lerp(horizontal_pair.right[0], horizontal_pair.right[1], _rng.randf()) 258 | var line : PackedVector2Array = [start, end] 259 | lines.append(line) 260 | else:#vertical 261 | var start : Vector2 = lerp(vertical_pair.top[0], vertical_pair.top[1], _rng.randf()) 262 | var end : Vector2 = lerp(vertical_pair.bottom[0], vertical_pair.bottom[1], _rng.randf()) 263 | var line : PackedVector2Array = [start, end] 264 | lines.append(line) 265 | 266 | return lines 267 | 268 | 269 | #generate a random polygon 270 | # points a generated around a circle (starting at 3 o´clock , clock wise) 271 | # each iteration a random angle step between angle_step_range.x and angle_step_range.y is calculated 272 | # that random angle step is added to the current angle 273 | # then a Vector2.RIGHT is scaled by a random size between radius_range.x and radius_range.y 274 | # finally the scaled vector is rotated by the current angle 275 | # !!!the angle_step_range is in degrees, x < y and x >= 0 and y > 0!!! 276 | # !!!radius_range x < y and x >= 0 and y > 0!!! 277 | func generateRandomPolygon(radius_range : Vector2, angle_step_range_deg : Vector2, offset := Vector2.ZERO) -> PackedVector2Array: 278 | var random_polygon : PackedVector2Array = [] 279 | 280 | var total_angle_deg : float = 0.0 281 | 282 | while true: 283 | var angle_step_deg : float = _rng.randf_range(angle_step_range_deg.x, angle_step_range_deg.y) 284 | total_angle_deg += angle_step_deg 285 | if total_angle_deg >= 360: 286 | break 287 | 288 | var point := Vector2.RIGHT * _rng.randf_range(radius_range.x, radius_range.y) 289 | random_polygon.append(point.rotated(deg_to_rad(total_angle_deg)) + offset) 290 | 291 | return random_polygon 292 | 293 | 294 | func getRandomPointsInRectangle(rectangle : Rect2, number : int) -> PackedVector2Array: 295 | var points : PackedVector2Array = [] 296 | 297 | for i in range(number): 298 | var x : float = _rng.randf_range(rectangle.position.x, rectangle.end.x) 299 | var y : float = _rng.randf_range(rectangle.position.y, rectangle.end.y) 300 | points.append(Vector2(x, y)) 301 | 302 | return points 303 | 304 | #gets a random triangle from a triangulation using the getRandomTriangle func and then gets a random point 305 | #inside the chosen triangle 306 | func getRandomPointsInPolygon(poly : PackedVector2Array, number : int) -> PackedVector2Array: 307 | var triangulation : Dictionary = PolygonLib.triangulatePolygon(poly, true, false) 308 | 309 | var points : PackedVector2Array = [] 310 | 311 | for i in range(number): 312 | var triangle : Array = getRandomTriangle(triangulation) 313 | if triangle.size() <= 0: continue 314 | var point : Vector2 = getRandomPointInTriangle(triangle) 315 | points.append(point) 316 | 317 | return points 318 | 319 | #if a polygon is triangulated, that function can be used to get a random triangle from the triangultion 320 | #each triangle is weighted based on its area 321 | func getRandomTriangle(triangulation : Dictionary) -> PackedVector2Array: 322 | var chosen_weight : float = _rng.randf() * triangulation.area 323 | var current_weight : float = 0.0 324 | for triangle in triangulation.triangles: 325 | current_weight += triangle.area 326 | if current_weight > chosen_weight: 327 | return triangle.points 328 | 329 | var empty : PackedVector2Array = [] 330 | return empty 331 | 332 | 333 | func getRandomPointInTriangle(points : PackedVector2Array) -> Vector2: 334 | var rand_1 : float = _rng.randf() 335 | var rand_2 : float = _rng.randf() 336 | var sqrt_1 : float = sqrt(rand_1) 337 | 338 | return (1.0 - sqrt_1) * points[0] + sqrt_1 * (1.0 - rand_2) * points[1] + sqrt_1 * rand_2 * points[2] 339 | -------------------------------------------------------------------------------- /polygon2d-fracture/PolygonRestorer.gd: -------------------------------------------------------------------------------- 1 | class_name PolygonRestorer 2 | 3 | 4 | 5 | 6 | var shape_stack : Array = [] 7 | var cur_entry : Dictionary 8 | 9 | 10 | func getOriginShape() -> PackedVector2Array: 11 | if shape_stack.size() <= 0: 12 | if cur_entry: 13 | return cur_entry.shape 14 | else: 15 | return PackedVector2Array([]) 16 | else: 17 | return shape_stack.front().shape 18 | 19 | func getOriginArea() -> float: 20 | if shape_stack.size() <= 0: 21 | if cur_entry: 22 | return cur_entry.area 23 | else: 24 | return -1.0 25 | else: 26 | return shape_stack.front().area 27 | 28 | func getCurShape() -> PackedVector2Array: 29 | if shape_stack.size() <= 0: 30 | return getOriginShape() 31 | else: 32 | return cur_entry.shape 33 | 34 | func getCurArea() -> float: 35 | if shape_stack.size() <= 0: 36 | return getOriginArea() 37 | else: 38 | return cur_entry.area 39 | 40 | 41 | 42 | 43 | func _init() -> void: 44 | cur_entry = createShapeEntry(PackedVector2Array([]), -1.0, true) 45 | 46 | 47 | 48 | 49 | func clear() -> void: 50 | shape_stack.clear() 51 | cur_entry = createShapeEntry(PackedVector2Array([]), -1.0, true) 52 | 53 | func addShape(shape : PackedVector2Array, shape_area : float = -1.0) -> void: 54 | if not cur_entry.empty: 55 | shape_stack.push_back(cur_entry) 56 | 57 | cur_entry = createShapeEntry(shape, shape_area) 58 | 59 | func popLast() -> Dictionary: 60 | if shape_stack.size() <= 0: 61 | return createShapeEntry(PackedVector2Array([]), -1.0, true) 62 | 63 | cur_entry = shape_stack.pop_back() 64 | return cur_entry 65 | 66 | func getLast() -> Dictionary: 67 | if shape_stack.size() <= 0: 68 | return createShapeEntry(PackedVector2Array([]), -1.0, true) 69 | else: 70 | return shape_stack.back() 71 | 72 | func createShapeEntry(shape : PackedVector2Array, area : float = -1.0, empty : bool = false) -> Dictionary: 73 | if area <= 0.0: 74 | area = PolygonLib.getPolygonArea(shape) 75 | 76 | var origin_area : float = getOriginArea() 77 | var total_p : float = 1.0 78 | if origin_area > 0.0: 79 | total_p = area / origin_area 80 | 81 | var delta_p : float = 0.0 82 | if shape_stack.size() > 0: 83 | delta_p = getLast().total_p - total_p 84 | 85 | return {"shape" : shape, "area" : area, "total_p" : total_p, "delta_p" : delta_p, "empty" : empty} 86 | 87 | 88 | 89 | 90 | #func getLastAmount(amount : float) -> Dictionary: 91 | # if shape_stack.size() <= 0: 92 | # return createShapeEntry(PoolVector2Array([]), -1.0, true) 93 | # elif shape_stack.size() == 1: 94 | # return shape_stack.front() 95 | # else: 96 | # var cur_amount : float = 0.0 97 | # for i in range(shape_stack.size()): 98 | # var entry : Dictionary = shape_stack[i] 99 | # cur_amount += entry.delta_p 100 | # if cur_amount >= amount: 101 | # return shape_stack[i] 102 | # 103 | # return shape_stack.front() 104 | # 105 | #func popLastAmount(amount : float) -> Dictionary: 106 | # if shape_stack.size() <= 0: 107 | # return createShapeEntry(PoolVector2Array([]), -1.0, true) 108 | # elif shape_stack.size() == 1: 109 | # cur_entry = shape_stack.pop_back() 110 | # return cur_entry 111 | # else: 112 | # var cur_amount : float = 0.0 113 | # var index : int = -1 114 | # for i in range(shape_stack.size()): 115 | # var entry : Dictionary = shape_stack[i] 116 | # cur_amount += entry.delta_p 117 | # if cur_amount >= amount: 118 | # index = i 119 | # break 120 | # 121 | # if index < 0: 122 | # var entry : Dictionary = shape_stack.front() 123 | # clear() 124 | # return entry 125 | # else: 126 | # cur_entry = shape_stack[index] 127 | # shape_stack.resize(index) 128 | # return cur_entry 129 | -------------------------------------------------------------------------------- /pool-manager/Pool2DBasic.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | 4 | 5 | 6 | # MIT License 7 | # ----------------------------------------------------------------------- 8 | # This file is part of: 9 | # GODOT Polygon 2D Fracture 10 | # https://github.com/SoloByte/godot-polygon2d-fracture 11 | # ----------------------------------------------------------------------- 12 | # Copyright (c) 2021 David Grueneis 13 | # 14 | # Permission is hereby granted, free of charge, to any person obtaining a copy 15 | # of this software and associated documentation files (the "Software"), to deal 16 | # in the Software without restriction, including without limitation the rights 17 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | # copies of the Software, and to permit persons to whom the Software is 19 | # furnished to do so, subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be included in all 22 | # copies or substantial portions of the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | # SOFTWARE. 31 | 32 | 33 | 34 | 35 | #!!!----All pooled instances need these 2 things----!!! 36 | #func despawn() -> called when instance is finished 37 | #signal Despawn(self) -> instance emits this signal when finished to let the pool know to despawn it 38 | #----------------------------------------------------- 39 | 40 | 41 | 42 | #if pool is added to scene tree from the editor use this or setup the pool via code later 43 | @export var placed_in_level: bool = false 44 | @export var instance_template: PackedScene 45 | @export var max_amount: int = 0 46 | @export var instantiate_new_on_empty: bool = false 47 | @export var keep_instances_in_tree: bool = false 48 | 49 | 50 | #all instances are a child of this node 51 | @onready var _instance_parent := $Instances 52 | 53 | 54 | 55 | 56 | var template : PackedScene 57 | var max_size : int = 0 58 | var instantiate_new : bool = false 59 | var keep_in_tree : bool = false 60 | #if normal clearPool is used 61 | #-> the pool waits for instances to be despawned before queue free is called on them 62 | var clear_in_process : bool = false 63 | 64 | 65 | var instances_ready : Array = [] 66 | var instances_in_use : Array = [] 67 | 68 | 69 | 70 | 71 | func _ready() -> void: 72 | if placed_in_level: 73 | setup(instance_template, max_amount, true, instantiate_new_on_empty, keep_instances_in_tree) 74 | 75 | 76 | func _exit_tree() -> void: 77 | clearPoolInstant() 78 | 79 | 80 | func setup(template : PackedScene, max_size : int, ready_on_start : bool, instantiate_new : bool = false, keep_in_tree : bool = false) -> void: 81 | self.template = template 82 | self.max_size = max_size 83 | self.instantiate_new = instantiate_new 84 | self.keep_in_tree = keep_in_tree 85 | 86 | if ready_on_start: 87 | fillPool() 88 | 89 | 90 | 91 | 92 | func getParent(): 93 | return _instance_parent 94 | 95 | func getInstance(): 96 | if instances_ready.size() <= 0: 97 | if instantiate_new: 98 | addSingleInstance() 99 | var instance = instances_ready.pop_back() 100 | instances_in_use.push_back(instance) 101 | if not keep_in_tree: 102 | _instance_parent.add_child(instance) 103 | max_size += 1 104 | return instance 105 | else: 106 | return null 107 | else: 108 | var instance = instances_ready.pop_back() 109 | instances_in_use.push_back(instance) 110 | if not keep_in_tree: 111 | _instance_parent.add_child(instance) 112 | return instance 113 | 114 | 115 | func fillPool() -> void: 116 | if max_size <= 0: return 117 | var cur_size : int = instances_ready.size() + instances_in_use.size() 118 | addInstances(max(max_size - cur_size, 0)) 119 | 120 | 121 | func clearPoolInstant() -> void: 122 | for instance in instances_in_use: 123 | instance.queue_free() 124 | 125 | for instance in instances_ready: 126 | if keep_in_tree: 127 | instance.queue_free() 128 | else: 129 | instance.free() 130 | 131 | 132 | func clearPool() -> void: 133 | if clear_in_process: return 134 | if instances_in_use.size() > 0: 135 | clear_in_process = true 136 | 137 | for instance in instances_ready: 138 | if keep_in_tree: 139 | instance.queue_free() 140 | else: 141 | instance.free() 142 | 143 | 144 | func resizePool(amount : int) -> void: 145 | if amount == 0: return 146 | if amount > 0: 147 | max_size += amount 148 | addInstances(amount) 149 | else: 150 | max_size -= min(amount, max_size) 151 | removeInstances(max_size) 152 | 153 | 154 | func addInstances(amount : int) -> void: 155 | for i in range(amount): 156 | addSingleInstance() 157 | 158 | 159 | func addSingleInstance(): 160 | var instance = template.instantiate() 161 | instances_ready.push_back(instance) 162 | if keep_in_tree: 163 | _instance_parent.add_child(instance) 164 | instance.connect("Despawn", Callable(self, "On_Instance_Despawn")) 165 | return instance 166 | 167 | 168 | func removeInstances(amount : int) -> void: 169 | for i in range(abs(amount)): 170 | if instances_ready.size() > 0: 171 | var instance = instances_ready.pop_back() 172 | if keep_in_tree: 173 | instance.queue_free() 174 | else: 175 | instance.free() 176 | elif instances_in_use.size() > 0: 177 | var instance = instances_in_use.pop_back() 178 | instance.queue_free() 179 | else: 180 | return 181 | 182 | 183 | 184 | 185 | 186 | func On_Instance_Despawn(instance) -> void: 187 | if clear_in_process: 188 | var index : int = instances_in_use.find(instance) 189 | if index >= 0: 190 | instances_in_use.remove_at(index) 191 | instance.queue_free() 192 | 193 | if instances_in_use.size() <= 0: 194 | clear_in_process = false 195 | else: 196 | var index : int = instances_in_use.find(instance) 197 | if index >= 0: 198 | instances_in_use.remove_at(index) 199 | instances_ready.append(instance) 200 | 201 | if instance.has_method("despawn"): 202 | instance.despawn() 203 | 204 | if not keep_in_tree: 205 | _instance_parent.remove_child(instance) 206 | -------------------------------------------------------------------------------- /pool-manager/Pool2DBasic.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://dhrqqo38wiuqi"] 2 | 3 | [ext_resource type="Script" path="res://pool-manager/Pool2DBasic.gd" id="1"] 4 | 5 | [node name="Pool2DBasic" type="Node"] 6 | script = ExtResource("1") 7 | 8 | [node name="Instances" type="Node2D" parent="."] 9 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="Polygon Fracture 2D" 14 | config/version="2.0.0" 15 | run/main_scene="res://demo/src/Main.tscn" 16 | config/features=PackedStringArray("4.2") 17 | boot_splash/image="res://BootSplash.png" 18 | config/icon="res://application-icons/4.png" 19 | boot_splash/minimum_display_time=2500 20 | 21 | [display] 22 | 23 | window/size/viewport_width=1920 24 | window/size/viewport_height=1080 25 | window/size/window_width_override=1024 26 | window/size/window_height_override=600 27 | window/stretch/mode="viewport" 28 | window/stretch/aspect="expand" 29 | 30 | [input] 31 | 32 | fullscreen={ 33 | "deadzone": 0.5, 34 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":70,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) 35 | ] 36 | } 37 | fracture={ 38 | "deadzone": 0.5, 39 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) 40 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) 41 | ] 42 | } 43 | change-test={ 44 | "deadzone": 0.5, 45 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":87,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) 46 | ] 47 | } 48 | auto={ 49 | "deadzone": 0.5, 50 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":81,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) 51 | ] 52 | } 53 | cut={ 54 | "deadzone": 0.5, 55 | "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":2,"canceled":false,"pressed":false,"double_click":false,"script":null) 56 | ] 57 | } 58 | cut_line={ 59 | "deadzone": 0.5, 60 | "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null) 61 | ] 62 | } 63 | 64 | [layer_names] 65 | 66 | 2d_physics/layer_1="Wall" 67 | 2d_physics/layer_10="Blob" 68 | 69 | [rendering] 70 | 71 | environment/defaults/default_environment="res://demo/src/default_env.tres" 72 | --------------------------------------------------------------------------------