Skip to content

Instantly share code, notes, and snippets.

@kevinthompson
Last active February 12, 2024 11:35
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kevinthompson/84b2010306817d7845d2b11e96da2c48 to your computer and use it in GitHub Desktop.
Save kevinthompson/84b2010306817d7845d2b11e96da2c48 to your computer and use it in GitHub Desktop.
Tool Script to Create a CollisionPolygon2D from a child Sprite2D in Godot 4
# Tool to regenerate CollisionPolygon2D nodes from a sprite node
# when the scenes is loaded, when the editor regains focus, or
# when the "Recalculate Geometry" checkbox is clicked in the inspector.
@tool
extends Node2D
@export var recalculate_geometry : bool :
set(_value):
_create_polygon2d_from_sprite2d()
func _create_polygon2d_from_sprite2d():
# Find First StaticBody2D Child
var static_body = find_children("*", "StaticBody2D")[0]
# Destroy Existing Collision Polygons
for node in static_body.find_children("*", "CollisionPolygon2D"):
node.queue_free()
# Generate Bitmap from Sprite2D
var sprite = $Sprite2D
var image = sprite.texture.get_image()
var bitmap = BitMap.new()
bitmap.create_from_image_alpha(image)
# Create Collision Polygon from Opaque Pixels
var polys = bitmap.opaque_to_polygons(Rect2(Vector2.ZERO, image.get_size()), 0.0)
for poly in polys:
var collision_polygon = CollisionPolygon2D.new()
collision_polygon.polygon = poly
static_body.add_child(collision_polygon)
collision_polygon.set_owner(get_tree().get_edited_scene_root())
collision_polygon.position -= sprite.texture.get_size() / 2
# This is the same as above but it also adds reloading the collision
# polygons on scene load and when the editor window is focussed.
#
# It also accounts for convex shapes (shapes with holes in the middle)
# by splitting the sprite down the center and generating one group of
# polygons for the left side of the sprite and another for the right.
@tool
extends Node2D
@export var recalculate_geometry : bool :
set(_value):
_create_polygon2d_from_sprite2d()
@onready var sprite = $Sprite2D
func _ready():
if Engine.is_editor_hint():
_create_polygon2d_from_sprite2d()
if sprite:
sprite.connect("texture_changed", _handle_texture_change)
if sprite.texture:
sprite.texture.connect("changed", _create_polygon2d_from_sprite2d)
func _handle_texture_change():
_create_polygon2d_from_sprite2d()
if sprite.texture && !sprite.texture.is_connected("changed", _create_polygon2d_from_sprite2d):
sprite.texture.connect("changed", _create_polygon2d_from_sprite2d)
func _create_polygon2d_from_sprite2d():
# Find First StaticBody2D Child
var static_body
var static_bodies = find_children("*", "StaticBody2D")
if static_bodies.size() > 0:
static_body = static_bodies[0]
# Destroy Existing Collision Polygons
for node in static_body.find_children("*", "CollisionPolygon2D"):
node.queue_free()
if sprite.texture:
# Generate Bitmap from Sprite2D
var sprite = $Sprite2D
var image = sprite.texture.get_image()
var bitmap = BitMap.new()
bitmap.create_from_image_alpha(image)
var polys = bitmap.opaque_to_polygons(Rect2(Vector2.ZERO, Vector2(image.get_width()/2,image.get_height())), 0.0)
_add_collision_polygons_to_static_body(polys,static_body,sprite.texture.get_size() / 2)
polys = bitmap.opaque_to_polygons(Rect2(Vector2(image.get_width()/2, 0), Vector2(image.get_width(),image.get_height())), 0.0)
_add_collision_polygons_to_static_body(polys,static_body,Vector2(0, sprite.texture.get_height() / 2))
func _add_collision_polygons_to_static_body(polys: Array[PackedVector2Array], static_body: StaticBody2D, offset: Vector2):
for poly in polys:
var collision_polygon = CollisionPolygon2D.new()
collision_polygon.polygon = poly
static_body.add_child(collision_polygon)
collision_polygon.set_owner(get_tree().get_edited_scene_root())
collision_polygon.position -= offset
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment