CollisionHandlerGravity: Horizontal velocity

Is it possible to define a horizontal velocity for the CollisionHandlerGravity? When using this handler with the player character he instantly falls down and no “previous-movement-direction” like a velocity push or something is been made. On this little drawing the right one would be nice to have :slight_smile:

Applying horizontal velocity is not the job of the CollisionHandlerGravity, if you wanted to continue applying the player velocity after the player is no longer making contact with some surface, there is nothing preventing you from doing so.

As I’m still a beginner, what is the best way of doing this? Programmatically setting the player position based on his movement direction isn’t hard, but it would interfere the CollisionHandlerGravity position manipulation I guess … same with a position Lerp if I’m not wrong

As long as you change the X and Y coordinates of the player, that should not interfere with the CollisionHandlerGravity managing their Z coordinate.

1 Like

At the end, I decided to use CollisionHandlerFloor instead of Gravity because of downwards movement.

What I was always wondering: to take the globalClock.dtime between frames into account, is it also viable to add it up? Because multiplying means small numbers which is in a “real world scale scenario” not always nice to have. Is something like this viable?

speed += dt
    def push_player(self, dt):
        '''
        Pushes the player based on his movement velocity
        with intensity shrinking over time
        '''
        self.velocity[0] = max(0, self.velocity[0] - (self.velocity[0] * dt * 1.019))
        self.velocity[1] = max(0, self.velocity[1] - (self.velocity[0] * dt * 1.019))

        self.simulate_gravity(dt)

        if self.direction == 'forward':
            self.player.set_fluid_y(self.player, self.velocity[1])
        elif self.direction == 'forward-diagonal-left':
            self.player.set_fluid_pos(self.player, -(self.velocity[0] * 0.707), (self.velocity[1] * 0.707), 0)
        elif self.direction == 'forward-diagonal-right':
            self.player.set_fluid_pos(self.player, (self.velocity[0] * 0.707), (self.velocity[1] * 0.707), 0)
        elif self.direction == 'left':
            self.player.set_fluid_x(self.player, -self.velocity[0])
        elif self.direction == 'right':
            self.player.set_fluid_x(self.player, self.velocity[0])
        elif self.direction == 'backward-diagonal-left':
            self.player.set_fluid_pos(self.player, -((self.velocity[0] / 2) * 0.707), -((self.velocity[1] / 2) * 0.707), 0)
        elif self.direction == 'backward-diagonal-right':
            self.player.set_fluid_pos(self.player, ((self.velocity[0] / 2) * 0.707), -((self.velocity[1] / 2) * 0.707), 0)
        elif self.direction == 'backward':
            self.player.set_fluid_y(self.player, -((self.velocity[1] / 2) * 0.707))

You have applied a deceleration correctly, but you really do also need to apply the dt when changing the position of the object, otherwise the object speed will be different depending on your frame rate.

Not sure what you mean by small numbers not being “nice to have”.

Aren’t these two lines taking the dt into account?

self.velocity[0] = max(0, self.velocity[0] - (self.velocity[0] * dt * 1.019))
self.velocity[1] = max(0, self.velocity[1] - (self.velocity[0] * dt * 1.019))

Only for the change in velocity (i.e. the acceleration); it’s still called for when handling the change in position (i.e. movement), I believe.

After all (as per the old equation, “speed = distance / time”), how far an object moves depends both on how fast it’s going and on the duration of its movement.

1 Like

In those two lines, you are applying an acceleration (or deceleration) to the velocity. But when calculating how much distance an object has travelled in the time since the last frame, you need to multiply the velocity by dt.

A way to verify this is by dimensional analysis: acceleration is m/s², or meters per second per second, so to get from an acceleration to a distance you have to end up multiplying by a unit of time twice. :slight_smile:

2 Likes

Then I guess my acceleration (velocity) is not calculated correctly. I pass here simply the player’s movement delta, meaning how far he moved from last frame to the current. But if I decrease and multiply this already small value (something like 0.06) I end up having zero visual impact of a push. Is that the correct way to calculate the speed the player had before he falls?

Ok I got it :slight_smile: in my case the velocity should then equal the speed value I use for the general movement

The acceleration calculation that you posted above looks fine enough, to me–presuming that you intend a deceleration, specifically.

I’m not sure of where you’re doing this–I don’t see it in the code that you posted above–so it’s hard to comment on the appropriateness of it.

But… Why would you multiply by a fixed, small value like that in the first place…?

Or are you talking about the effect of multiplying by dt? If so, then the small scale of dt shouldn’t be a problem; at most, you may have to increase the maximum speed (and if called for, the rate of acceleration) used for your character in order to compensate.

In short, I would suggest that there is no real difference in the player’s horizontal movement before and after a fall: in both cases, the position is offset by the character’s horizontal velocity multiplied by dt.

The only significant difference would be in whether the player has any control–and thus any means of influencing their velocity–while falling, I daresay.

@Thaumaturge you nailed it with this:

In short, I would suggest that there is no real difference in the player’s horizontal movement before and after a fall: in both cases, the position is offset by the character’s horizontal velocity multiplied by dt

Everything seems to work now and here is a little overview of the different steps with shortened code focusing on the ‘forward’ push:

def player_control(self, task):
    '''
    Main task to control the player
    '''
    dt = globalClock.dt

    walk_forward = self.is_button_down(self.forward_button)

    if not self.is_on_ground():

            if self.state != 'Fall':

                if walk_forward:
                    self.set_direction('forward')

                self.set_velocity(force_x=self.speed, force_y=self.speed)
                self.request('Fall') # using FSM states mainly to control character animation

            else:
                self.push_player(dt)

            return task.cont
    else:
        # allow movement because we are on the ground
        ...
    return task.cont
def set_velocity(self, force_x: float = 0, force_y: float = 0, force_z: float = 0):
    '''
    Set initial xyz velocity
    '''
    self.velocity = [
        0 + force_x,
        0 + force_y,
        0 + force_z,
    ]
def push_player(self, dt):
    '''
    Pushes the player based on velocity,
    with intensity decreasing over time
    '''
    self.velocity[0] -= self.velocity[0] * self.speed * dt
    self.velocity[1] -= self.velocity[1] * self.speed * dt

    if self.direction == 'forward':
        self.player.set_fluid_y(self.player, self.velocity[1] * dt)

Thanks guys for your answers and questions – helped me to sort things out and is pretty much appreciated

2 Likes