Log In  


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

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
12

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.

12


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.


How would I use the exported data?


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.



[Please log in to post a comment]