How do you 'flick' something?

This has been bugging me for about a week…how do you go about ‘flicking’ something? I want to make a simple 2D program that allows someone to select a point on the ipad and ‘flick’ towards a goal. Various 2D objects will be in the course (so, the physics classes will be used), and the ‘puck’ will bounce around.

Seems simple enough, but my rather poor flick code just isn’t producing any results. I get barely moving circles, not speedy flicked ones…

Borrowing from the physics sample I wrote this snippet. (Zoom library in use)


    if touch.state == BEGAN then
        startPos =zoom:getWorldPoint(vec2(touch.x, touch.y) )
     End

    if touch.state== MOVING then
        pt =zoom:getWorldPoint(vec2(touch.x, touch.y) )
    end

   if touch.state==ENDED then
        local sz=30
        local gain = 2.0
        local damp = .5
        cir = createCircle(startPos.x, startPos.y, sz) --creates a physics circle
        local diff = vec2(touch.x, touch.y) - startPos
        local vel = cir:getLinearVelocityFromWorldPoint(pt)
        cir:applyForce( (1/1) * diff * gain - vel * damp, vec2(touch.x, touch.y))         

Why don’t you use a calculation with touch.delta... and say of the speed is above z pixels then it is a flick?

Getting flicks right is tricky. One big problem is that the final touch can have ended at any time in the last draw cycle. If it ended right at the start then the recorded position will be only slightly different to the previous position. This is then problematic if you compute the velocity as that will assume that the two events are a whole number of cycles apart. What you can do is compute the last-but-one velocity, or you can compute a “running” velocity. But you will need to use ElapsedTime (or DeltaTime) because the touch events might not come in one every draw cycle so you need to record. both the position and the time for the velocity computations.

Here’s my velocity code:

function Touch:instVelocity()
    if self.deltatime > 0 then
        return vec2(
            self.touch.deltaX/self.deltatime,
            self.touch.deltaY/self.deltatime
            )
    else
        return vec2(0,0)
    end
end

function Touch:meanVelocity()
    if self.updatedat > self.createdat then
        return vec2(
            self.touch.x - self.firsttouch.x,
            self.touch.y - self.firsttouch.y
            )/(self.updatedat - self.createdat)
    else
        return vec2(0,0)
    end
end

function Touch:velocity()
    local dt = 0
    local dx = 0
    local dy = 0
    local i
    local n = 0
    local rec = false
    local vel = self.velocities
    for k,v in ipairs(vel) do
        n = n + 1
        if v[3] > ElapsedTime - 2*self.velDelta then
            rec = true
        end
        if i and v[3] > ElapsedTime - self.velDelta then
            rec = false
        end
        if rec then
            i = k
        end
    end
    if i == 0 then
        i = 1
    end
    dt = ElapsedTime - vel[i][3]
    if dt == 0 then
        return vec2(0,0)
    end
    dx = vel[n][1] - vel[i][1]
    dy = vel[n][2] - vel[i][2]
    return vec2(dx/dt,dy/dt)
end

These a part of my extended Touch class (so uses extra information than just the in-built touch object). instVelocity computes the velocity between the previous and current touch points so is subject to the above error. meanVelocity is the average velocity of the entire touch, from first event to current. velocity is a little more complicated. It finds a touch event in the current touch that is within a particular time period ago (as best it can) and computes the velocity between that and the current event. I find this works quite well.

@andrew_stacey, I’ve looked at your touch class, and it’s a bit heady…it’s good to know that this problem wasn’t as simple as I originally thought…I thought that I was making more out of it than was there. Good to see that I wasn’t wrong!

@jordan, I forgot there was a touch.delta. It’ll be good to see the new version of Codea with integrated help…

Hi Aciolino. Is this what you want?
http://www.youtube.com/watch?v=hJCDFwwjjHE
If yes then the code is to define the 3 following functions to pass your pressed, moved and released events:

    controller6 = OneTouch{
        pressed = function(v) 
                    controller6.lastPos = v
                    controller6.speed = vec2(0,0)
                    speed = vec2(0,0)
                    steer = 1
                    pos = v
                end,
        moved = function(v) 
                    pos = v                    
                    local dv = (v-controller6.lastPos)/DeltaTime
                    controller6.lastPos = v
                    controller6.speed = controller6.speed*0.8 + dv*0.2
                end,
        released = function(v) 
                    speed = controller6.speed
                end,
        x0=0.41,x1=0.8,y0=0,y1=0.49,name="one touch => flick",nameSide="bottom"}

When you release your finger, ‘speed’ gives you the speed at this moment. You can grab the code there:
https://gist.github.com/4067678
There are many contollers in this file, so check for the ‘contoller6’ example in the main.

‘speed’ gives you the speed at this moment

Well, not quite. The value returned by speed is some amalgamation of all the speeds that have been read so far. At least, if I’m reading the code correctly. Suppose that there were three events: began, moved, and ended. Then at began the velocity is set to (0,0). At moved, it is set to (0,0)*.8 + dv*.2 where dv is the velocity of that event. Finally, at ended it is set to the previous value of the speed.

So if the touch was (0,0) -> (10,0) -> (12,3) and time was such that DeltaTime = 1 then our reported velocity would be (2,0) = .8*(0,0) + .2*(10,0).

This is correct. The goal of the .8* old+ 0.2*new is to smooth a little bit the speed over the 5 last values: it avoids the random problems you described above: the speed is then consistent with recent finger movement. It works fine (grab the code and check it). If you want to be really accurate, it is not exactly the 5 last: it is a time moving average with exponential memory loss of the past.

Yes, the general formula is:

v_n = \\sum_{j=1}^n .8^{n-j} * .2*u_j

(Since u_0 = 0)

What you can’t do with that is move an object into position and then flick it (which was the problem I encountered when I tried a naive flick method). Moreover, for that to work properly then you need to assume that DeltaTime is (effectively) constant. The average of the speeds is not the average speed!

Actually i do move it into the pos i want nd then flick it. Run the code, youll see it woks not too bad i think.

That is pretty much what I am looking for.

So…I’ve seen the VirtualStick / VirtualSlider stuff before…did you write OneTouch and TwoTouch yourself?

Yes. Actually I’ve started with some of Nat’s controllers, modified them a lot, and added several new interracting tools to the program, as i needed them: menus, info, oneTouch, twoTouch and confirmBox. I use this project to stock and test all those utilities and i use them as a dependency in my other projects.

.@aciolino btw, i remember you had the project to write some code to save/load 3d objects. Did you finilize it?

Yes, I did. I have an HTTPLoader class that can read and store. Local 3d objects. It doesn’t create 3d objects though. There is a cool tool out here that does CSG; that code is pretty. Awesome for creating 3d primitives! I use that often for placeholders,

I don’t have a gist or source backup (!) of my files yet, but glad to share…

Btw: what did you make TwoTouch for? Not sure i see the application.

TwoTouch i use: 1 touch = move, 2 touch = pinch,zoom,rotate around z axis (i use this in my planet simulation)

well if you give me a link to your library to save/load 3d data, i’ll be glad to get the code if you allow me to use it!

Here is a little game I created awhile ago. It uses a flick of the finger to shoot white balls at a red one. The object is to push the red ball through the red line. I’m not sure how accurate the flick controls the white balls as far as speed, but it seems to work OK here with very little math involved.


    supportedOrientations(PORTRAIT)
    displayMode(FULLSCREEN)
    lineCapMode(SQUARE)

function setup()
    tab={}
    rcount=0
    scount=0
    screated=false
    rcreated=false
    createEdges()
end

function draw()
    background(40, 40, 50)
    showLines()
    showText()
    drawEdge()
    drawRandom()
    drawShots()
end

function showLines()
    stroke(0,255,0)
    strokeWidth(4)
    line(0,300,WIDTH,300)
    stroke(255,0,0)
    strokeWidth(20)
    line(300,HEIGHT,450,HEIGHT)  
end

