Dynamically creating a CollisionShape2D and making it a child of another object?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Robster
:warning: Old Version Published before Godot 3 was released.

Hi all,

I’m dynamically creating a sprite like so:

powerUpSprite = Sprite.new()	#create a new sprite
add_child(powerUpSprite)		#add it as a child 

var powerUpImage 				#the texture of the powerup will live here

powerUpImage = load("res://textures/powerUpTestImage.png")
powerUpSprite.set_texture(powerUpImage)
powerUpSprite.set_pos(Vector2(100,100))

It works fine.

Now I want to create a CollisionShape2D and making it a child of that sprite.
It must basically:

  • Follow the sprite (be a child)
  • resize to be the same dimensions of the sprite
  • have a collision rectangleShape2D added that matches the size of the CollisionShape2D

So I guess I’m ok to figure out all those points, but the initial part I’m stuck on is making the CollisionShape2D a child of the sprite. Then making the rectangleShape2D.

OK I’ve been playing further with this… Still stuck though

I have above but I’ve added the following:

var powerUpImage                #the texture of the powerup will live here	

powerUpSprite = Sprite.new()    #create a new sprite
add_child(powerUpSprite)        #add it as a child 

powerUpImage = load("res://textures/"+powerUp+".png")	#load the appropriate image file
powerUpSprite.set_texture(powerUpImage)					#assign it to the sprite	

#screen is 540 x 300.  Fit the spawing in an appropriate random area within that
var posX = randi()%350+1
var posY = randi()%190+1
#now add some offset, so we don't appear behind bats or in walls etc
posX = posX + 100
posY = posY + 80
#now draw the powerup on screen
powerUpSprite.set_pos(Vector2(posX,posY))					


#Create the collision shape
var shape = RectangleShape2D.new()
#resize the shape to be the same size as the sprite texture
shape.set_extents(Vector2(powerUpSprite.get_texture().get_width(),powerUpSprite.get_texture().get_height()))	
#create the collission object
powerUpCollision = CollisionShape2D.new()
#attach the shape to the object
powerUpCollision.set_shape(shape)
#add it as a child
add_child(powerUpCollision)

So that works great, in that it creates the shape, it sizes it to match the sprite, but the problem is… it places it in the top left of the screen (at 0,0).

I can’t find anything in the docs that makes reference to moving that shape so it’s in the same location of the sprite.

Does anyone know how to do that? Thanks a tonne…

Robster | 2017-03-25 01:23

You shouldn’t be using CollisionShape2D in code as it’s only a helper class for the editor. You’re also missing a collision object of some kind (Area2D should work fine for a power-up). Do something like:
-Create an Area2D
-Create a RectangleShape2D like you’ve done above (if all your power-ups are the same shape you can re-use the RectangleShape2D)
-Use Area2D.add_shape to assign the shape to the Area2D
-Add your Sprite as a child of the Area2D
-Add the Area2D to the scene and set its position

mollusca | 2017-03-25 18:34

Thank you. I’ve tried this and am not having much luck. Have added my reply to the answer below to keep the thread organised.

Robster | 2017-03-31 05:32

@mollusca, I was having an issue with creating the CollisionShape2D because I kept trying to mimick how it’s created in the Editor:

	var shape = CircleShape2D.new()
	var col = CollisionShape2D.new()
	col.set_shape(col)
	add_child(col)
	shape.set_radius(wpn_sprite_tex.get_size().x)

But it would never work, so I changed it to:

	var shape = CircleShape2D.new()
	shape.set_radius(wpn_sprite_tex.get_size().x)
	add_shape(shape)

And works great. Unfortunately it’s not visible if you turn on the “View Colliison Shapes” setting in the editor, but it works. Thanks for your help!!

wombatTurkey | 2020-01-16 01:00

:bust_in_silhouette: Reply From: eons

CollisionShape nodes are editor helpers for in-editor interactive shape manipulation and to get visual debug information, only work with CollisionObjects as parent.

Shape is just a shape (and a resource, does not have transform data), and I guess CollisionShape nodes retrieve their CollisionObject parent transform information to draw it, that is why you see it always at 0.

If you want to use only shapes and sprites (instead of nodes ready for overlap detection like Area2D), then make the shape resource and use the sprite node transform data later when you need to check for collisions.

You can look at the “shower of bullets” demo for more about how to handle pure shapes.

Thank you (and @mollusca) for the replies. I’ve been having a good go at this but am starting to bash my head.

Here’s what I have:

