How to constraint physics to 1 dimension?

Is there any way to constraint the physics of a body to 1 dimension, for example the Y axis, without setting its position artificially? What I’d like to have is an object that only moves vertically while interacting with other objects that move freely in 2D. Is this possible without incurring in the teleporting problem described here: http://twolivesleft.com/Codea/Talk/discussion/1731/draggable-physics-objects/p1 ?

You could try setting the body’s linearVelocity.x property to 0 each draw. Might work but without code I can’t test

@West I tried that already and it didn’t work.

@LightDye - if your objects are simple enough, you could roll your own physics code. I have a series of posts on this.

There’s no need to roll your own physics, I believe a PRISMATIC joint should do the trick.

Thank you all for replying. @toadkick could you please elaborate further? I’m experimenting with the code below. A ball will be created at touch point. I’d like to keep the ball at the same X position while it is pushed upwards by the weaves


function setup()
    parameter.number("speed", 0, 50, 5)
    parameter.integer("segments", 1, 100, 5)
    parameter.number("fraction", 0.1, 1, 1)
    parameter.number("height", 0, HEIGHT, HEIGHT/2)
    parameter.boolean("debug", false)
    parameter.watch("a")
    r = 20
end

function draw()
    background(40, 40, 50)
    
    strokeWidth(2)
    stroke(255, 255, 255, 255)
    fontSize(15)
    
    if ball ~= nil then
        ellipse(ball.x, ball.y, 2*r)
    end
    
    calculate()
    doEdges()
    
    for _,edge in ipairs(edges) do
        local p1 = edge.points[1]
        local p2 = edge.points[2]
        line(p1.x, p1.y, p2.x, p2.y)
    end
    
    if debug then
        debugDraw()
    end
end

function debugDraw()
    pushStyle()
        stroke(255, 0, 0, 255)
        p1 = points[1]
        for i = 2,#points do
            p2 = points[i]
            line(p1.x, p1.y, p2.x, p2.y)
                
            fill(255, 0, 0, 255)
            ellipse(p1.x, p1.y, 30)
            fill(255, 255, 255, 255)
            text(i-1, p1.x, p1.y)
            
            p1 = p2
        end
    popStyle()
end

function calculate()
    deltaX = WIDTH / segments
    deltaA = 2 * math.pi / segments * fraction
    points = {}
    a = math.fmod(ElapsedTime * speed / math.pi, 180)
    for x = 0,WIDTH*2,deltaX do
        local y = math.sin(a) * height / 2 + height / 2
        table.insert(points, vec2(x,y))
        a = a + deltaA
    end
end
    
function doEdges()
    destroyEdges()
    physics.pause()
    edges = {}
    p1 = points[1]
    for i = 2,#points do
        p2 = points[i]
        table.insert(edges, doEdge(p1, p2))
        p1 = p2
    end
    physics.resume()
end

function doEdge(p1, p2)
    local edge = physics.body(EDGE, p1, p2)
    edge.type = STATIC
    return edge
end

function destroyEdges()
    if edges ~= nil then
        physics.pause()
        for _,edge in ipairs(edges) do
            edge:destroy()
        end
        edges = nil
        collectgarbage()
        physics.resume()
    end
end
    
function touched(touch)
    if touch.state == ENDED then
        ball = physics.body(CIRCLE, r)
        ball.x = touch.x
        ball.y = touch.y
        
        ball.gravityScale = 1
        ball.restitution = 0
    end
end

@LightDye I tried using PRISMATIC as suggested by@toadkick, but I couldn’t get it to work right. So I tried using EDGE. Try the touched function I have below.


function touched(touch)
    if touch.state == ENDED then
        ball = physics.body(CIRCLE, r)
        ball.x = touch.x
        ball.y = touch.y
        ball.gravityScale = 1
        ball.restitution = 0
        
        e1=physics.body(EDGE,vec2(touch.x-20,0),vec2(touch.x-20,HEIGHT))
        e2=physics.body(EDGE,vec2(touch.x+20,0),vec2(touch.x+20,HEIGHT))
    end
end

