+1 vote

Hello again.

So I've got a player who is able to move freely in my 2D game. My overall aim is to allow the player to be able to climb the ladders that have been placed randomly around the map.

But in order to do that I first need to solve a problem, I can't figure out how to get my player to recognise he's collided with the ladder but still allow him free movement, ie so the collision in place doesn't stop the player dead in their tracks.

I have three different tilemaps. One for blocks (of various sizes, colours and types), one for misc objects to place around the game and finally, one for ladders.

Originally, there was no collisions for the ladders so I could move past them fine no problem. But I couldn't do anything with them.

So therefore I created a collision for the ladder tileset, so when my players comes into contact with them it'll print ("Ladder") for testing purposes. This works fine. The only problem is now the player collides with the ladders and it blocks his way.

My thinking was maybe there was some code I could use so that for players and objects like barrels, it wouldn't collide, it would allow it to pass. But I'm not sure how to proceed.

I've heard of collision layers that might be able to solve this little problem of mine, I altered the ladder tilemaps layer and mask options but this didn't appear to make a difference. Am I missing something?

Thanks in advance!

EDIT: I've just posted an 'answer' which really is just an update of my situation with the ladder problem at the moment. Feel free to take a look and answer! :)

in Engine by (88 points)
reshown by

3 Answers

+1 vote
Best answer

Here's a simple demo of how to make ladder blocks using either StaticBody2D or Area2D nodes. In this demo I didn't make a tileset, but just made and duplicated ladder nodes. However, the concept is the same and it's simple to make tiles the same way.

by (844 points)
selected by

"fixed"

Okay so it looks to me like you are trying to use a RigidBody2D node as if it was a KinematicBody2D node. You're not supposed to be setting the velocity of a RigidBody2D manually, but instead apply a force or an impulse on it. (aka give it a push) Setting the velocity every frame kinda messes up the physics and the point of using the physics in the first place. So I'd recommend switching to a KinematicBody2D since you are moving the character as if it were a KinematicBody2D anyways, but this way it'll be less buggy.

But disregarding all that, when you copied over my demo code, you didn't fully integrate it. You were setting the "velocity" variable but not using it. All I did was change it at around line 88, where I just set the velocity of the RigidBody2D using set_linear_velocity to a negative Y value, this way he goes up. I think you mixed up the variable "velocity" with the actual velocity of the character that's built into the RigidBody2D node. But yeah, now he goes up ladders, but now you need to clean up the code a little and polish it. A few suggestions would be to set the X value in the linear_velocity to 0 while climbing so he doesn't slide off ladders, and set the velocity to (0,0) when climbing but not pressing a key so that he doesn't fall off the ladder when not holding Up or W.

Thank you so much for helping me with this problem! You've helped me a great deal and I appreciate the time you've taken to assist me with this, it means a lot :)

You were correct about me getting the velocities mixed up, I was tired at the time, trying anything to get it to work. Thank you for your explanation, it's easy to understand.

Hopefully now I can clean up the code a little bit, make a few fixes as you've suggested and then do this for every set of ladders in my game. Wooo! Thanks again :)

Hi batmanasb.

I'm really sorry to be a nuisance but I've been trying to implement those fixes you suggested but I haven't had any luck with them. I've tried to do what you've said in your description but it ends up quite being quite wrong.

For example, the part where you suggested to set the velocity to (0,0) when climbing but not pressing a key, I tried to do that but instead when the player hit the ladder he stopped and then he couldn't move after that.

And me trying to set the X value in the linear_velocity to 0 while climbing so he doesn't slide off ladders, didn't accomplish anything.

It's quite frustrating really, I know exactly what you mean but I just can't figure out the code to in order to implement it properly. The sooner I put this ladder business behind me, the better!

I'm a bit hesitant to ask for your help as you've already been really helpful with everything else, I just figured seen as you suggested those two things you'd be able to help me finally fix this.

Sorry in advance if I've bothered you.

I meant set the Y velocity to 0 when on the ladder but not pressing any key, that way you won't fall off the ladder, but can still move in the X axis (left and right). Also, when climbing up (pressing UP) it might help to disable left/right movement so that you don't slide off the ladder. So setting linear_velocity to (0, climbingSpeed) should do that. Both these things are in my demo. But I think your real problem is dealing with a RigidBody2D node, they are awkward to control and only really useful when your character is physics based, which no Mario character ever was. If you'd switch it to a KinematicBody2D node, then you can follow the demo so much easier. Besides, using set_linear_velocity for movement is considered sloppy code as it messes up the physics.

Can you please reupload the demo

+1 vote

Ladder could be a Area2d with collision pollygon or collisionshape.

If player(body) enter/exit Area2d then player gain/lose possibility to climbe it.

by (689 points)
edited by

But wouldn't that still mean the Area2D or Ladder would still stop the player from moving past the ladder? That's the problem I'm facing at the moment.

I can set up the collision fine with the tilemap it's just I don't know how to allow the player to pass it without the ladder colliding with the player.

No, an area2d don't stop the player, it only register when a body enter or leave the area.
The area2d need to have a collision node to define the shape of the area.