func renderPowerup(powerUp):

	var powerUpImage = load("res://textures/"+powerUp+".png")	#load the appropriate image file, sent as a variable to this function
	powerUpSprite = Sprite.new()    							#create a new sprite
	powerUpSprite.set_texture(powerUpImage)						#assign it to the sprite	
	#add_child(powerUpSprite) 									#draw the sprite to screen

	#screen is 540 x 300.  Fit the spawing in an appropriate random area within that
	var posX = randi()%350+1
	var posY = randi()%190+1
	#now add some offset, so we don't appear behind bats or in walls etc
	posX = posX + 100
	posY = posY + 80

	#create an area2D 
	var area = Area2D.new()
	#Set the location of the area2D
	area.set_pos(Vector2(posX,posY))

	#create a rectangle shape
	var rectShape = RectangleShape2D.new()
	#add the rectShape to the areaShape
	area.add_shape(rectShape)	
	#make the sprite a child of the areaShape, this draws it to the screen I believe?
	area.add_child(powerUpSprite)
	#resize the shape to be the same size as the sprite texture
	rectShape.set_extents(Vector2(powerUpSprite.get_texture().get_width(),powerUpSprite.get_texture().get_height()))
	#Set the position of the sprite
	powerUpSprite.set_pos(Vector2(posX,posY))

You’ll note that at the top I have #add_child(powerUpSprite) commented out. If I uncomment this and comment out the line area.add_child(powerUpSprite) then it works, but I suspect it’s not a child of the area2D anymore. So it’s just displaying, but probably in no relation to the area2D which is not what we want.

If I leave it commented, then it never appears and I get an error on the line area.add_child(powerUpSprite) saying that it’s already parented.

Can anyone offer some advice as to why? I’m stumped! Thanks so much.

Robster | 2017-03-31 05:35

I suspect you are a bit confused on how the scene system works, if you can read again about scenes and instancing on docs and/or some of the recommended tutorials you can find around here or on reddit before continuing; you are almost there but seems you are missing in some part of the scene/node system.


For the questions:

Look at the remote inspector to see the scene structure and where the nodes are being added.

A node can have one parent only (is a tree structure), if says powerUpSprite is already parented is because was already added to the tree.

I see that powerUpSprite is on an instance variable, you can’t add the same Node instance twice to the tree.
Also, the last line is setting the sprite’s local position (relative to the area parent) far from local zero.


You are creating all the stuff by code, are you sure you don’t want to use the engine features to create a scene and just change some properties after instancing it?

The power up scene could be (like demo and docs examples)

-Area2D (script)
   |-Sprite
   |-CollisionShape2D (only if shape won't change, until you know what it means)

Instance the PackedScene, then add the powerup scene instance to the tree.
The scene instance could take care itself about the random variables, on _init and/or _ready and/or _enter_tree, also add the shape and texture it needs.
You can add a spritesheet for the sprite and change the powerup image by changing the frames too.


Some people even use:

-Area2D (script)
 |-sprite-powerup1 (hidden)
 |-sprite-powerup2 (hidden)
 |-sprite-powerup3 (hidden)
 |-sprite-powerup4 (hidden)

Useful if spritesheet is not an option for X reason.
Then just show the sprite for the active powerup mode on ready, all the resources are loaded once so there is almost no waste of resources no matter how many times you instance it.

eons | 2017-03-31 16:26

I really want to thank you for the large amount of work you put into the replies.

I ended up going googley eyed and my brain melted. I’m just too much a beginer. To get through this for now, I’ve just made the power up sprites all the same size so I was able to do it all in the editor by creating as so:

Area2D (script here)

  • Sprite
  • CollisionShape2D (with shape assigned)

Then I just instanced the whole scene. It works perfectly.

The reason I was trying to do it the other way is my original artwork had different sized sprites for different powerups, hence requiring the shape to be a different size each time. But, there you have it. :slight_smile:

Thanks again.

Robster | 2017-04-07 06:02

Shapes are tricky to work with because are resources shared by all the instances of the same scene, and there are some bugs around them that may give headaches (hopefully, the shape system will change for 3.0).

A possible thing to do is to create a shape with the editor, then save on disk as resource, the same for each shape for the corresponding texture, then remove the CollisionShape (and texture).

When you load the powerup scene, make it load the shape (from file) corresponding to the sprite’s texture too and add it to the area.
This way you are using a single resource for each shape, no need to create a new one, just load and add it the same way as the texture images.

eons | 2017-04-07 12:08