Board game simulation (physics)

Just some notes…

-tan(touch.deltaY / (body.x - touch.x))


          touch                    body.position(center)




          touch.deltaY

http://en.wikipedia.org/wiki/Angular_velocity

Has the proper description bit I don’t understand a single word.

Well, for touch causing spin you have 2 options as I see it, and it depends what you want to achieve in the ultimate game/app.

If the goal is for wherever you touch the object to cause spinning then rather than doing all the math yourself, you can try using applyForce.
If you replace the line in touched that sets the linearVelocity instead with something like:

bodies[bodyTouches[touch.id]].body:applyForce(vec2(touch.deltaX, touch.deltaY)/DeltaTime, vec2(touch.x, touch.y))

That works, the first vector is how much force to apply, currently that quick version applies much less acceleration than the previous approach. The second vector is a location on the screen to apply the force based on, in my example this means that while the touch is within the object it’s fairly sensible, once your touch gets far away it just makes it spin like crazy because it’s acting like a lever. But the approach should work, it’s just a case of figuring out how to scale the force, and make some assumption on positioning.

If the goal is that the user accurately controls rotation, then you probably want touch movement to work as current, but then detect 2 fingers on an object and rotate it on the basis of the relative movement of the 2 touches.

I was wandering around the physics example project and found it’s touch mechanism interesting, so I added it to this. Basically once touched the objects elastic towards wherever the initiating touch is, the force is applied to where you initially touched the object, and you can touch each end with a finger and control it’s rotation quite precisely.

supportedOrientations(LANDSCAPE_ANY)

function setup()
    iparameter("linearFriction", 1, 100, 15)
    iparameter("angularFriction", 1, 100, 15)
    watch("bodies[1].body.angularVelocity")
    physics.gravity(0,0)
    walls = { 
        physics.body(EDGE, vec2(0, 0), vec2(WIDTH, 0)),
        physics.body(EDGE, vec2(0, 0), vec2(0, HEIGHT)),
        physics.body(EDGE, vec2(WIDTH, HEIGHT), vec2(WIDTH, 0)),
        physics.body(EDGE, vec2(WIDTH, HEIGHT), vec2(0, HEIGHT)),
    }
    for k, v in ipairs(walls) do
        v.restitution = .7
    end 
    bodies = {}
    for i = 1, 5 do
        bodies[i] = Brick()
    end
    bodyTouches = {}
end

function draw()
    background(0, 0, 0, 255)
    processTouches()
    for k, v in ipairs(bodies) do
        applySurfaceFriction(v.body)
        v:draw()
    end
end

function processTouches()
    strokeWidth(5)
    stroke(128,0,128)
    
    local gain = 100.0
    local damp = 5
    for k,v in pairs(bodyTouches) do
        local worldAnchor = v.body:getWorldPoint(v.anchor)
        local touchPoint = v.tp
        local diff = touchPoint - worldAnchor
        local vel = v.body:getLinearVelocityFromWorldPoint(worldAnchor)
        v.body:applyForce( (1/1) * diff * gain - vel * damp, worldAnchor)
        
        line(touchPoint.x, touchPoint.y, worldAnchor.x, worldAnchor.y)
    end
end

function applySurfaceFriction(body)
    --if it's slow, stop it dead
    --movement friction
    if body.linearVelocity:len() < linearFriction * 2 then
        body.linearVelocity = vec2(0,0)
    else
        body.linearVelocity = body.linearVelocity - linearFriction * body.linearVelocity:normalize()
    end
    --spinning friction
    if math.abs(body.angularVelocity) < angularFriction * 2 then
        body.angularVelocity = 0
    else
        if body.angularVelocity > 0 then
            body.angularVelocity = body.angularVelocity - angularFriction
        else
            body.angularVelocity = body.angularVelocity + angularFriction
        end
    end
end

