Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Splinerider

edited May 2013 in General Posts: 1,595

My take on the flash game line rider using a ball and splines, check it out!

Tell me what you think

«1

Comments

  • Really great work ;)

  • Posts: 1,595

    I think the video is being processed atm ;) it'll be up in a few minutes

  • Posts: 37

    Pretty cool, nice work!

  • edited May 2013 Posts: 1,595

    Thanks I've added setting a marker and swipe off screen to delete and a few other things a bit smaller than that but seem to boost the overall performance by about 100 times by using meshes instead, gives quite a nice look but I prefered the lines with a cap on them, anyway heres the newer one:

  • Posts: 5

    very cool!

  • Posts: 1,222

    Very nice. Clean interface and nifty functionality.

  • Posts: 372

    That is awesome. I love the way you made those curvy shapes always been thinking how to make them. Now that you've made them I atleast know that there's a way to do it!!

  • Posts: 1,595

    For your convenience heres how I do it:


    -- spline -- Use this function to perform your initial setup function setup() Splines = {} table.insert(Splines,Spline({vec2(200,500),vec2(300,400),vec2(400,400),vec2(500,500)})) scr = vec2() touches = {} end function angleOfPoint( pt ) local x, y = pt.x, pt.y local radian = math.atan2(y,x) local angle = radian*180/math.pi if angle < 0 then angle = 360 + angle elseif angle > 360 then angle = 0 + (angle-360) end return angle end function touched(t) for k,v in pairs(Splines) do v:touched(t) end end -- This function gets called once every frame function draw() -- This sets a dark background color background(161, 161, 161, 255) -- This sets the line thickness strokeWidth(5) -- Do your drawing here for k,v in pairs(Splines) do v:drawPoints() v:draw() end end ------------------ Points = class() function Points:init(v) self.m = mesh() self.r = {} for i=1,#v*2 do if i <= 4 then self.p = v[i] self.r[i] = self.m:addRect(self.p.x,self.p.y,10,10,0) --self.texture = readImage("Documents:whitecircle") self.m:setColors(50,50,50,255) elseif i > 4 then self.p = v[i-4] self.r[i] = self.m:addRect(self.p.x,self.p.y,10,10,45/57.3) self.m:setColors(50,50,50,255) end end end function Points:draw() self.m:draw() end function Points:touched(touch) -- Codea does not automatically call this method end -------------- Spline = class() function Spline:init(v) -- you can accept and set parameters here self.v = v self.holding = {} self.moving = nil self.verts = {} self.newv = {} local n = 31 local t = 0 for i = 1,n do self.verts[i] = (1-t)^3 * v[1] + 3*(1-t)^2 *t * v[2] + 3*(1-t)* t^2 *v[3] + t^3*v[4] t=t+1/(n-1) end self.lines = Lines(self.verts,50) self.vlines = Lines(self.v,200) self.points = Points(self.v) self.spline = physics.body(CHAIN,false,unpack(self.verts)) end function Spline:draw() self.lines:draw() end function Spline:drawPoints() self.vlines:draw() self.points:draw() self.p = vec2() for i = 1,#self.v do local pos = self.v[i] self.p = self.p + pos end strokeWidth(2) stroke(255,200) self.p = self.p/4 fill(179, 21, 21, 150) ellipse(self.p.x,self.p.y,30) end function Spline:pos() return self.p end function Spline:touched(t) if t.state == BEGAN then for i=1,#self.v do local pos = self.v[i]+scr if vec2(t.x,t.y):dist(pos) < 25 then self.holding[i] = true end end if vec2(t.x,t.y):dist(self.p+scr) < 25 then self.moving = 1 for i=1,#self.v do self.newv[i] = (self.v[i]-self.p) end end for i=1,#self.v do if self.v[i]:dist(self.p) < 20 then self.v[i] = self.v[i] + (self.v[i]-self.p):normalize()*20 end end elseif t.state == MOVING and self.moving == 1 then tp = vec2(t.x,t.y) for i=1,#self.v do self.v[i] = tp + self.newv[i]-scr end local t = 0 local n = 31 local v = self.v for i = 1,n do self.verts[i] = (1-t)^3 * v[1] + 3*(1-t)^2 *t * v[2] + 3*(1-t)* t^2 *v[3] + t^3*v[4] t=t+1/(n-1) end self.lines = Lines(self.verts,50) self.vlines = Lines(self.v,200) self.points = Points(self.v) elseif t.state == ENDED and self.moving == 1 then self.moving = nil end for i=1,#self.v do local pos = self.v[i] if t.state == MOVING and self.holding[i] then touches[2] = t tp = vec2(t.x,t.y) self.v[i] = tp-scr local t = 0 local n = 31 local v = self.v for i = 1,n do self.verts[i] = (1-t)^3 * (v[1]) + 3*(1-t)^2 *t * (v[2]) + 3*(1-t)* t^2 *(v[3])+ t^3*(v[4]) t=t+1/(n-1) end self.lines = Lines(self.verts,50) self.vlines = Lines(self.v,200) self.points = Points(self.v) elseif t.state == ENDED and self.holding[i] then self.holding[i] = nil end end end ------------------ Lines = class() function Lines:init(v,col) self.m = mesh() self.r = {} for i=1,#v do if i > 1 then self.p = (v[i*1-1]+v[i*1])/2 self.dir = (v[i*1]-v[i*1-1]):normalize() self.len = (v[i*1]-v[i*1-1]):len() self.r[i] = self.m:addRect(self.p.x,self.p.y,self.len+2,5,angleOfPoint(self.dir)/57.3) self.m:setColors(col,col,col,255) end end end function Lines:draw() self.m:draw() end function Lines:touched(touch) -- Codea does not automatically call this method end

    I plan to make a game out of it but its not that hard to make a game when you've got that!
    Also note you can only use a table of four vertices in the Spline(v) function.

    I hope you enjoy it and to make more curves just add them to the table of Splines.

  • Posts: 1,595

    Btw it runs about 60fps even with about 20 on the screen, if I bothered to use meshes with the ellipse in the middle I doubt it would go down at all until about 40

  • Posts: 372

    Thanks @Luatee. I'll look into it.

  • edited May 2013 Posts: 372

    Thanks @Luatee for the code. I hope you don't mind if I borrow your curves equation. Making an equation that too cubic will be difficult for me. Here's the code I wrote though its not exactly the same but its similar. (Long hold on new project and paste into project)



    --# Main function setup() p = Points(WIDTH/2-100,HEIGHT/2) BallSetup() parameter.boolean("start",false) end function draw() background(255) p:draw() BallDraw() if start then physics.resume() elseif not start then physics.pause() end end function touched(touch) tx = touch.x ty = touch.y touchPoint = false p:touched(touch) BallTouch(touch) end --# Points Points = class() function Points:init(x1,y1) self.px = {} self.py = {} self.x1 = x1 self.y1 = y1 self.x2 = self.x1+100 self.y2 = self.y1-100 self.x3 = self.x2+100 self.y3 = self.y2 self.x4 = self.x3+100 self.y4 = self.y3+100 n = 30 end function Points:draw() pushStyle() fill(127, 127, 127, 255) ellipse(self.x1,self.y1,15) ellipse(self.x2,self.y2,15) ellipse(self.x3,self.y3,15) ellipse(self.x4,self.y4,15) popStyle() local t = 0 for i=1,n do self.px[i] = (1-t)^3 * (self.x1) + 3*(1-t)^2 *t * (self.x2) + 3*(1-t)* t^2 *(self.x3)+ t^3*(self.x4) self.py[i] = (1-t)^3 * (self.y1) + 3*(1-t)^2 *t * (self.y2) + 3*(1-t)* t^2 *(self.y3)+ t^3*(self.y4) t=t+1/(n-1) end for i=1,n-1 do pushMatrix() pushStyle() stroke(0, 0, 0, 255) strokeWidth(5) line(self.px[i],self.py[i],self.px[i+1],self.py[i+1]) self.body = physics.body(EDGE,vec2(self.px[i],self.py[i]), vec2(self.px[i-1],self.py[i-1])) popStyle() popMatrix() end pushStyle() stroke(73, 73, 73, 80) strokeWidth(7) line(self.x1,self.y1,self.x2,self.y2) line(self.x2,self.y2,self.x3,self.y3) line(self.x3,self.y3,self.x4,self.y4) popStyle() end function Points:touched(touch) if tx>self.x1-30 and tx<self.x1+30 and ty>self.y1-30 and ty<self.y1+30 then touchPoint = true self.x1 = tx self.y1 = ty end if tx>self.x2-30 and tx<self.x2+30 and ty>self.y2-30 and ty<self.y2+30 then touchPoint = true self.x2 = tx self.y2 = ty end if tx>self.x3-30 and tx<self.x3+30 and ty>self.y3-30 and ty<self.y3+30 then touchPoint = true self.x3 = tx self.y3 = ty end if tx>self.x4-30 and tx<self.x4+30 and ty>self.y4-30 and ty<self.y4+30 then touchPoint = true self.x4 = tx self.y4 = ty end end --# Ball function BallSetup() ball = physics.body(CIRCLE,20) ball.x = WIDTH/2 ball.y = HEIGHT/2 ball.restitution = 0 ball.friction = 0 ball.gravityScale = 2 end function BallDraw() pushStyle() fill(255, 0, 0, 255) stroke(0, 0, 0, 255) strokeWidth(3) ellipse(ball.x,ball.y,40) popStyle() end function BallTouch(touch) if not touchPoint then ball.x = tx ball.y = ty end end
  • edited May 2013 Posts: 1,595

    Just checked that out thats a good way of doing it but make sure when you're holding the ball to set its velocity to vec2(0,0) the BallTouch function but other than that its a good way of doing it, I prefer to keep it in the class though as its easy to create many of these curves.

    Also the equation itself is standard so feel free to use it, but if you create a game using the classes then all I ask is credit, thanks!

  • Posts: 372

    Yeah I made the game using classes I just posted this to show it to you. Thanks for the idea and main part of the games code @Luatee. But now I am into a problem how should I move the screen along with the ball. I am trying to use translate but it's not working. I tried using FPS but then the movement was becoming jerky. I'll be grateful if you could help.

  • Posts: 372

    @Luatee how did you move the splines along with the cycle without using FPS I did as in the code below. But the movement becomes jerky and inaccurate as I am using DeltaTime. Could you help me out??https://gist.github.com/Saurabh96jun/5655889

  • edited May 2013 Posts: 1,595

    Well the red circle in the middle (The one I assume you're talking about), I just found the midpoint of all the vectors (the average) so you divide all the points by 4 assuming you have 4 points in total. Then create variable in the class called self.moving and then set it to true in the touched function using a statement such as if touchpos:dist(self.p) < 25 then blah blah end, then create a set of new vectors (we'll call them newvecs, which is a table of four vectors) which consist of each vertex/point minus the midpoint (I call the midpoint self.p) and then move self.p to your touchpos and then add the contents of newvecs to self.p and set each vertex to this new value and it should work. If you dont understand the above then feel free to send me a message me on here.

  • Posts: 372

    Thanks @Luatee

  • Posts: 1,595

    Here's a newer version what do you guys think?
    I've added a few things and I think its overall better than before..

  • SimeonSimeon Admin Mod
    Posts: 4,538

    That looks fantastic @Luatee. I love the little pedalling animation you have going on the bike rider. It seems to correlate to the speed of the bike, nice touch.

  • Posts: 1,595

    Thanks Simeon, I've mainly been experimenting with adding realistic motion to the body and such but also get the legs in sync, I have made it to go to how much he would realistically need to pedal using the spacing of splines Bézier curve points. But yeah Im going to try and get most of it sorted and maybe make it a bit less grey haha looks quite depressing atm

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Saurabh - re your query on moving the screen, i think you just need to translate your position. The Lua jump demo project (the little girl jumping on clouds) does this, and moves the screen as the girl jumps, in the draw function.

  • Posts: 139

    I kinda want to know whole code just to play with it because it looks fun is it alright if you pm it to me.

  • Posts: 1,595

    I posted the code for the basics from above, its pretty easy to go on from there, I'm going to try to make it in to a game first before I give out my code.

  • Posts: 139

    Ok thx

  • edited June 2013 Posts: 372

    Thanks @Ignatz!!

  • Posts: 688

    @Luatee - thanks for sharing the spline code, I was just wondering how best to do that kind of thing.

  • edited January 2014 Posts: 1,595

    I think theres an alternative way of making the curve as there are a few ways of constructing bezier curves between points, this being my favorite, thanks!

  • edited June 2013 Posts: 396

    @Luatee - this is really cool! Think you've got a really nice set of game mechanics there - cant wait to see the final game!

    Incidentally, I've been playing around with splines a fair bit for a game im developing, and im half way through writing a pretty extensive library of classes and functions to help set them up a bit more easily. Basically, it handles an unlimited number of control points, open/closed curves, position and orientation along path etc.. Ill probably put the code up on here at somepoint - but an early test is here:

    vimeo.com/67845937

  • Posts: 1,595

    @andymac3d Thanks! I wanted to put in an unlimited amount of points at the start but I spoke to my friend who I thought might know being a math wizz and all but he said that if I wanted to put more points in I would have to constantly update the equation I use with more points and that seemed like too much but I suppose it wouldn't be too bad thinking about it now. You're class looks really good, but what I've been dealing with is FPS drop from drawing too much, how do you deal with this or have you not come to it yet?

  • Posts: 396

    Hi @Luatee - I get around some of the performance issues by pre-computing as much as possible (via Tables) and only updating things when 'something' changes.

    This is quite important when using the 'Piece-wise' method of joining together Beziers that i've used, as obviously there can be a fair few iterations which can degrade the FPS, although I've got some pretty good performance so far with a reasonably large number of points. I guess the worse case is if all your control points are moving constantly, then naturally you'd have to continually recompute.

    Similarly, the number of parametric samples you use per span (ie. from 0 -> 1) to draw a smooth curve segment is also important. I'm using 30 samples which gives a reasonable approximation - although I guess increasing this will drop the FPS accordingly.

    For most uses, where a sprite needs to follow a simple 'invisible' static path then most of this is not an issue - as you can pre-compute most of this and not even draw the curve at all - its then basically a simple 'lookup' of where it is on the curve to return x,y coords which is pretty fast!

  • Posts: 2,161

    @Luatee and @andymac3d Interesting discussion. I've a fair bit of code scattered around relating to drawing curves that I'd be happy to share if you wanted (but I don't want to tread on any toes ...). In short:

    1. The Roller Coaster project included in Codea defines a parametrised track, but then it reparametrises it according to arc length to make it easier to make the motion more realistic.
    2. The Harmony drawing program draws its lines and curves using a mesh. It goes to some length to make the lines look nice and not just a sequence of disjointed lines. In particular, it uses a smoothing algorithm to add more points to the sequence (based on John Hobby's algorithm) and it handles the line joins.
    3. I also have a bezier shader. This is a mesh that uses a vertex shader to warp it to a cubic bezier shape.
  • edited June 2013 Posts: 1,595

    @Andrew_Stacey I didn't actually think the roller coaster had anything like this in it, but I haven't looked at many of the examples. I remember looking at harmony when I was making a drawing app and I could use a few things such as creating single images for each splines and drawing them, I can see this would improve the performance by 100 fold quite easily. To do this I would need to find the bounding box of a quadratic Bézier curve, in that case how do I do this?

    Also about the shader you mention is this for any shape of cubic? Aswell as that what is the performance like for this compared to normal meshes?

  • Posts: 396

    Nice one @Andrew_Stacey - feel free to chip in! Would be nice to get some feedback as I progress with the Spline-Lib (having a Mathematician on-board is always a good thing! :-) .

    p.s. I really liked the roller-coaster example, I'll check again at how you reparameterised your curve as obviously using simple parametric values across multiple spans with different spacing can cause velocity problems (computing the arc-length is really the proper way to do this)

  • Posts: 2,161

    @Luatee I was about to say that the thing about the roller coaster is that the path isn't drawn, but then I remembered that it is drawn: as the roller coaster track. One of the time-savers there is that the track is static so once it's been computed and the mesh sent off to the GPU then there's no further computations to be done. When drawn as slabs, the track has 3672 vertices and the stars have 9600. The frame rate is around 60fps. I have a variant where I replace the track by a tube with upwards of 72,000 vertices and still a frame rate of about 60fps.

    The exact bounding box of a Bézier curve can be difficult to compute but it is always contained in the convex hull of the control points. So use those (add a little something for line width).

    The shader is for a cubic Bézier in that that's how it is specified. But any cubic segment can be parametrised as a cubic Bézier so that's no hindrance. I actually use it in the Harmony app to do colour curves for images and there I convert between an ordinary cubic and a Bézier cubic.

    The trick with Harmony was to use meshes while a line is being drawn, and then when the user stops drawing it then to render it to an image. Rendering as it is being drawn takes too long, but keeping it all in a single mesh proved complicated as well. By doing it when the user stops drawing then they don't notice if the frame rate dips momentarily.

    @andymac3d I decided to do the reparametrisation using a step-by-step approach. That is, I step along the curve at incredibly small intervals and test to see when I get more than the desired step along and mark that point. Solving the actual arc-length reparametrisation is only possible for "nice" curves and even then it can be a pain to compute so I figured this was a better way to be able to handle all curves.

    What does your library do with curves? (As in, what functionality does it provide?)

  • Posts: 2,161

    @Luatee Incidentally, what would be a reasonable test for my cubic shader? I could throw in a number of shaders with varying parameters and see what frame rates I get. What would be a reasonable number of cubics to draw?

  • Posts: 2,161

    I just tried it with a single mesh (200 interpolation points, so mesh size of 1200 vertices) and redrawing it n number of times where I pass the control points to the GPU on each draw cycle (and I made them vary so that they had to be passed anew to the GPU). I had reasonable FPS for up to n = 150 (reasonable meaning above 50). By 200, the FPS had dropped to the mid 40s. Mind you, by 200 then the whole system is pretty much filling the screen!

  • edited June 2013 Posts: 1,595

    @Andrew_Stacey I'm not really in to 3d at the moment, so I haven't looked at the roller coaster much. Although I did do something similar using a lua coding environment in 3d before where I created the track infront of the train using keys and rotating the direction and such but I have tested the meshes a lot and I found that its more dynamic the mesh then the slower it seems to run but if its just sat there static then it doesn't take up much memory at all.

    Finding the Bézier bounding box shouldn't be hard if I give it the four points, I think what makes it hard is crossing over the points so they aren't in order (from the algorithm I used anyway) so any help off you for that would be great.

    I'm quite interested in the shader you've got for that but it wouldn't be applicable as I'm using a quadratic curve unless its easy to change. For me a reasonable test would be about 100 curves, as I get just over 40fps with about 50 curves on the screen but then when the simulation is run it always runs at 60fps, despite the amount of curves (this will probably / most definitely change with higher numbers, ~100)

    I saw this and I continued to integrate that sort of system of drawing in to my old drawing app which worked out well, the reason why I need to get the bounding box of the spline is to create an image of that size to store it in unless there is a better way for me to do this.

    You're test seems to have better outcomes than mine, but the issue I face is which a frame rate drop when the draw function is running whilst not in simulation. If you have a way of me integrating that it would be useful, although I wouldn't use it in my actual game as I'd like to do the creating of it myself.

  • Posts: 2,161

    Here's my shader with two auxiliary drawing functions for quadratic and cubic beziers. 200 step points (nstep in the code) seems plenty, at 100 then with a full-sized curve you can tell it is segmented, 150 looks fine.

    function setup()
        --displayMode(FULLSCREEN)
        local width = 10
    
        pts = {vec2(width/2,0),
            vec2(width/2,3*HEIGHT-width/2),
            vec2(WIDTH - width/2,-2*HEIGHT+width/2),
            vec2(WIDTH - width/2,HEIGHT)}
    
        --[[
        curves = {}
        local n = 200
        for k=1,n do
            table.insert(curves,{
                function(t) return vec2(0,HEIGHT*(1+math.sin(t + 2*math.pi*k/n))/2) end,
                function(t) return vec2(WIDTH/3,HEIGHT*(1+math.sin(t + 2*math.pi*(k+1)/n))/2) end,
                function(t) return vec2(2*WIDTH/3,HEIGHT*(1+math.sin(t + 2*math.pi*(k+2)/n))/2) end,
                function(t) return vec2(WIDTH,HEIGHT*(1+math.sin(t + 2*math.pi*(k+3)/n))/2) end
                    })
        end
        --]]
        fps = {}
        for k=1,20 do
            table.insert(fps,1/60)
        end
        afps = 60
        parameter.watch("math.floor(20/afps)")
    end
    
    function draw()
        table.remove(fps,1)
        table.insert(fps,DeltaTime)
        afps = 0
        for k,v in ipairs(fps) do
            afps = afps + v
        end
        background(75, 104, 90, 255)
        strokeWidth(5)
    
        stroke(151, 115, 115, 255)
        for i=1,3 do
            line(pts[i].x,pts[i].y,pts[i+1].x,pts[i+1].y)
        end
        fill(160, 172, 22, 255)
        noStroke()
        for i = 1,4 do
            ellipse(pts[i].x,pts[i].y,20)
        end
        strokeWidth(5)
        stroke(255, 255, 255, 255)
        cbezier(pts)
        stroke(81, 255, 0, 255)
        qbezier(vec2(10,10),vec2(WIDTH/2,2*HEIGHT-20),vec2(WIDTH-10,10))
        --[[
        for _,v in ipairs(curves) do
            cbezier({v[1](ElapsedTime),v[2](ElapsedTime),v[3](ElapsedTime),v[4](ElapsedTime)})
        end
        --]]
    end
    
    function touched(touch)
        if touch.state == BEGAN then
        for k,v in ipairs(pts) do
            if v:distSqr(vec2(touch.x,touch.y)) < 900 then
                pt = k
            end
        end
        elseif pt then
            pts[pt].x = touch.x
            pts[pt].y = touch.y
        end
        if touch.state == ENDED then
            pt = nil
        end
    end
    
    local m = mesh()
    m.shader = shader([[
    //
    // A basic vertex shader
    //
    
    //This is the current model * view * projection matrix
    // Codea sets it automatically
    uniform mat4 modelViewProjection;
    
    //This is the current mesh vertex position, color and tex coord
    // Set automatically
    attribute vec4 position;
    attribute vec4 color;
    attribute vec2 texCoord;
    
    //This is an output variable that will be passed to the fragment shader
    varying highp vec2 vTexCoord;
    
    uniform float len;
    uniform float width;
    uniform float blur;
    float twidth = width+blur;
    uniform vec2 pts[4];
    
    void main()
    {
        highp float t = position.y/len;
        highp float tt = 1.0 - t;
        highp vec2 bpos = tt*tt*tt*pts[0] + 3.0*tt*tt*t*pts[1] 
        + 3.0*tt*t*t*pts[2] + t*t*t*pts[3];
        highp vec2 bdir = tt*tt*(pts[1]-pts[0])
             + 2.0*tt*t*(pts[2]-pts[1]) + t*t*(pts[3]-pts[2]);
        bdir = vec2(bdir.y,-bdir.x);
        bdir = twidth*position.x*normalize(bdir);
        bpos = bpos + bdir;
        highp vec4 bzpos = vec4(bpos.x,bpos.y,0,1);
        //Pass the mesh color to the fragment shader
        vTexCoord = vec2(texCoord.x, 1.0 - texCoord.y);
        //Multiply the vertex position by our combined transform
        gl_Position = modelViewProjection * bzpos;
    }
    ]],[[
    //
    // A basic fragment shader
    //
    
    //This represents the current texture on the mesh
    uniform lowp sampler2D texture;
    uniform highp float width;
    uniform highp float blur;
    highp float edge = blur/(width+blur);
    uniform lowp vec4 colour;
    
    //The interpolated texture coordinate for this fragment
    varying highp vec2 vTexCoord;
    
    void main()
    {
        //Sample the texture at the interpolated coordinate
        lowp vec4 col = colour;
        //if (vTexCoord.x < edge)
        //    col.a = col.a*vTexCoord.x/edge;
        //if (vTexCoord.x > 1. - edge)
        //    col.a = col.a*(1.-vTexCoord.x)/edge;
        //Set the output color to the texture color
    
        col.a = mix( 0., col.a,
        smoothstep( 0., edge, min(vTexCoord.x,1. - vTexCoord.x) ) );
        gl_FragColor = col;
    }
    ]])
    
    local nsteps = 200
    for n=1,nsteps do
        m:addRect(0,(n-.5),1,1)
    end
    m.shader.len = nsteps
    m.shader.blur = 2
    
    function cbezier(a,b,c,d)
        if type(a) ~= "table" then
            a = {a,b,c,d}
        end
        m.shader.width = strokeWidth()
        m.shader.colour = color(stroke())
        m.shader.pts = a
        m:draw()
    end
    
    function qbezier(a,b,c)
        if type(a) == "table" then
            a,b,c = unpack(a)
        end
        b = 2*b/3
        m.shader.width = strokeWidth()
        m.shader.colour = color(stroke())
        m.shader.pts = {a,a/3 + b,b + c/3,c}
        m:draw()
    end
    
  • Posts: 1,595

    Thanks @Andrew_Stacey, can see myself playing about with that for a bit! Once ive put it in ill post the results and run some tests on speed and others, might help if I made quadratic splines aswell as cubic, I didnt really think I needed this though as I get a nice quadratic looking curve when I put the points in the middle together, but this sort of curve has a gradient change thats too steep, causing the bike to slow down

  • Posts: 2,161

    @Luatee The qbezier function in my code is a quadratic bezier, and it's the right conversion from a quadratic to a cubic so you'll get exactly the same curve as you would with the quadratic bezier.

  • Posts: 1,595

    I just tried the code @Andrew_Stacey and it works like a f1 car compared to the drawing I had going on before, thanks!

  • Posts: 2,161

    @Luatee Great!

    I'm working on making it into a library of bézier-related commands. I think that there's a slight speed saving by creating a new mesh for a new bézier. That is, if you have a dozen curves and they don't change their parameters then it's quicker to create a dozen separate meshes and keep drawing them than to have one mesh and change its parameters each time.

  • Posts: 1,595

    Sounds like it could be pretty good, and probably more unique than the Bézier classes going about at the moment as the speed is just not comparable with others, aswell as the colours and such. This is a reason I should start shaders

  • Posts: 396

    Yes indeed - yet to touch shaders as yet and would be really interested in a shader oriented Bezier class/library as well - really interested in the general performance speedup!

    I'll continue with my non-shader version for my game, as its less to do with raw display performance - more a convenient/simple/fast method of specifying paths for sprites and other useful functions (e.g. closest point to curve etc..).

    I use the same approach as @Andrew_Stacey and pretty much pre-process all the calculations prior to drawing unless something gets changed. I guess the beauty of a piece-wise approximating (i.e. multiple Quadratic curves with C0/C1 continuity joining them together) is that you dont necessarily have to recompute all the curves just the current 'span' local to where the points have changed. Im sure there are lots of other speed ups like this to help pre-processing the curve prior to draw should interactive performance be an issue. :-)

  • Posts: 2,161

    Quick question: Would either of you (@Luatee and @andymac3d) have a use for a function that computed a sequences of béziers through a given set of points, so that the resulting curve looked smooth? That is, you specify a set of points and there's an algorithm that computes a set of Béziers, one between each successive pair of points, which match up well.

  • Posts: 396

    Absolutely @Andrew_Stacey . This sort of 'inverse' computation of control-points that fit a set of 'knots' that lie on a curve is really useful and far more intuitive than specifying a bunch of CP's that influence the curve directly. Happy days :-D

  • Posts: 1,595

    This would definitely be of use I could see a overhaul of controls in this game with the use of that

  • Posts: 437

    Uhm, I would like to use it in a game :)

  • edited August 2013 Posts: 1,595

    I stopped working on it but here's the code, shouldn't be too hard to integrate its in classes for the most part.

    --# Spline
    Spline = class()
    
    function Spline:init(v)
        -- you can accept and set parameters here
        self.v = v
        self.p = vec2()
        self.msh = mesh()
        self.msh.texture = txture2
        self.rct = self.msh:addRect(0,0,35,35)
        self.msh:setColors(180,0,0,150)
    
        self.holding = {}
        self.moving = nil
        self.verts = {}
        self.newv = {}
        self.n = 30
        local n = self.n
        local t = 0
        for i = 1,n do
            self.verts[i] = (1-t)^3 * v[1] + 3*(1-t)^2 *t * v[2] + 3*(1-t)* t^2 *v[3] + t^3*v[4]
            t=t+1/(n-1)
        end
        self.lines = Lines(self.verts,50)
        self.vlines = Lines(self.v)
        self.points = Points(self.v,200)
        self.p = vec2()
        for i = 1,#self.v do
            self.p = self.p + self.v[i]
        end
        self.p = self.p/4
        self.msh:setRect(self.rct,self.p.x,self.p.y,30,30)
    end
    
    function Spline:draw()
        self.lines:draw()
        if ball == nil then
            self.vlines:draw()
            self.points:draw()
            self.msh:draw()
        end
    end
    
    function Spline:closestPoint()
        return self.cp
    end
    
    function Spline:drawPoints()
    
    end
    
    function Spline:pos()
        return self.p
    end
    
    function Spline:createFloor()
        local verts = {}
        local t = 0
        local n = 60
        local v = self.v
        for i = 1,n do
            verts[i] = (1-t)^3 * v[1] + 3*(1-t)^2 *t * v[2] + 3*(1-t)* t^2 *v[3] + t^3*v[4]
            t=t+1/(n-1)
        end
        self.spline = physics.body(CHAIN,false,unpack(verts))
        self.spline.type = STATIC
        self.spline.friction = 0.2
    end
    
    function Spline:destroy()
        if self.spline ~= nil then
            self.spline:destroy()
            self.spline = nil
        end
    end
    
    
    function Spline:touched(t)
        self.p = vec2()
        for i = 1,#self.v do
            self.p = self.p + self.v[i]
        end
        self.p = self.p/4
        self.msh:setRect(self.rct,self.p.x,self.p.y,30,30)
        if t.state == BEGAN then
            for i=1,#self.v do
                local pos = self.v[i]+scr
                if vec2(t.x,t.y):dist(pos) < 25 then
                    self.holding[i] = true
                end
            end
            if vec2(t.x,t.y):dist(self.p+scr) < 25 then
                self.moving = 1
                for i=1,#self.v do
                    self.newv[i] = (self.v[i]-self.p)
                end
            end
            for i=1,#self.v do
                if self.v[i]:dist(self.p) < 20 then
                    self.v[i] = self.v[i] + (self.v[i]-self.p):normalize()*20
                    self.v[i].x = math.ceil(self.v[i].x)
                    self.v[i].y = math.ceil(self.v[i].y)
                    local t = 0
                        local n = self.n
                        local v = self.v
                        for i = 1,n do
                            self.verts[i] = (1-t)^3 * v[1] + 3*(1-t)^2 *t * v[2] + 3*(1-t)* t^2 *v[3] + t^3*v[4]
                            t=t+1/(n-1)
                        end
                    self.lines = Lines(self.verts,50)
                    self.vlines = Lines(self.v,200)
                    self.points:setpoints(i,self.v)
                end
            end
        elseif t.state == MOVING and self.moving == 1 then
            tp = vec2(t.x,t.y)
                    for i=1,#self.v do
                        self.v[i] = tp + self.newv[i]-scr
                        self.v[i].x = math.ceil(self.v[i].x)
                        self.v[i].y = math.ceil(self.v[i].y)
                    end
                        local t = 0
                        local n = self.n
                        local v = self.v
                        for i = 1,n do
                            self.verts[i] = (1-t)^3 * v[1] + 3*(1-t)^2 *t * v[2] + 3*(1-t)* t^2 *v[3] + t^3*v[4]
                            t=t+1/(n-1)
                        end
                        self.lines = Lines(self.verts,50)
                        self.vlines = Lines(self.v,200)
                        self.points:setpoint(self.v)
                        self.p = (self.v[1]+self.v[2]+self.v[3]+self.v[4])/4
                        self.msh:setRect(self.rct,self.p.x,self.p.y,30,30)
        elseif t.state == ENDED and self.moving == 1 then
            self.moving = nil
        end
        for i=1,#self.v do
            local pos = self.v[i]
            if t.state == MOVING and self.holding[i] then
                touches[2] = t
                tp = vec2(t.x,t.y)
                self.v[i] = tp-scr
                self.v[i].x = math.ceil(self.v[i].x)
                self.v[i].y = math.ceil(self.v[i].y)
                local t = 0
                local n = self.n
                local v = self.v
                for d = 1,n do
                    self.verts[d] = (1-t)^3 * (v[1]) + 3*(1-t)^2 *t * (v[2]) + 3*(1-t)* t^2 *(v[3])+ t^3*(v[4])
                    t=t+1/(n-1)
                end
                self.lines = Lines(self.verts,50)
                self.vlines = Lines(self.v,200)
                self.points:setpoints(i,self.v)
        elseif t.state == ENDED and self.holding[i] then
                self.v[i].x = math.ceil(self.v[i].x)
                self.v[i].y = math.ceil(self.v[i].y)
                self.holding[i] = nil
            end
        end
    end
    
    
    
    --# Lines
    Lines = class()
    
    function Lines:init(v,col)
        self.m = mesh()
        self.r = {}
        local c = col or 255
        for i=2,#v do
                self.p = (v[i]+v[i-1])
                self.p2 = (v[i]-v[i-1])
                self.dir = self.p2:normalize()
                self.len = self.p2:len()
                self.p = self.p/2
                self.r[i] = self.m:addRect(self.p.x,self.p.y,self.len,3,angleOfPoint(self.dir)/57.3)
                self.m:setColors(c,c,c,255)
    
        end
    end
    
    function Lines:draw()
        self.m:draw()
    end
    
    function Lines:touched(touch)
        -- Codea does not automatically call this method
    end
    
    --# Points
    Points = class()
    
    function Points:init(v,col)
        self.m = mesh()
        self.r = {}
        local c = col or 255
        for i=1,#v do
    
            self.p = v[i]
            self.r[i] = self.m:addRect(self.p.x,self.p.y,13,13,0)
            self.m.texture = txture
            self.m:setColors(c,c,c,150)
    
        end 
    end
    
    function Points:setpoint(v)
        for i=1,#v do
            self.m:setRect(self.r[i],v[i].x,v[i].y,13,13,0)
        end
    end
    
    function Points:setpoints(i,v)
            self.m:setRect(self.r[i],v[i].x,v[i].y,13,13,0)
    end
    
    function Points:draw()
        self.m:draw()
    end
    
    function Points:touched(touch)
        -- Codea does not automatically call this method
    end
    
  • Posts: 1,595

    There are a few missing textures which won't be hard to make, it's simple graphics. If you need help to use the class then send me a message

  • Posts: 437

    Hey, i have noticed that you have changed the Point:init function with different initializator params type, those were numeric and now tables...? I would like to see the Main , you can submit to gist :)

Sign In or Register to comment.