This topc explains about BooleanSpriteCutterWithTaggedShapes Script/ Fragment Manager Script.
Note: Part 1 is Here https://guild.rpgmakerofficial.com/t/topic/1115
BooleanSpriteCutterWithTaggedShapes Overview
BooleanSpriteCutterWithTaggedShapes is a Node2D script that targets Sprite2D nodes (and optionally re-cuttable fragments) in a specified group, uses “mask shape nodes” from another group to cut out intersecting regions, fragments them via Voronoi partitioning + boolean operations, and generates RigidBody2D fragments.
Fragments are categorized as inside (within the mask) and outside (the remainder), and you can configure force, physics, materials, and more separately for each.
Features
- Batch processing of multiple target sprites via groups (cut multiple at once)
- Collect mask shapes from another group (sequential processing of multiple masks)
- Separate settings for inside/outside force (direction/strength/torque)
- Fragment physics setup modes: Manual / inherit from Sprite / inherit from Shape / inherit from specified node
- Fragment spawn parent modes: Self / specified node / owner’s parent / owner’s child / Manager
- Supports re-cutting (re-fragmentation) with separate allow flags for inside/outside
- Supports edge lines (
Line2D) and material inheritance/override
Prerequisites and Requirements
| Item |
Content |
| Required add-on |
res://addons/gdDelaunay/Delaunay.gd must exist (it is preloaded) |
| Targets |
Sprite2D in target_sprite_group (texture required) / re-cuttable RigidBody2D fragments |
| Masks |
Shape nodes in shape_node_group (supported types listed below) |
| Intersection |
Cutting occurs only when mask AABB intersects target AABB and polygon intersection exists |
| Note |
For the sprite base polygon, only the first element of opaque_to_polygons() is used (materials with multiple separate opaque islands may behave differently from intention) |
Setup
- Place a
Node2D in the scene and attach this script.
- Add the
Sprite2D you want to break to target_sprite_group (default: SpriteGroup).
- Add mask nodes to
shape_node_group (default: CutterShapeGroup).
- Examples:
Polygon2D / CollisionPolygon2D / CollisionShape2D (Convex/Concave/Circle/Rectangle)
- Configure parameters in the Inspector (minimum:
alpha_threshold, simplify_tolerance, voronoi_seed_count).
- Call
call_cut() at the desired moment.
- Examples: input, buttons, collision events, animation events, etc.
Generated Node Specification
| Generated object |
Structure |
Meta / Group |
Notes |
| Inside fragments |
RigidBody2D (children: Polygon2D + CollisionPolygon2D + optional Line2D) |
META_IS_FRAGMENT=true / META_FRAGMENT_TYPE="inside" / META_FRAGMENT_SOURCE_SPRITE=source Sprite / added to target_sprite_group |
If unfreeze_delay>0, fragments are temporarily frozen then unfrozen and forces applied after timer |
| Outside fragments |
Same as above |
META_FRAGMENT_TYPE="outside" |
Outside force/physics/material settings are applied |
Source Sprite2D |
Hidden |
META_ALREADY_CUT=true |
Prevents double-cutting the same sprite |
| Re-cut source (fragment) |
queue_free() |
- |
During re-cutting, the original fragment is replaced |
Usage Examples
| Goal (use case) |
Setting |
Result |
Notes |
| Fragment only the area touched by the mask |
Add a mask shape to shape_node_group → call_cut() |
Only intersecting sprites generate fragments |
Multiple masks are processed sequentially |
| Throw inside fragments to the right |
inside_force_direction_mode=FixedVector, inside_force_fixed_vector=(1,0) |
Inside fragments fly in a constant direction |
Strength is inside_force_base_strength |
| Scatter radially from an explosion center |
*_force_direction_mode=ExplosionFromPoint, set *_explosion_center_node |
Fragments scatter outward |
You can override center via call_cut(force_config) |
| Larger pieces get stronger force/rotation |
use_area_scaling_for_impulse=true / use_area_scaling_for_torque=true |
Force and torque scale by area ratio |
Clamped by area_ratio_min/max |
| Organize fragments under a specific node |
fragment_parent_mode=SpecifiedNode (set fragment_parent_node) |
Fragments are grouped under the specified node |
You can also select Manager |
| Re-cut (finish cutting) |
allow_recutted_inside_fragments=true (and/or outside) |
Next call_cut() also includes fragments |
Fragments are added to target_sprite_group |
| Disable nearby collisions after cutting |
disable_related_collisions_on_cut=true |
Disables collisions around the original |
Generated fragments (META_IS_FRAGMENT) are excluded |
Property List
Basics
| Property |
Type / Default |
Description (behavior/calculation/notes) |
target_sprite_group |
String / "SpriteGroup" |
Group name to collect cut targets (Sprite2D and allowed fragments) |
shape_node_group |
String / "CutterShapeGroup" |
Group name to collect mask shape nodes |
alpha_threshold |
float / 0.1 |
Opaque threshold for BitMap.create_from_image_alpha (alpha >= threshold is opaque) |
simplify_tolerance |
float / 2.0 |
Simplification tolerance for opaque_to_polygons() (higher is lighter but rougher) |
voronoi_seed_count |
int / 10 |
Number of Voronoi seeds (more fragments, heavier) |
density |
float / 1.0 |
Mass coefficient: mass = area_world * density |
unfreeze_delay |
float / 0.0 |
Delay seconds before unfreezing generated bodies (>0 yields a temporary pause effect) |
circle_approx_segments |
int / 32 |
Segment count to approximate CircleShape2D as a polygon |
debug_log |
bool / false |
If true, prints processing logs (normally keep false) |
Voronoi Seed Distribution
| Property |
Type / Default |
Description |
voronoi_seed_density_mode |
enum / Uniform |
Uniform / TowardMaskCenter / TowardSpriteCenter / TowardNodeCenter |
voronoi_seed_density_power |
float / 2.0 |
Strength of centering bias (pow(t, power)) |
voronoi_seed_center_node |
NodePath / empty |
Center for TowardNodeCenter (Node2D.global_position) |
Force Settings (Inside)
| Property |
Type / Default |
Description |
inside_force_base_strength |
float / 1000.0 |
Base impulse strength for inside fragments |
inside_force_strength_jitter_ratio |
float / 0.0 |
Strength jitter (±ratio; direction fixed) |
inside_force_direction_mode |
enum / FixedVector |
FixedVector / ExplosionFromPoint |
inside_force_fixed_vector |
Vector2 / (1,0) |
Direction for FixedVector (normalized internally) |
inside_explosion_center_node |
NodePath / empty |
Explosion center Node2D for ExplosionFromPoint |
inside_torque_impulse |
float / 0.0 |
Torque applied to inside fragments |
Force Settings (Outside)
| Property |
Type / Default |
Description |
outside_force_base_strength |
float / 600.0 |
Base impulse strength for outside fragments |
outside_force_strength_jitter_ratio |
float / 0.0 |
Strength jitter (±ratio) |
outside_force_direction_mode |
enum / FixedVector |
FixedVector / ExplosionFromPoint |
outside_force_fixed_vector |
Vector2 / (0,-1) |
Direction for FixedVector |
outside_explosion_center_node |
NodePath / empty |
Explosion center |
outside_torque_impulse |
float / 0.0 |
Torque applied to outside fragments |
Size Scaling
| Property |
Type / Default |
Description |
use_area_scaling_for_impulse |
bool / true |
Scale impulse by area ratio |
use_area_scaling_for_torque |
bool / true |
Scale torque by area ratio |
area_ratio_min |
float / 0.2 |
Minimum clamp for area ratio |
area_ratio_max |
float / 3.0 |
Maximum clamp for area ratio |
impulse_area_exponent |
float / 0.5 |
impulse_scale = pow(area_ratio, exponent) |
torque_area_exponent |
float / 1.0 |
torque_scale = pow(area_ratio, exponent) |
Material Settings (Common / Inside / Outside)
| Property |
Type / Default |
Description |
fragment_inherit_material |
bool / true |
Inherit the original material (common default) |
fragment_use_custom_material |
bool / false |
Force-apply a common custom material |
fragment_custom_material |
Material / null |
Common custom material |
inside_fragment_inherit_material |
bool / true |
Inside-only: whether to inherit |
inside_fragment_use_custom_material |
bool / false |
Inside-only: use custom material |
inside_fragment_custom_material |
Material / null |
Inside-only material |
outside_fragment_inherit_material |
bool / true |
Outside-only: whether to inherit |
outside_fragment_use_custom_material |
bool / false |
Outside-only: use custom material |
outside_fragment_custom_material |
Material / null |
Outside-only material |
Edge Line Settings
| Property |
Type / Default |
Description |
draw_edge_line |
bool / true |
Draw fragment edges using Line2D |
edge_line_width |
float / 2.0 |
Line width |
edge_line_color_inside |
Color / white |
Line color for inside |
edge_line_color_outside |
Color / white |
Line color for outside |
Re-cut Settings
| Property |
Type / Default |
Description |
allow_recutted_inside_fragments |
bool / false |
Include inside fragments as targets on the next cut |
allow_recutted_outside_fragments |
bool / false |
Include outside fragments as targets on the next cut |
Disable Original Collisions
| Property |
Type / Default |
Description |
disable_related_collisions_on_cut |
bool / false |
Scan and disable collisions around the original sprite (generated fragments excluded) |
Fragment Parent (Spawn Destination)
| Property |
Type / Default |
Description |
fragment_parent_mode |
enum / Self |
Self / SpecifiedNode / OwnerParent / OwnerChild / Manager |
fragment_parent_node |
NodePath / empty |
Spawn destination for SpecifiedNode |
| Note |
- |
OwnerChild applies a fallback when re-cutting (owner is a fragment) |
Fragment Physics (Inside)
| Property |
Type / Default |
Description |
inside_fragment_physics_mode |
enum / FromSprite |
Manual / FromSprite / FromShape / FromSpecified |
inside_fragment_physics_reference_node |
NodePath / empty |
Reference node for FromSpecified |
inside_fragment_gravity_scale_manual |
float / 1.0 |
Manual: gravity scale |
inside_fragment_collision_layer_manual |
int(flags) / 1 |
Manual: Collision Layer |
inside_fragment_collision_mask_manual |
int(flags) / 1 |
Manual: Collision Mask |
inside_fragment_linear_damp_manual |
float / 0.0 |
Manual: Linear Damp |
inside_fragment_angular_damp_manual |
float / 0.0 |
Manual: Angular Damp |
inside_fragment_physics_material_manual |
PhysicsMaterial / null |
Manual: friction/bounce |
inside_fragment_lock_rotation_manual |
bool / false |
Manual: lock rotation |
Fragment Physics (Outside)
| Property |
Type / Default |
Description |
outside_fragment_physics_mode |
enum / FromSprite |
Manual / FromSprite / FromShape / FromSpecified |
outside_fragment_physics_reference_node |
NodePath / empty |
Reference node for FromSpecified |
outside_fragment_gravity_scale_manual |
float / 1.0 |
Manual: gravity scale |
outside_fragment_collision_layer_manual |
int(flags) / 1 |
Manual: Collision Layer |
outside_fragment_collision_mask_manual |
int(flags) / 1 |
Manual: Collision Mask |
outside_fragment_linear_damp_manual |
float / 0.0 |
Manual: Linear Damp |
outside_fragment_angular_damp_manual |
float / 0.0 |
Manual: Angular Damp |
outside_fragment_physics_material_manual |
PhysicsMaterial / null |
Manual: friction/bounce |
outside_fragment_lock_rotation_manual |
bool / false |
Manual: lock rotation |
Manager Integration
| Property |
Type / Default |
Description |
manager_mode |
enum / Autoload |
None / Autoload / SpecifiedNode |
manager_autoload_name |
String / "FragmentManagerSingleton" |
Autoload name (references /root/<name>) |
manager_node |
NodePath / empty |
Reference target for SpecifiedNode |
| Behavior |
- |
On fragment generation, calls register_fragment(body) on the resolved Manager (only if the method exists) |
Runtime Override (call_cut(force_config))
With the force_config argument to call_cut(), you can override the Inspector force settings only for that call.
| Target |
Key |
Example value |
| inside/outside |
base_strength |
1400.0 |
| inside/outside |
strength_jitter_ratio |
0.15 |
| inside/outside |
direction_mode |
FORCE_DIR_FIXED_VECTOR or FORCE_DIR_EXPLOSION |
| inside/outside |
fixed_vector |
Vector2(1, 0) |
| inside/outside |
explosion_center |
Vector2(100, 200) (world coordinates) |
| inside/outside |
torque_impulse |
3.0 |
FragmentManager Overview
FragmentManager is a Node2D script that manages the RigidBody2D fragments generated by the Cutter. Fragments are registered via register_fragment(), and it can manage count limits, TTL, freeze on settled, and optional off-screen deletion, with separate policies for inside/outside.
Features
- Separate management policies for inside/outside (limits, TTL, freeze, off-screen deletion)
- Holds references via
WeakRef; references are cleaned up automatically if fragments leave the tree
- Optionally disables collisions on freeze (set layer/mask to 0)
- Off-screen checks compute a view rectangle from the viewport
Camera2D (skips if unavailable)
Prerequisites and Requirements
- Assumes Godot 4.x 2D node structure.
- Managed objects are
RigidBody2D passed to register_fragment(body).
body must have META_FRAGMENT_TYPE ("inside" / "outside") set by the Cutter.
- To exclude certain fragments from management, set
META_FRAGMENT_NO_MANAGE=true.
Setup
- Attach this script to the root node of
fragment_manager.tscn.
- Register
fragment_manager.tscn as an Autoload so it stays under /root.
- Ensure the Cutter calls
FragmentManager.register_fragment(body) when generating fragments.
- If the Cutter already implements “call
register_fragment on spawn,” ensure the Autoload name and references match.
Generated/Managed Data
- This node stays as
Node2D and internally maintains two lists:
_fragments_inside
_fragments_outside
- Registered fragments receive meta data:
_boolean_fragment (fragment flag)
_fragment_spawn_msec (spawn time in ms)
_fragment_last_active_msec (last active time in ms)
- If
inside_fragment_group_name / outside_fragment_group_name are non-empty, fragments are added to those groups at registration.
Usage Examples
| Goal (use case) |
Setting |
Result |
Notes |
| Prevent too many fragments |
Set inside_max_fragments / outside_max_fragments |
Old fragments are deleted when exceeding limit |
No limit if <= 0 |
| Natural disappearance after time |
*_enable_ttl=true, set *_ttl_seconds |
Deleted after TTL |
Spawn time recorded on registration |
| Stop physics after settling |
*_enable_freeze_on_settled=true |
Frozen after sustained low speed |
Can also disable collisions optionally |
| Collect fragments off-screen |
*_delete_when_offscreen=true |
Deleted after off-screen duration |
If camera cannot be obtained, check is skipped |
| Keep specific fragments |
body.set_meta("_fragment_no_manage", true) |
Manager ignores that fragment |
Safest to set immediately after spawn |
Property List (FragmentManager)
| Property |
Type / Default |
Description (behavior/calculation/notes) |
inside_fragment_group_name |
String / "FragmentGroupInside" |
Group name added on registration for inside fragments. If empty, not added. |
inside_max_fragments |
int / 300 |
Max inside fragment count. If <= 0, unlimited. Excess is deleted via queue_free() oldest-first. |
inside_enable_ttl |
bool / true |
Enable TTL deletion for inside fragments. |
inside_ttl_seconds |
float / 8.0 |
Lifetime in seconds. Deleted when now - spawn_msec >= this. |
inside_enable_freeze_on_settled |
bool / true |
Enable freeze-on-settled for inside fragments. |
inside_settled_linear_speed |
float / 15.0 |
Linear speed threshold. Above this is considered “active” and updates last-active time. |
inside_settled_angular_speed |
float / 1.5 |
Angular speed threshold. Above this is considered “active.” |
inside_settled_grace_seconds |
float / 0.6 |
If low-speed state lasts this long, freeze is applied. |
inside_disable_collision_when_frozen |
bool / true |
On freeze, sets collision layer/mask to 0. No unfreeze restoration is performed. |
inside_delete_when_offscreen |
bool / false |
Enable off-screen deletion for inside fragments. |
inside_offscreen_grace_seconds |
float / 1.0 |
If off-screen lasts this long, delete. If it returns on-screen, update last-active time. |
outside_fragment_group_name |
String / "FragmentGroupOutside" |
Group name added on registration for outside fragments. If empty, not added. |
outside_max_fragments |
int / 300 |
Max outside fragment count. If <= 0, unlimited. Excess deleted oldest-first. |
outside_enable_ttl |
bool / true |
Enable TTL deletion for outside fragments. |
outside_ttl_seconds |
float / 8.0 |
Outside lifetime seconds. Deleted when expired. |
outside_enable_freeze_on_settled |
bool / true |
Enable freeze-on-settled for outside fragments. |
outside_settled_linear_speed |
float / 15.0 |
Linear speed threshold for outside fragments. |
outside_settled_angular_speed |
float / 1.5 |
Angular speed threshold for outside fragments. |
outside_settled_grace_seconds |
float / 0.6 |
Low-speed grace duration for outside fragments. |
outside_disable_collision_when_frozen |
bool / true |
On freeze, sets collision layer/mask to 0. No restoration. |
outside_delete_when_offscreen |
bool / false |
Enable off-screen deletion for outside fragments. |
outside_offscreen_grace_seconds |
float / 1.0 |
Off-screen grace seconds for outside fragments. |
debug_log |
bool / false |
If true, logs when deleting by limit/TTL/off-screen, etc. |
1 Like
It feels like a really good way to enhance the visual expressiveness of the screen.
1 Like