Platformer Collisions (Solved)

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:

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:

  1. It tracks a ground contact flag just like I suggested:
  1. It applies gravity every frame:
  1. 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?

1 Like

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.