function touched(touch)
    local touchPoint = vec2(touch.x, touch.y)
    if touch.state == BEGAN then
        for k,v in ipairs(bodies) do
            if v.body:testPoint(touchPoint) then
                bodyTouches[touch.id] = {tp = touchPoint, body = v.body, anchor = v.body:getLocalPoint(touchPoint)}
            end
        end
    end
    if touch.state == ENDED then
        if bodyTouches[touch.id] ~= nil then
            bodyTouches[touch.id] = nil
        end
    end 
    if touch.state == MOVING then
        if bodyTouches[touch.id] ~= nil then
            bodyTouches[touch.id].tp = touchPoint
        end
    end
end

---------------

Brick = class()

function Brick:init()
    self.width = 100
    self.height = 50
    self.colour = color(255, 0, 0, 255)

    --self.body = physics.body(CIRCLE, 50)
    self.body = physics.body(POLYGON, vec2(-self.width / 2, -self.height / 2),
        vec2(-self.width / 2, self.height / 2), vec2(self.width / 2, self.height / 2),
        vec2(self.width / 2, -self.height / 2))
    self.body.position = vec2(math.random(WIDTH), math.random(HEIGHT))
    self.body.density = 2
    self.body.restitution = .7
    --self.body.linearDamping = 0.9
    --self.body.angularDamping = 0.9
end

function Brick:draw()
    fill(self.colour)

    --ellipse(self.body.x, self.body.y, 100)
    pushMatrix()
            translate(self.body.x, self.body.y)
            rotate(self.body.angle)
            rect(-self.width / 2, -self.height / 2, self.width, self.height)
    popMatrix() 
end

I tried it and I like it.
Just some quick questions:
In the ‘processTouches’ function you use pairs instead of ipairs. Because we are using the ‘touch.id’ as key I believe ipais would be ok, too?
The applying of the force and the friction is done in the draw function. Before we applied the velocity in the ‘touched’ function. Does that make any difference in the way it will run on devices with different rendering power (speed)? I mean is the draw function called at a fixed rate?

P.S.: I fixed the name ‘porTable’ in the thread start.In case anybody had problems finding the app in the store. Do you like it?

You are right about doing the physics in the draw() function. The draw function will max out at 60fps, however if your app is doing heavy things this will drop, and if you are recording a video it will drop to max 30fps.

To make these calls legit and independant of device speed etc you can use DeltaTime which returns the elapsed time since the previous frame was drawn and you need to scale the forces when it’s running slower.

As to pairs vs ipairs I’ve not really got my head around the difference, ipairs assumes the key is an integer I think, but I’m not sure if it also implies consecutive keys from 1, where as pairs will work with any type of key.

but I’m not sure if it also implies consecutive keys from 1

It does. ipairs starts with key 1 and continues until the value is nil.

Here is an example showing the difference when using “pairs” and “ipairs”. Looks like pairs show all entries while ipairs only shows integer entries.


function setup()
    tab={a="zxcvb",5,b="asdfg",2,c="qazwsx",3,1,d="qwert",6,7,e="edcrfv",8,9}
    
    print("\
print tab using -pairs-")
    for a,b in pairs(tab) do
        print(a,b)
    end
    
    print("\
print tab using -ipairs-")
    for a,b in ipairs(tab) do
        print(a,b)
    end
end



ipairs starts with 1 and continues until it gets a nil value. Try dave1707’s code with:

tab={a="zxcvb",5,b="asdfg",2,c="qazwsx",nil,1,d="qwert",6,7,e="edcrfv",8,9}

then with ipairs you get:

1    5
2    2

which is why although the touch ids are integers, you can’t access them with ipairs.

So: ipairs is good for ordered lists indexed by integers with no gaps. pairs works with unordered lists with arbitrary keys.

If using ipairs then you need to make sure that you use table.remove to get rid of an entry since that then shifts the indices of all higher entries down. With pairs you can do tab[index] = nil to remove a single entry.

Ok, mixed something up. For our simple list of bodies we can use ipairs because we use an increasing integer as index. For our bodyTouches list we use the touch.id as key. I had a look and the touch id is something like
5.48089e+08
5.48111e+08
5.47151e+08
So it is an integer but it’s totally mixed. We have to use pairs instead.
I checked it and ipairs is not enumerating through the list.

And thanks for the hint about the removal of entries that is very good to know.