To clarify: Add an Area2D node to the player and connect the signals area_enter and area_exit to the player script. Then add an Area2D node to the ladder tiles and name it "ladder". This way, when the player's Area2D overlaps the ladder Area2D, the functions area_enter and area_exit will be called in the player script. This way you can keep track of whether the player is in climbing mode or not. Although since tiles require an Area2D for each, that means the player will be entering and exiting while climbing from tile to tile, so you'll need a way to handle that. Something like a onLadderTile counter might work, where every area_enter adds 1 to the counter and ever area_exit subtracts one. Then if the counter is > 0, the player is in climbing mode. Also, in case you have multiple Area2D nodes on the same collision layer/mask, you can call area.get_name() in the area_enter and area_exit functions to identify if it's actually a ladder and not something else.

perfect description :-)

Thanks for answering! This is definitely something I may consider doing if my alternative idea doesn't work out, I'll post an answer just to show a little update I've got going, just to see if you'd be able to help with that. If not, I'll give your Area2D idea a go. Thanks :)

Your welcome. Oh and I just realized something. Your levels have a lot of slopes (tilted platforms), for which using X velocity isn't the most ideal because you run into the slope and slow down. And as seen here in the docs and in my demo, you can make a KinematicBody2D slide along the slope so it travels as if the slope is flat. Keep that in mind if you ever notice your character slowing down while walking in certain directions.

0 votes

I just thought I'd post an update. So I managed to be able to use the collision layer for the tilemap. Here is my code:

This is in my player script in the ready function:

connect("body_enter",self,"body_enter_function")

This is in my player script:

func body_enter_function( body ):
    var name = body.get_name()
    if name == "Barrel":
        lose_life()
    if name == "Ladder Tilemap":
        print("Player can climb the ladder!")
        get_node("../Ladder Tilemap").set_collision_layer(2)
        get_node("../Ladder Tilemap").set_collision_mask(2)
        #if btn_climb:
            #climb upwards
    print(self.get_name(), " has collided with ", body.get_name())

At the moment I've got it set up so that when the player moves past the ladder (it can do that now) it prints 'player can climb the ladder'. The only problem is it only prints this once, the collision layer remains the same though, so the player can still walk past as many ladders with no collision.

My only worry is that when I implement the climb part it'll only allow it the first time I pass a ladder and that's it. Because that's similar to what the test print function is doing. If that makes sense?

If this fails, I guess I'll use the Area2D idea. I just thought seen as I've got this far with this way, I might as well try and finish it. If any of you guys have an ideas, feel free to let me know! Cheers :)

by (88 points)

What nodes are you using for "collision" because (according to the search help tab) literally only 4 nodes have a "body_enter" signal, which are Area2D, RigidBody2D, and their 3D equivalents.

Also, it seems like you are already doing the "Area2D idea" just with enter_body instead of enter_area. Take a look at my clarification, it already covers the issue with multiple collisions per ladder.

I'm using a RigidBody2D node (called Player), which inside has my player sprite, raycastdown, animation, collision polygon2D information in it. I have a script attached to my RigidBody2D node (Player), and that is where the code above is located. I of course, created a signal connection from the RigidBody2D node (bodyenter) called 'bodyenter_function' as well for it to work.

Okay, I'll be sure to give it a try later on today. Hopefully I'll get somewhere with it.

I've tried what you suggested but unfortunately I haven't been able to get it working correctly. In fact, for some reason, it won't pick up anything. So I'm a bit stuck at the moment :(

I'm able to get it to recognise collisions with bodyenter, but with areaenter, nothing happens!

For area_enter to work the ladder has to be an Area2D or have an Area2D in it. But body_enter works for everything else. When an Area2D overlaps another collisionobject2d, such as an Area2D, RigidBody, etc. it sends the signal. if you want, I could make a quick demo for you if you are still having trouble.

What I'm basically saying is:

  1. Add an Area2D with a CollisionShape2D to the player
  2. Add an Area2D with a CollisionShape2D to the ladder
  3. Select the Area2D on the player and connect the signals for "enterarea" and "exitarea" to the player script
  4. The functions you connected it to will be called when ever you overlap any ladder object/tile
  5. Then you get the problem of moving between tiles (enter tile1, enter tile2, exit tile1), so simply having a variable called "isClimbing" doesn't work, but something like "climbingCounter" where you add 1 to it in "enterarea" and subtract one in "exitarea", then as long as it's greater than 0, you are climbing.

Also, one clean technique would be to change the layer and mask (idk the difference) of the Area2D nodes to their own value. So for example, collision is usually on:enter image description here
But you could move these two to the next one over (layer/mask 2). This way you won't need to make sure "enter_area" are called for the correct node because only ladders exist on mask/layer 2.

I'm fairly confident I followed your instructions to the letter, but I still don't know how to get it to work. I'm sorry!

If it helps, I can send you my project and you can see what the issue is? Ie, see what I'm doing wrong?

Thank you for taking the time to reply though, I appreciate the help you've given so far!

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.