Density not working.

We are making a game that involves physics, we have an elevator that goes up and down. There can be objects on the elevator. The problem is when our player is on the elevator and when it goes up and the elevator stops at its termination point, the player performs a little jump probably because of the inertia. The bounce is 0 for both and we tried changing the weight of the player by increasing the density (which is an obvious workaround) of the player but it doesn’t seem to work.

The player is a circular dynamic physics body: density=2,friction=1,bounce=0

The elevator is a kinematic rectangular body: density=0,friction=1,bounce=0

@Chris_31

  1. I don’t believe 0 is a legal value for density.

  2. Changing the player’s density won’t change its inertia. Try linearDamping instead: https://docs.coronalabs.com/api/type/Body/linearDamping.html

  3. Can you share a snippet showing how you’re making (and MOVING) this moving platform? e.g. elevator.

Ok, we can’t use damping as it will affect the player’s movement on elevator itself. By the way we have gravity enabled in the game.

Here’s a video of the problem to help understand better what the problem I am facing is.
@vlads @roaminggamer

Here’s a code snippet I have trimmed unimportant parts to keep it simple: moveElevator is a Runtime touch listener and below is the function’s definition.

if(eventTable.phase=="began")then

    --reset values of the previous touch to current touch values whenever touch is started afresh

    previousTouch.x=eventTable.x

    previousTouch.y=eventTable.y

elseif(eventTable.phase=="moved")then

        --compute the displacement from the previous touch and current location and set velocity values etc for movement of elevators

        local dY=eventTable.y-previousTouch.y

        if(myMath.abs(dY)>2)then--minimum sensistitivy

            local dt=deltaTime.getDelta()

            if(dY>0 )then

                object.lastTargetObject.elevatorDirection="down"--set direction of elevator to down so that it can be moved accordingly during update

            elseif(dY<0 )then

                object.lastTargetObject.elevatorDirection="up"--set direction of elevator to up so that it can be moved accordingly during update

            end

        end

        --mount values of touch coordinates into the record of the previous coordinates

        previousTouch.x=eventTable.x

        previousTouch.y=eventTable.y

    end
end

Then in the Update function, we just set velocities based on the direction of the elevator’s physics body i.e. sprite.

if(self.elevatorDirection=="up")then

        self.sprite:setLinearVelocity( 0, -200 )

    elseif(self.elevatorDirection=="down")then

        self.sprite:setLinearVelocity( 0, 200 )

    elseif(self.elevatorDirection=="none")then

        self.sprite:setLinearVelocity( 0,0 )    

    end
end

Physics body definition:

Player:

physics.addBody(character.sprite,"dynamic",{radius=25,density=1,friction=1,bounce=0})

Elevator:

local shape1={-120,20,-120,-15,120,-15,120,20,-120,20}
physics.addBody(object.sprite,"kinematic",{density=0,friction=1,bounce=0,shape=shape1})

Seems like a simple misunderstanding about the fundamentals of physics.

Your elevator may stop at a moment’s notice, but the physics forces will still apply to the the player (and to whatever else may be on said platform). This is the same as driving without a seatbelt and then hitting the breaks.

You need to set the player’s y velocity to 0 as well.

@XeduR has a good suggestion there. I should have thought of that! :slight_smile:

@Chris_31 - Nice looking game by the way. Thanks for sharing that!

@XeduR Thanks for the suggestion, but it won’t work for us as there can be multiple types of objects on the elevator that requires natural physics behaviour according to their mass. I believe whatever the force acting upwards on an object the instance the elevator is stopped, the object with high value of mass/density should counter the forces faster and thus should be less jumpy.

@roaminggamer Thanks for the compliment :slight_smile:

It doesn’t quite work that way.

When the elevator is moving up, the objects on top of it are being moved up at the same velocity as the elevator. This means that whatever object is on top of the elevator when it is moving will have the same velocity as the elevator regardless of their density. Since the velocity of any object should be a constant (i.e. the elevator’s velocity), adjusting the object’s density will only increase the force that is applied to the object by the elevator.

A relatively easy fix for this would be to keep track of which objects are on the elevator, by looking at the began phase of the collision, for instance. When the elevator stops, you check what objects are still on the elevator, i.e. haven’t received the “ended” phase of the collision. Then, as the elevator stops, you loop through the objects and assign their y velocity to zero.

Another approach could be to place a joint on each object on top of the elevator when it starts moving and remove the joints after the elevator has stopped, of course this wouldn’t be quite as dynamic as just setting the y velocity to zero.

There are several ways of addressing this issues, and unless I am completely mistaken, then there’s no issue with the physics engine itself or with density not working, this is how it’s supposed to work.

Thanks mate, you have been really helpful.

Another issue is realism.

The rate at which your elevator is moving up doesn’t reflect the true rate at which an elevator would move.

It feels right in the context of the game, but in reality an elevator that moved that fast and slowed that rapidly in the real-world would have the same effect on a human or object in the elevator as you’re seeing. It kind of makes me chuckle to think what that would look like.

