I’m making a 2d platformer game and i’m having trouble working out how to stop my player moving through the ground. I have made it so that when the player hits the ground the y-velocity is set to 0, however due to adding gravity to my game the player sinks through the ground at the rate of gravity. I know that the velocity is stopping when I hit the bottom of the platform as it won’t let me back up through the platform. What can I do to stop gravity while a collision is in place with the top of a tile?
First some resources that might be helpful:
- The Platformer sample project (available from the Editor welcome screen): https://github.com/defold/template-platformer
- The Platformer tutorial: https://defold.com/tutorials/platformer/
- And the manual on resolving physics collisions: https://defold.com/manuals/physics-resolving-collisions/
One thing you could do is to set a ground contact flag when the player lands and don’t apply gravity while the flag is true. You reset the flag at the end of every update.
Keeping track of the flag is useful for deciding animations and if the player can jump or not, but it should not be necessary to not apply gravity. Have a look at the Platformer sample:
- It tracks a ground contact flag just like I suggested:
- It applies gravity every frame:
- BUT it also resets vertical velocity every frame that ground contact is detected:
Are you doing this as well or only when initial ground contact is made?
Thank you, I think I was doing it when it first contacted the ground. I’ll try this tomorrow.
I’ve just tried this and my player no longer stops moving when touching the ground at all for some reason. What does the normal and distance parts do?
I’m getting this error: attempt to compare number with nil
from this line
if distance > 0 then
The contact_point_response message is described here:
distance = the penetration distance between the objects
normal = normal in world space of the contact point, which points from the other object towards the current object
The distance describes the amount of overlap between the objects and the normal is the direction of the collision. A very simplified way of resolving a collision is to move the colliding object normal * distance pixels. (but there is more to it if you have multiple contacts in a single frame)
You can see examples of this in the images in the section on resolving collisions:
This should be message.distance
.
Thank you.
I changed the distance to message.distance and now get this error:
attempt to index global ‘message’ (a nil value)
Are you doing this in on_message()? Where the contact_point_response message is received.
I was doing it in this
local function handle_obstacle_contact(self, normal, distance)
Did it need to be in the on message?
No. If the correct messages are passed to handle_obstacle_contact() from your on_message() function it shouldn’t be necessary. Check where handle_obstacle_contact() is called in your code and make sure you pass it normal and distance from the contact_point_response message.
This is what I am passing it:
(self, message.normal, message.distance)
That looks good I think. Share the entire piece of code and the error you are getting.
local function handle_obstacle_contact(self, normal, distance)
if message.distance > 0 then
local project = vmath.project(self.correction, normal * distance)
if project < 1 then
local compensation = (distance -distance * project) * normal
go.set_position(go.get_position() + compensation)
self.correction = self.correction + compensation
end
end
--Collision with a wall
if math.abs(normal.x) > 0.7 then
self.wall_contact = true
self.velocity.x = 0
end
--Collision with the ground
if normal.y > 0.7 then
self.ground_contact = true
self.velocity.y = 0
end
--Collision with a celing
if normal.y < -0.7 then
self.velocity.y = 0
end
end
function on_message(self, message_id, message, sender)
--Collision
if message_id == hash("collision_response") then
handle_obstacle_contact(self, message.normal, message.distance)
print(yes)
end
end
Error:
ERROR:SCRIPT: /Levels/Level 1/player.script:60: attempt to index global 'message' (a nil value)
stack traceback:
/Levels/Level 1/player.script:60: in function handle_obstacle_contact
/Levels/Level 1/player.script:91: in function </Levels/Level 1/player.script:87>
line 60 is the if message.distance line and line 91 is where i am calling the function
That is the wrong message. The message you need to react to is “contact_point_response”.
I’ve just changed that and I’m still getting the same error message.
This is also wrong. Remove message and only check distance. You asked if it needed to be message and I said no a few replies above.
Thank you.
The player now stands on the ground for a while but his hotbox sinks through the ground and the player gets pulled through. Sorry for the questions I’m new to defold.
there is this error with this code.
ERROR:SCRIPT: /Levels/Level 1/player.script:78: attempt to index field 'velocity' (a nil value)
stack traceback:
/Levels/Level 1/player.script:78: in function handle_obstacle_contact
/Levels/Level 1/player.script:90: in function </Levels/Level 1/player.script:87>
if normal.y > 0.7 then
self.ground_contact = true
self.velocity.y = 0
end
Have you created the self.velocity field somewhere, usually in init().
Check this example of a player control script for a platformer game:
Thank you, it works how I want, however if the player holds jump when he hits the bottom of a platform he will stay there for a few seconds.
Getting stuck? Is that with the completely unmodified template project?
That’s with my project. When the player hits the top, he stays there for about a second before falling and the same if you hold left or right on a wall you will stick to it.