Log In  

Cart #spline-2 | 2022-10-01 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
11

This is a tool for creating and editing splines within your game. I used this initally for a shmup i was working on where I wanted to create an intresting enemy flight pattern.

The black area represents a 128x128 screen.

Just click in the points you want then press CTRL+C to copy to clipboard This will copy all points to the clipboard as a string.

This string can be then used within your game.

This is what I use it for.

You can test out pasting of splines here. Also this cart has code for handling the splines.

Cart #spline_demo-0 | 2022-10-01 | Code ▽ | Embed ▽ | No License
11

How to use

Reading code

copy the following code to your cart (this can also be read from tab 2 of the demo cart)

--distance vunerable to overflow
function pdist(p1,p2) 
    return ((p2.x-p1.x)^2+(p2.y-p1.y)^2)^0.5
end

--cubic bezier single dimension
function cub_b_p(a,b,c,d,t)
    local tm=1-t
    return tm*tm*tm*a+
        tm*tm*3*t*b+
        tm*3*t*t*c+
        t*t*t*d
end

--cubic bezier x&y
function cub_bez(p1,p2,p3,p4,t)
    return {x=cub_b_p(p1.x,
    p2.x,
    p3.x,
    p4.x,
    t),
    y=cub_b_p(p1.y,
    p2.y,
    p3.y,
    p4.y,
    t)}
end

--read splines
--expected table with multiple
--of 8 entries.
--p1.x,p1.y
function read_spline(points)
    local curves,dists,td={},{},0
    local limits={0}
    for i=1,#points,8 do
        local pts={}
        for j=i,i+8,2 do
            add(pts, {
            x=points[j],
            y=points[j+1]})
        end
        local function c(t)
            return cub_bez(
                pts[1],
                pts[2],
                pts[3],
                pts[4],
                t)
        end
        local d=0
        for j=0x0.1,1,0x0.1 do
            d+=pdist(c(j-0x0.1),c(j))
        end
        add(curves,c)
        add(dists,d)
        td+=d
    end

    local l=0
    for d in all(dists) do
        l+=d/td
        add(limits,l)
    end

    limits[#limits]=1

    local function spl(t)
        if t==1 then 
            return curves[#curves](1)
        end

        local i,l=1,0
        while t>=l do
            i+=1
            l=limits[i] or 1
        end
        local ol=limits[i-1]
        local fact=1/(l-ol)
        local t2=(t-ol)*fact
        return curves[i-1](t2)
    end
    --return curves
    return spl
end

2 Generating curves

Generate curves using the main cart that represent how you would like your mobs to move. Just click different points into place. Note that the black area represents a whole screen of 128x128 pixels.

You can click and drag points around, press delete to delete the last point. When you are happy with your curve press CTRL+C to copy a string to the clipboard.

NOTE: Sometimes the string doesn't copy correctly in the web player, if this happens from pico-8 you can load #spline and run the cart locally. The copy seems far more reliable when running in pico-8

Paste the following into your _init() function.

path=read_spline(split(*your pasted string*))

Then whenever you want a point along the path you just call path using the decimal of what portion of the path you want covered.

examples

local point = path(0) Will give you the start point of the path
local point = path(0.25) Will give you a point 1/4 the way through the path.
local point = path(1) Will give you a point at the end of the path.

 local point = path(0.5) --half way
 spr(1, point.x, point.y) --draw sprite 1 at this point.

Feel free to look at the demo cart for code that uses a sprite.

Update

Now cart accepts pasting of splines not just upload.
Now curves are more round by default

Credits

Thanks to @pancelor for the idea about the splines.
Thanks to @Heracleum for assistance writing up how to use the cart.

P#117952 2022-09-25 17:35 ( Edited 2022-10-01 22:09)

Man, I never thought about this, but pico 8 NEED a path editor. Kinda like the path editor from game maker. This does the work very well. Gold star.

P#117956 2022-09-25 18:12

How would I use the exported data?

P#118251 2022-09-30 23:32
1

@spellcaster there are multiple ways that this can be read. The approach I take is as follows.

--read splines
--expected table with multiple
--of 8 entries.
--p1.x,p1.y
function read_spline(points)
    local curves,dists,td={},{},0
    local limits={0}
    for i=1,#points,8 do
        local pts={}
        for j=i,i+8,2 do
            add(pts, {
            x=points[j],
            y=points[j+1]})
        end
        local function c(t)
            return cub_bez(
                pts[1],
                pts[2],
                pts[3],
                pts[4],
                t)
        end
        local d=0
        for j=0x0.1,1,0x0.1 do
            d+=pdist(c(j-0x0.1),c(j))
        end
        add(curves,c)
        add(dists,d)
        td+=d
    end

    local l=0
    for d in all(dists) do
        l+=d/td
        add(limits,l)
    end

    limits[#limits]=1

    local function spl(t)

        local i,l=1,0
        while t>=l do
            i+=1
            l=limits[i] or 1
        end
        local ol=limits[i-1]
        local fact=1/(l-ol)
        local t2=(t-ol)*fact
        return curves[i-1](t2)
    end
    --return curves
    return spl
end

--distance vunerable to overflow
function pdist(p1,p2) 
    return ((p2.x-p1.x)^2+(p2.y-p1.y)^2)^0.5
end

--cubic bezier single dimension
function cub_b_p(a,b,c,d,t)
    local tm=1-t
    return tm*tm*tm*a+
        tm*tm*3*t*b+
        tm*3*t*t*c+
        t*t*t*d
end

--cubic bezier x&y
function cub_bez(p1,p2,p3,p4,t)
    return {x=cub_b_p(p1.x,
    p2.x,
    p3.x,
    p4.x,
    t),
    y=cub_b_p(p1.y,
    p2.y,
    p3.y,
    p4.y,
    t)}
end

Take the array of points. Every number represents X,y pairs.

For every four pairs represents a segment. I run this through the function cub_bez() with a t between 0 and 1 which represents 0 is at the first point of the segment 1 is at the end point of the segment.

Depending on what you want to do this may be enough. However I'm using it for motion therefore I want to normalize all of this so that each segment is the same speed.

To do this I sample each curve segment multiple times and get a sum of the distance between each of the points.

I then add all of these together and produce a new function that takes a value between 0 and 1 that represents the entire spline as a single function.

This function is returned from the read_spline function below.

P#118282 2022-10-01 14:46

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-03-29 11:30:01 | 0.053s | Q:22