Anyways, this leaves us with a conundrum. The Box2D physics library is trying to follow the rules, but the result isn’t what we want. i.e. We want fast movement (feels right in the game), but we also want fast stopping and objects not bouncing / thrown around.

@XeduR suggested some options and I have another. Instead of stopping the elevator abruptly, reduce the rate to zero over a short period just before it reaches the end of the move distance.

I think you can use the proxy helper from SSK2 to achieve this:


You don’t need all of SSK, you can grab the proxy helper alone: https://github.com/roaminggamer/SSK2/blob/master/ssk2/external/proxy.lua

The general idea (code may not be entirely right) is something like this:

-- PSEUDO CODE!!!
-- PSEUDO CODE!!!
-- PSEUDO CODE!!!

local proxy = require "proxy" -- assumes is in same folder as main.lua, but put it wherever you want and change require call

elevator = ssk.proxy.get_proxy_for( elevator )

function elevator:propertyUpdate( event )
    if( <moving up> && elevetor.y >= elevator.targetY ) begin
       -- Stop moving and snap to target Y
       elevator:setLinearVelocity(0,0)
       elevator.y = elevator.targetY
    end else if( <moving down> && elevetor.y <= elevator.targetY ) begin
       -- Stop moving and snap to target Y
       elevator:setLinearVelocity(0,0)
       elevator.y = elevator.targetY
    else begin
       elevator.setLinearVelocity( 0, self.yVel ) 
    end
end
elevator:addEventListener( "propertyUpdate" )

... then later

elevator.targetY = <calculate me>

local distance = math.abs(platform.y - elevator.targetY)

local moveTime = <calculate me based on rate of movement and distance>

elevator.yVel = <initial velocity>

transition.to( elevator, { time = moveTime, yVel = 0, transition = easing.inOutSine } )
-- or maybe try easing.outSine

If this idea is at all attractive, you might code up a standalone test to check the code first and get it all working, then apply that to your game.

Alternately, if this is totally confusing but you want to try it and can’t work it out, post back. I’ll try to find some time to make a demo. I just can’t right now.

  1. Sorry about the pseudo code. I’ve been writing a lot of SystemVerilog lately and some of the syntax carried over into the code I wrote above.

  2. That did NOT fix the problem.

  3. HOWEVER… Combined with linear damping I solved the problem.

See this demo:
https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2021/06/proxy_elevator.zip

Contains two examples. Just proxy and proxy with temporarily applied damping.

local physics = require("physics")
local proxy = require "proxy"

physics.start()
physics.setDrawMode("hybrid")


-- 
-- Elevator 1
-- 
local elevator1 = display.newRect( 200, 600, 100, 25 )
physics.addBody( elevator1, "kinematic", { bounce = 0,friction = 1 } )

local player1 = display.newRect( elevator1.x, elevator1.y - 50, 50, 50 )
physics.addBody( player1, "dynamic", { bounce = 0,friction = 1 } )

--convert 'elevator' to a proxy-based display object
elevator1 = proxy.get_proxy_for( elevator1 )

-- next, let's set up a listener for property updates
function elevator1:propertyUpdate( event )
	print( "Changed " .. event.key .. " to " .. event.value )

	if( event.key == "yVel" ) then
		elevator1:setLinearVelocity( 0, event.value )
	end
end
elevator1:addEventListener( "propertyUpdate" )

-- Start elevator in 1 second
timer.performWithDelay( 1000,
	function() 
		-- Elevator is going up
		elevator1.yVel = -500

		-- elevator1:setLinearVelocity( 0, elevator1.yVel )

		transition.to( elevator1, { yVel = 0, time = 1500, transition = easing.inOutSine } )
	end )


-- 
-- Elevator 2
-- 
local elevator2 = display.newRect( 500, 600, 100, 25 )
physics.addBody( elevator2, "kinematic", { bounce = 0,friction = 1 } )

local player2 = display.newRect( elevator2.x, elevator2.y - 50, 50, 50 )
physics.addBody( player2, "dynamic", { bounce = 0,friction = 1 } )

--convert 'elevator' to a proxy-based display object
elevator2 = proxy.get_proxy_for( elevator2 )

-- next, let's set up a listener for property updates
function elevator2:propertyUpdate( event )
	print( "Changed " .. event.key .. " to " .. event.value )

	if( event.key == "yVel" ) then
		elevator2:setLinearVelocity( 0, event.value )
		if( math.abs(event.value) < 0.1 ) then
			player2.linearDamping = 0
		end
	end
end
elevator2:addEventListener( "propertyUpdate" )

-- Start elevator in 1 second
timer.performWithDelay( 1000,
	function() 
		-- Elevator is going up
		elevator2.yVel = -500
		player2.linearDamping = 20

		-- elevator2:setLinearVelocity( 0, elevator2.yVel )

		transition.to( elevator2, { yVel = 0, time = 1500, transition = easing.inOutSine } )
	end )


1 Like

Thanks man, really appreciate the help. Looks like it solves the problem will try it and let you know if it works for us. Thanks for the nice little demo you made for us.