Hello everybody,

Hoping you are all doing great, I was trying to implement slopes in the runner tutorial, and I want to know if anyone has tried it before, because but I don’t get it…

I looked into the Platypus engine by @Britzl, but the slope implementation is totally different, in the runner tutorial the “hero” is static (velocity.x = 0) and the “ground” is moving, and in Platypus, the ground is static and the hero is moving.

But @Britzl specifies the concept very well in the comments:

- detect if hero is moving up or down the slope?
- if slope_normal.x > 0 and the hero is going to the RIGHT then

move up - push up

the right amount depends on how the slope (goes?)

too much = airborne

too little = pushing into slope - else

move down - push down

So the concept is there and my idea is to have another contact group for the ground, let’s say “slope”, and then modify the message response:

```
function on_message(self, message_id, message, sender)
if message_id == hash("contact_point_response") then
-- check if we received a contact point message. One message for each contact point
if message.group == hash("geometry") then
handle_geometry_contact(self, message.normal, message.distance)
elseif message.group == hash("slope") then
handle_slope_contact(self, message.normal, message.distance)
end
end
end
```

If we don’t do anything the hero bounces and is projected forward…

Here’s how the function is going till now

```
local function handle_slope_contact(self, normal, distance)
local movement = vmath.vector3()
print("handle_slope_contact", normal, distance, self.velocity, movement)
-- moving up or down the slope?
if normal.x > 0 then
-- moving down
movement.y = -self.velocity.x * math.abs(normal.x)
movement.x = self.velocity.x * normal.y
print("SLOPE DOWN?", movement)
else
-- moving up
local ratio = 1 - math.abs(normal.x / normal.y)
print("SLOPE UP?", ratio, movement, self.velocity, normal)
movement.y = -self.velocity.x * normal.x * ratio
movement.x = self.velocity.x * normal.y
print("SLOPE UP->", ratio, movement)
end
-- project the correction vector onto the contact normal
-- (the correction vector is the 0-vector for the first contact point)
local proj = vmath.dot(self.correction, normal)
-- calculate the compensation we need to make for this contact point
local comp = (distance - proj) * normal
-- add it to the correction vector
self.correction = self.correction + comp
-- apply the compensation to the player character
go.set_position(go.get_position() + comp)
-- check if the normal points enough up to consider the player standing on the ground
-- (0.7 is roughly equal to 45 degrees deviation from pure vertical direction)
-- (1 is equal to 0 degrees deviation from pure vertical direction = flat horizon)
if normal.y > 0.7 then
self.ground_contact = true
end
-- project the velocity onto the normal
proj = vmath.dot(self.velocity, normal)
-- if the projection is negative, it means that some of the velocity points towards the contact point
if proj < 0 then
-- remove that component in that case
self.velocity = self.velocity - proj * normal
end
end
```

It’s not working because I’m really doing nothing yet…

Following the Platypus example, in the main script.update we have:

```
function update(self, dt)
...
if input.is_pressed(RIGHT) then
self.platypus.right(ground_contact and 120 or 100)
--where ground_contact is true or false
...
self.platypus.update(dt)
```

And in the Lua module we have, first the direction input:

```
--- Move the game object right
-- @param velocity Horizontal velocity (a number, not a vector)
function platypus.right(velocity)
...
if slope_normal then
-- moving up or down the slope?
-- the right amount depends on how the slope
-- to much = airborne
-- too little = pushing into slope
movement.y = ...
movement.x = ...
else
movement.x = velocity
end
```

And then the actual platypus update:

```
--- Call this every frame to update the platformer physics
-- @param dt
function platypus.update(dt)
...
-- move the game object
local distance = (platypus.velocity * dt) + (movement * dt)
local position = go.get_position()
go.set_position(position + distance)
```

So the slope dampens the displacement, because the hero moves, but again, in the Runner Tutorial the hero does not move, it’s the ground that moves…

Then… Shall I just zero-out the x speed?..

What do you think?