I use prismatic for the slider joint in my builder game, it is exactly what you are looking for, it works like this, physics.joint(PRISMATIC,bodyA,bodyB,position,direction) the direction you specify determines which way the body constrained can travel (in all cases of physics.joint bodyB is affected) directly. This can create the piston effect and many other fixed direction travel possibilities.

@Luatee That’s exactly what I tried doing and the ball just got pushed off the screen by the wave. Maybe I wasn’t doing something right, there wasn’t any documentation on how the direction was supposed to be used.

@dave1707 it isn’t covered that well in codea reference so I checked the box2d reference for how it worked, it’s very versatile and bug free from my experience, allowing you to set a lower limit and an upper limit to constrict its movement along the 1d axis

@LightDye @dave1707 @Luatee: “Test2” in the “Physics Lab” example project shows how to set up a prismatic joint.

Here’s the setup() function for instant gratification:

function Test2:setup()
    createGround()
    
    local circle = createCircle(WIDTH/2, HEIGHT/2, 25)
    circle.type = STATIC
    
    local box = createBox(WIDTH/2, HEIGHT/2 - 75, 25, 150)
    
    local joint = physics.joint(REVOLUTE, circle, box, circle.position)
    debugDraw:addJoint(joint)
    
    local circle2 = createCircle(WIDTH/2, HEIGHT/2 - 125, 25)
    local distJoint = physics.joint(DISTANCE, box, circle2, box.position, circle2.position)
    debugDraw:addJoint(distJoint)
    
    local box2 = createBox(WIDTH/2 + 100, HEIGHT/2, 50, 50)
    local sliderJoint = physics.joint(PRISMATIC, circle, box2, box2.position, vec2(1,0))
    sliderJoint.enableLimit = true
    sliderJoint.upperLimit = 50
    debugDraw:addJoint(sliderJoint)
end

The prismatic joint sliderJoint is set up so that it connects the dynamic body box2 to the static body circle, anchored at box2’s initial position, and constrained to the x axis (vec2(1,0)). Additionally, it is set up to limit the translation to 50 pixels along the positive axis direction.

@toadkick Were you able to get that to work in the above program. I had the prismatic joint set up similar to that, but the ball was still pushed off the screen by the wave. In a test program, I had a joint set up that kept a bouncing ball going at a 45 degree angle. So the joint was done correctly, but when I tried something similar but only going up/down, it didn’t work with the wave.

@LightDye @toadkick Change the touched() function to look like this. This uses the prismatic joint which I used in my test program that worked. When I originally added it to this program, I left out the STATIC line of code which didn’t work. This one seems to work.


function touched(touch)
    if touch.state == ENDED then
        ball = physics.body(CIRCLE, r)
        ball.x = touch.x
        ball.y = touch.y
        ball.gravityScale = 1
        ball.restitution = 0
        b=physics.body(CIRCLE,10)
        b.x=touch.x
        b.y=HEIGHT
        b.type=STATIC
        j=physics.joint(PRISMATIC,b,ball,b.position,vec2(0,1))
    end
end

Thanks @dave1707 and @toadkick, based on your lines I added the ones below. That keeps the ball in the screen but the joint acts as a rubber band stretching until the waves let the the joint pull the ball back to its original position. Still not the desired effect, but certainly an improvement.

        anchor = physics.body(CIRCLE, 1)
        anchor.position = vec2(touch.x, touch.y)
        anchor.type = STATIC
        constraint = physics.joint(PRISMATIC, anchor, ball, ball.position, vec2(0,1))
        constraint.enableLimit = true
        constraint.lowerLimit = 0
        constraint.upperLimit = 10

@LightDye In your PRISMATIC line of code, you have the anchor parameter set to the ball position. The anchor point should be anchor.position. Try the code below.


constraint = physics.joint(PRISMATIC, anchor, ball, anchor.position, vec2(0,1))

@dave1707, thanks for keeping giving me ideas. I set the joint position to the anchor position but it didn’t make any difference because it was exactly the same initial position of the ball. Then, as per your example, I changed the anchor position to be at HEIGHT but it didn’t make much difference, except that when I debug-draw the joint it doesn’t move for a while until the ball is taken out of the screen by the wave and from there it was acting as a rubber band hanging from the anchor point at the top of the screen.

@LightDye Comment out the 3 Limit lines of code and try again.