Any way to change collision shape per sprite frame?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By ondesic

I have a sprite on a sheet that has different size depending on the frame. Is there a way to have collision shape change depending on what frame it is on?

Some game engines allow a black and white image to be added to a sprite sheet as a collision mask. Is there anything like this in Godot?

I don’t know if there is an easy way to do it, but if you manage to do it, maybe it will be fine for static objects… but issues could happen on kinematic or rigidbodies. Physics engines rarely like their shapes to change or teleport into something else every frame.

Zylann | 2018-04-17 17:58

Darn. How do you handle a sprite collision when the character ducks and the collision shape needs to be smaller?

ondesic | 2018-04-17 18:13

I didn’t say it doesn’t work, just that there might be issues.
For kinematic chars you could try, but probably will be more efficient to set a collision shape per state, and not per frame (because you could have many frames for the animation)

Zylann | 2018-04-17 18:41

So how would you do that? Do you create multiple shapes and programmatically disable them as needed?

ondesic | 2018-04-17 19:09

Yes. To do what you asked, you would need to do that anyways first before being able to do that per frame (which is basically what it would do)

Zylann | 2018-04-17 21:01

I have exactly the same problem: KinematicBody2D player needs a different collision rectange when it walks VS when it ducks VS when it swims. I have a separate CollisionShape2D for each state, and I use CollisionShape2D.disable so that only one of them is active at a time. It works, except, because the effective collision shape changes suddenly, it might will overlap with another collision shaple (like a wall tile or enemy), and the physics engine doesn’t allow that. So usually it automatically teleports the character on some direction to get rid of the overlap. This teleportation sometimes leads to comical glitches, since the engine can’t know what makes sense gameplay wise. Worse, sometimes it lefts the player stuck inside the wall… There must be a common way of handling this… So I also wonder what’s the best practice.

hdc128 | 2018-04-22 12:59

Since then I have realized that this kind of problem pops up in an even more basic case: if you flip a KinematicBody2D (like as the actor moves left or right), its CollisionShape2D isn’t flipped by Godot. (So it’s better be symmetrical…) If it would be flipped, they run into the same issue (changing collision shape, might overlaps with another suddenly). Now it starts to be absurd… I must be blind or something.

hdc128 | 2018-04-22 14:56

:bust_in_silhouette: Reply From: ondesic

Here is an answer to my own question. I hope this helps those trying to change the shape on the fly. If your shape is called “shape” you use:

$shape.get_shape().set_extents(Vector2(59, 59))

Hello,
I am currently having the same problem but I don’t understand how your example solved it exactly. What is the vector being used for? Also how/where would you incorporate this line of code into your program? I have four different hit-boxes (made with collisionpolygon2D) but I dont know how to attach them to the different animations. Any help or insight would be really appreciated.

Ramsey | 2018-10-25 00:26

:bust_in_silhouette: Reply From: 2plus2makes5

If you are using an AnimationPlayer node you can easily set any parameter of your CollisionShape

We should mark this one as the answer to the question

smedelyan | 2019-03-31 11:22

We should mark this one as the answer to the question [2]

bakudas | 2019-08-29 01:24

:bust_in_silhouette: Reply From: Hyper-Dimensional

If you’re still having trouble (I know I’m like a year late in 2019) then try using a raycast2D pointing above the character in that goes a little above it’s maximum height. Then use something like : if $RayCast2D.is_colliding() == false:
$CollisionShape2D.disabled = false
$CollisionShape2D2.disabled = true
in the code. The collision2D2 is the smaller one and the other is of course larger. Also the raycast will know when the ceiling is hit. I also used:
if Input.is_action_pressed(“ui_up”):
if is_attacking == false:
if on_ground == true:
velocity.y = JUMP_POWER
on_ground = false
$CollisionShape2D.disabled = true
$CollisionShape2D2.disabled = false
elif is_falling == true:
$CollisionShape2D.disabled = false
$CollisionShape2D2.disabled = true
I’ve tried it so far at it always seems to work. My character wasn’t getting stuck in the ceiling.