function showText()
    fontSize(40)
    fill(255,0,0)
    if not rcreated then
        text("Double tap screen for red ball.",WIDTH/2,HEIGHT/2+300)
    end
    str=string.format("Goals %4d            Shots %4d",rcount,scount)
    fontSize(20)
    fill(255)
    text(str,WIDTH/2,HEIGHT/2-50)
    str=string.format("Score %6.4f         (Goals / shot)",rcount/scount)
    text(str,WIDTH/2,HEIGHT/2-100)
    text("Flick shots from below this line to push",WIDTH/2,320)
    text("the red ball through the red line at the top.",WIDTH/2,280)   
end

function createEdges()
    e1=physics.body(EDGE,vec2(0,0),vec2(0,1200))
    e1.type=STATIC
    e2=physics.body(EDGE,vec2(WIDTH,0),vec2(WIDTH,1200))
    e2.type=STATIC  
    e3=physics.body(EDGE,vec2(0,HEIGHT),vec2(300,HEIGHT))
    e3.type=STATIC 
    e4=physics.body(EDGE,vec2(450,HEIGHT),vec2(WIDTH,HEIGHT))
    e4.type=STATIC             
end

function drawEdge()
    stroke(255)
    strokeWidth(10)
    line(0,0,0,HEIGHT) 
    line(WIDTH,0,WIDTH,HEIGHT) 
    line(0,HEIGHT,300,HEIGHT)
    line(450,HEIGHT,WIDTH,HEIGHT)
end

function createRandom()
    r1=physics.body(CIRCLE,30)
    r1.gravityScale=.5
    r1.x=math.random(WIDTH) 
    r1.y=HEIGHT-200
    rcreated=true
end  

function drawRandom()
    if rcreated then
        strokeWidth(0)
        fill(255,0,0)
        ellipse(r1.x,r1.y,60,60)
        destroyRandom()
    end
end

function destroyRandom()
    if r1.y < 0 or r1.y>HEIGHT then
        rcreated=false       
        tab={}
        if r1.y>HEIGHT then
            rcount = rcount + 1
        end        
        r1:destroy()
    end 
end 

function createShots()
    scount = scount + 1
    z=#tab+1
    tab[z]=physics.body(CIRCLE,15)
    tab[z].gravityScale=-.1
end
    
function drawShots()
    fill(255) 
    for z=1,#tab do
        ellipse(tab[z].x,tab[z].y,30,30)
    end
    destroyShots()
end

function destroyShots()
    for z=1,#tab do
        if tab[z].y > 1000 or tab[z].y < 0 then
            tab[z]:destroy()
            table.remove(tab,z)
            return
        end
    end
end   

function touched(t)
    if not rcreated then
        if t.tapCount==2 then
            createRandom()
        end
        return
    end
    if t.state==BEGAN and t.y< 300 and rcreated then
        createShots()
        screated=true
        z=#tab
        tab[z].x=t.x
        tab[z].y=t.y
        x1=t.x
        y1=t.y
    end
    if t.state==MOVING and screated then
        if #tab>0 then
            z=#tab
            tab[z].x=t.x
            tab[z].y=t.y
        end
    end
    if t.state==ENDED and screated then
        screated=false
        if #tab>0 then
            z=#tab
            x2=t.x
            y2=t.y
            xd=x2-x1
            yd=y2-y1
            tab[z].linearVelocity=vec2(xd*5,yd*5)
        end
    end    
end

you see that “*5” in your code? I did the same thing, too…seems that “5” was a magic number for both of us. Nt quite sure “why” 5 works well.

I tried playing with the default gravity and varous proerties of the physics object, but nothing really worked as well as just cramming a 5 in there.

@JVM38 - working on the HTTP / 3D upload concept. I’ll be able to do it pretty soon (a few days); I’m just cleaning up the Dropbox Upload code I have.

@aciolino

I originally tried doing all kinds of calculations, but nothing seemed to work right. I then just multiplied it by a number ranging from 2 to 10 and 5 was the most realistic.

.@aciolino thanks! Can you only save/load via http? Not locally on the ipad?