[Library] Simple Path Animation

Showcase your libraries, tools and other projects that help your fellow love users.
Post Reply
User avatar
HugoBDesigner
Party member
Posts: 403
Joined: Mon Feb 24, 2014 6:54 pm
Location: Above the Pocket Dimension
Contact:

[Library] Simple Path Animation

Post by HugoBDesigner »

Just wanted to share my "Mini Animation Library". It plays an animation on a given path at a constant speed:

Image

If you want to play around, I've made a very, very simple and rough demo /s:
Image

Feel free to check out the source code if you need more info or want to "borrow" some code, I worked pretty hard on making this "tech demo" as unnecessarily pretty as possible (credits appreciated but not required. Same for the library itself). I will warn, however, that my code is probably a mess, so if anyone wants some clarification or better in-code commenting, feel free to let me know :nyu:

Here's the "library" itself:

Code: Select all

function newAnimation(...)
	local args = {...}
	local self = {}
	
	local function errHand(t)
		if #t < 4 then
			error("Too few points to create animation")
		elseif math.mod(#t, 2) ~= 0 then
			error("Odd number of coordinates, a pair of numbers for each point is required")
		else
			for i, v in ipairs(t) do
				if type(v) ~= "number" then
					error("Bad argument #" .. i .. " to 'newAnimation' (number expected, got " .. type(v) .. ")")
				end
			end
		end
	end
	
	if type(args[1]) == "table" then
		errHand(args[1])
		self.path = {unpack(args[1])}
	else
		errHand(args)
		self.path = {unpack(args)}
	end
	
	function self.point(pos, n)
		return self.path[(pos+1)*2 - (4-n)]
	end
	function self.distance(x1, y1, x2, y2)
		return math.sqrt((x1-x2)^2 + (y1-y2)^2)
	end
	
	self.dists = {}
	for i = 3, #self.path-1, 2 do
		table.insert(self.dists, self.distance(self.path[i-2], self.path[i-1], self.path[i], self.path[i+1]))
	end
	
	self.sum = 0
	self.dsums = {}
	
	for i, v in ipairs(self.dists) do
		self.sum = self.sum + v
		table.insert(self.dsums, self.sum)
	end
	
	function self.getPoint(n)
		return self.path[(n-1)*2+1], self.path[(n-1)*2+2]
	end
	
	function self.getPos(f)
		if type(f) ~= "number" then
			error("Bad argument #1 to 'getPos' (number expected, got " .. type(f) .. ")")
		end
		
		if self.sum == 0 then
			--NO DIVIDING BY 0! My computer doesn't like that for *some* reason...
			return self.path[1], self.path[2]
		end
		
		local f = math.max(0, math.min(1, f))
		local pos = 1
		
		while pos < #self.dsums and f*self.sum > self.dsums[pos] do
			pos = pos + 1
		end
		
		local st = 0
		if pos > 1 then
			st = self.dsums[pos-1]
		end
		
		local nf = f - (st/self.sum)
		nf = nf / (self.dists[pos]/self.sum)
		
		local px = self.point(pos, 1) + ( self.point(pos, 3) - self.point(pos, 1) )*nf
		local py = self.point(pos, 2) + ( self.point(pos, 4) - self.point(pos, 2) )*nf
		
		return px, py
	end
	
	return self
end

Basically, you create an animation path at your load function:

Code: Select all

function love.load()
	myAnim = newAnimation(50, 50, 150, 150, 300, 100, 150, 50, 50, 50)
end
And you can have as many coordinates as you want, looping back or not:
newAnimation({x1, y1, x2, y2, x3, y3...})

(It supports both raw arguments or a single table)


Later on, you can retrieve coordinates from the path based on a given time position. This is given via the myAnim.getPos(n), where n is a number between 0 and 1. You may set an independent timer and get the position based on it:

Code: Select all

function love.load()
	myAnim = newAnimation(50, 50, 150, 150, 300, 100, 150, 50, 50, 50)
	myTimer = {0, 3}
end

function love.update(dt)
	myTimer[1] = math.mod(myTimer[1] + dt, myTimer[2])
end

function love.draw()
	local factor = myTimer[1]/myTimer[2]
	
	local x, y = myAnim.getPos(factor)
	
	love.graphics.circle("fill", x, y, 5, 16)
end
To change the values of the coordinates, you just have to replace the original variable assigned to it with a new animation:

Code: Select all

function love.load()
	myAnim = newAnimation(50, 50, 150, 150, 300, 100, 150, 50, 50, 50)
	--code to change animation path
	myAnim = newAnimation(50, 50, 50, 200, 300, 100, 150, 50, 50, 50)
end
And that's about it! If you don't want to copy my code, here's a .lua file:
SimplePathAnimation.lua
v1.0 - Library (.lua)
(1.97 KiB) Downloaded 98 times
[/size][/b]


Demo changelog:

Code: Select all

version 1.1
 - Ctrl+Z and Ctrl+Y for undoing/redoing changes to the path;
 - Increased the maximum number of coordinates to 50;
 - Fixed a bug with removing the starting point from a looped path;
 - Right-clicking a point on a looped path sets it to the new origin point;
 - Quitting the demo now saves how you left it, so when you re-open it, it's just like you left it;
 - Added a "Clear data" button to reset the demo from the previously-mentioned saved data;
 - Added a small notification system.

version 1.0
 - Released
Attachments
Path Animation Demo.zip
v1.1 - Executable (.exe)
(3.07 MiB) Downloaded 95 times
Path Animation Demo.love
v1.1 - Source (.love)
(250.29 KiB) Downloaded 113 times
Last edited by HugoBDesigner on Tue Feb 21, 2017 11:10 pm, edited 7 times in total.
@HugoBDesigner - Twitter
HugoBDesigner - Blog
User avatar
Positive07
Party member
Posts: 1014
Joined: Sun Aug 12, 2012 4:34 pm
Location: Argentina

Re: [Library] Simple Path Animation

Post by Positive07 »

I have an addition I would love you to add!! [wiki]BezierCurve[/wiki] would this be possible?
for i, person in ipairs(everybody) do
[tab]if not person.obey then person:setObey(true) end
end
love.system.openURL(github.com/pablomayobre)
User avatar
HugoBDesigner
Party member
Posts: 403
Joined: Mon Feb 24, 2014 6:54 pm
Location: Above the Pocket Dimension
Contact:

Re: [Library] Simple Path Animation

Post by HugoBDesigner »

If you determine the points on the curve, then yes, it still works, regardless of "point density". Same for regular curves. What this does is taking the overall path then making sure the point moves at a constant speed, and it handles different lengths as well. The same if you happen to have overlapping points - they're ignored.
@HugoBDesigner - Twitter
HugoBDesigner - Blog
User avatar
Ulydev
Party member
Posts: 445
Joined: Mon Nov 10, 2014 10:46 pm
Location: Paris
Contact:

Re: [Library] Simple Path Animation

Post by Ulydev »

Sick! If I understand correctly, speed is the time it takes to play one loop?
User avatar
HugoBDesigner
Party member
Posts: 403
Joined: Mon Feb 24, 2014 6:54 pm
Location: Above the Pocket Dimension
Contact:

Re: [Library] Simple Path Animation

Post by HugoBDesigner »

Yup, the time it takes from one end to the other. If you want an actual speed (pixels per second), you can calculate that by checking on the general size of the path, which can be acquired via myAnim.sum:

Code: Select all

timeLimit = 1/speed*myAnim.sum --speed here is in pixels per second

x, y = myAnim.getPos(timer/timeLimit)
if you need to change speed midway through but don't want to mess up with the position:

Code: Select all

local p = timer/timeLimit*myAnim.sum

--change stuff here, such as path or speed

timer = p/myAnim.sum*timeLimit --If you change either the path or the speed as in the code above, it should work. Not tested though
@HugoBDesigner - Twitter
HugoBDesigner - Blog
User avatar
Jasoco
Inner party member
Posts: 3725
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: [Library] Simple Path Animation

Post by Jasoco »

This is really cool. I was writing my own for my 3D engine before I put it aside. I agree with the bezier curve option.
User avatar
HugoBDesigner
Party member
Posts: 403
Joined: Mon Feb 24, 2014 6:54 pm
Location: Above the Pocket Dimension
Contact:

Re: [Library] Simple Path Animation

Post by HugoBDesigner »

Alright guys, updated the demo and the main post. New, much necessary features from the demo include:

- Ctrl+Z and Ctrl+Y for undoing/redoing changes to the path;
- Increased the maximum number of coordinates to 50;
- Fixed a bug with removing the starting point from a looped path;
- Right-clicking a point on a looped path sets it to the new origin point;
- Quitting the demo now saves how you left it, so when you re-open it, it's just like you left it;
- Added a "Clear data" button to reset the demo from the previously-mentioned saved data;
- Added a small notification system.

As for the Bézier option, it's like I answered on Twitter: if you can determine points on the Bézier curve, then yes, it 100% works. The only trick to this library is that it only works with linear paths. But you can add as many points to it as you want without changing the overall execution. So a "curve" with 8 points and a "curve" with 256 points will have virtually the same impact (except, of course, that the total length of both are slightly different, but what I'm trying to say is that the amount of points doesn't affect the program).

In fact, you could stack a thousand points on the exact same spot, but have the 1001st spot be a few pixels to the right. The animation would still count as the point moving from left to right just as it would do if there were 999 points less :awesome:
@HugoBDesigner - Twitter
HugoBDesigner - Blog
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 29 guests