/skeletor

A skeletal animation system for LÖVE 2D. https://love2d.org

Primary LanguageLuaOtherNOASSERTION

skeletor

About

Skeletor is a skeleton animation system for LÖVE 2D.

Features

  • A detailed cascading style system to decorate the wireframe, joints, and shapes.
  • A comprehensive tagging system to retain track of skeletons and bones.
  • A skeleton boundary system for collision detection.
  • A fast learning curve.
  • A clean and comprehensive API.
  • Clean source code.
  • Good documentation.
  • Unit tests for better development and testing.

To do

  • An animation algorithm to morph from one frame to another in a given number of steps.
  • A texture system to decorate skeletons and bones with .jpg or .pgn images.
  • A grouping system for skeletons.
  • Saving and loading skeletons using files.
  • Separate skeletons' calculations from skeletor:draw() by creating skeletor:calculate()

Skeletor's functions reference

local Skeletor = require('skeletor.skeletor')
skeletor = Skeletor(style, skeletons)

skeletor:getStyle()
skeletor:setStyle(style)
skeletor:getSkeletons()
skeletor:setSkeletons(skeletons)

skeletor:newSkeleton(name, props)
skeletor:getSkeletonProp(name, propName)
skeletor:editSkeleton(name, props)
skeletor:cloneSkeleton(fromName, toName, props)
skeletor:deleteSkeleton(name)

skeletor:newBone(path, props)
skeletor:getBoneProp(path, propName)
skeletor:editBone(path, props)
skeletor:deleteBone(path)

skeletor:draw()

Default style

Below are the default style properties used by the skeletor engine. They control the display and behavior of skeletons and their bones.

These style properties can be overwritten at many different stages: when first loading the module, anytime using skeletor:setStyle(), when creating skeletons, and when creating bones.

show = true -- show element (use false to hide skeletons and/or bones)  

boundariesCalculate = false -- calculate the skeleton's boundary (useful for collision detection)  
boundariesShow = false -- show the boundary (useful for testing)  
boundariesStyle = "smooth" -- the style of the boundary line ("smooth", "rough")  
boundariesWidth = 1 -- the width of the boundary line  
boundariesColor = {255, 255, 255} -- the color of the boundary line  

wireShow = true -- show the wireframe  
wireStyle = "smooth" -- the style of the wireframe line ("smooth", "rough")  
wireWidth = 1 -- the width of the wireframe line  
wireColor = {255, 255, 255} -- the color of the wireframe line  

jointShow = true -- show joints  
jointMode = "fill" -- the drawing mode for joints  
jointShape = utils:getEllipseVertices(0, 0, 8, 8, math.rad(0), 30) -- the shape of joints  
jointRotatable = false -- rotate joints when skeletons or bones are scaled  
jointScalable = true -- scale joints when skeletons or bones are scaled  
jointColor = {255, 0, 0} -- the color of joints  

shapeShow = false -- show shapes  
shapeMode = "line" -- the drawing mode for shapes  
shapeShape = utils:getEllipseVertices(0, 0, 1, .35, 0, 30) -- the shape of shapes  
shapeSx = 1 -- x scale factor for shapes  
shapeSy = 1 -- y scale factor for shapes  
shapeColor = {0, 255, 0} -- the color of shapes  

Loading the module

-- simple loading
local Skeletor = require('skeletor.skeletor')
skeletor = Skeletor()
-- modifying some style properties at load time
local Skeletor = require('skeletor.skeletor')
skeletor = Skeletor({
	wireShow = false,
	shapeShow = true,
})
-- loading skeletons from a file
-- NOTE: this feature is not yet implemented (reference only)
local skeletons = -- retrieve skeletons from file

local Skeletor = require('skeletor.skeletor')
skeletor = Skeletor(nil, skeletons)

Getters and Setters

skeletor:getStyle() -- returns the default style being used
skeletor:setStyle(style) -- modifies the default style being used
skeletor:getSkeletons() -- returns the skeletons
skeletor:setSkeletons(skeletons) -- sets the skeletons

At this time, the most useful is skeletor:setStyle(style). It can be used to modify the default styles. For example, we can modify the wireShow and shapeShow properties after the module has been loaded.

-- modifying some style properties after the module has been loaded
local Skeletor = require('skeletor.skeletor')
skeletor = Skeletor()

skeletor:setStyle({
	wireShow = false,
	shapeShow = true
)

skeletor:getSkeletons() and skeletor:setStyle(style) are there for reference. They will become useful when saving and loading skeletons from file is implemented.

Skeleton

Creating a skeleton

Creating a new skeleton is done via skeletor:newSkeleton(name, props).

name is a tag used to identify the skeleton. (required)

props is a list of properties. (optional)

Here is the list of skeleton properties and their defaults values:

x = 0 -- the skeleton's x position
y = 0 -- the skeleton's y position
sx = 1 -- the skeleton's x scale factor
sy = 1 -- the skeleton's y scale factor
angle = 0 -- the skeleton's angle (in radians)

-- The remaining properties are the style properties mentionned above. (@see Default style)
-- When you create a skeleton, you can overwrite any of these style properties.

show
boundariesCalculate
boundariesShow
boundariesStyle
boundariesWidth
boundariesColor
wireShow
wireStyle
wireWidth
wireColor
jointShow
jointMode
jointShape
jointRotatable
jointScalable
jointColor
shapeShow
shapeMode
shapeShape
shapeSx
shapeSy
shapeColor

Examples of skeleton creation:

-- create a skeleton named john
skeletor:newSkeleton('john')
-- create a skeleton named max with a position of 200, 200
skeletor:newSkeleton('max', {
	x = 200,
	y = 200
})
-- create a skeleton named carl with various properties
skeletor:newSkeleton('carl', {
	x = 200,
	y = 200,
	angle = math.rad(90),
	wireShow = true,
	shapeColor = {23, 432, 23}
})

Retrieving a skeleton property

skeletor:getSkeletonProp(name, propName) makes it possible to retrieve any given property of a skeleton.

-- retrieving the x value of a skeleton named jim
local x = skeletor:getSkeletonProp('jim', 'x')

Editing a skeleton's properties

skeletor:editSkeleton(name, props) is used to edit any number of properties at once.

-- editing a skeleton named sandra
skeletor:editSkeleton('sandra', {
	x = 123,
	y = 34,
	sx = 2,
	wireColor = {123, 22, 33}
})

Cloning a skeleton

skeletor:cloneSkeleton(fromName, toName, props) is used to clone a skeleton.

You can optionally change some of the skeleton's properties when cloning.

-- Clone joe from a skeleton named jerry
skeletor:cloneSkeleton('jerry', 'joe')
-- Clone betsy from a skeleton name monica and change some properties
skeletor:cloneSkeleton('monica', 'betsy', {
	x = 100,
	angle = math.rad(23),
	sy = 2.4,
	wireColor = {234, 234, 12}
})

Deleting a skeleton

skeletor:deleteSkeleton(name) is used to delete skeletons.

-- deleting a skeleton named mary
skeletor:deleteSkeleton('mary')

Bone

Creating a bone

Creating a new bone is done via skeletor:newBone(path, props).

path is a tag used to identify the bone. (required)

props is a list of properties. (optional)

Here is the list of bone properties.

If they are not overwritten when creating the bone, the style properties will be derived from the skeleton they are attached to.

length = 0 -- the bone's length
angle = 0 -- the bone's angle (in radians)
sx = 1 -- the bone's x scale factor
sy = 1 -- the bone's y scale factor

-- The remaining properties are the style properties mentionned above. (@see Default style)
-- Notice boundary styles are gone. This is because boundaries are reserved for the skeleton.
-- When you create a bone, you can overwrite any of these style properties.

show
wireShow
wireStyle
wireWidth
wireColor
jointShow
jointMode
jointShape
jointRotatable
jointScalable
jointColor
shapeShow
shapeMode
shapeShape
shapeSx
shapeSy
shapeColor

Examples of bone creation:

-- add an arm bone on a skeleton named james
skeletor:newSkeleton('james')
skeletor:newBone('james.arm')
-- add a hand bone on an arm bone on a skeleton named maurice
skeletor:newSkeleton('maurice')
skeletor:newBone('maurice.arm')
skeletor:newBone('maurice.arm.hand')
-- add a foot bone with properties on a skeleton named tim
skeletor:newSkeleton('tim')
skeletor:newBone('tim.foot', {
	length = 100,
	angle = math.rad(180),
	wireStyle = "rough",
	wireColor = {23, 42, 255}
})

Retrieving a bone property

skeletor:getBoneProp(path, propName) makes it possible to retrieve any given property of a bone.

-- retrieving the angle of a bone named joe on skeleton mary
local angle  = skeletor:getBoneProp('joe.mary', 'angle')

Editing a bone's properties

skeletor:editBone(path, props) is used to edit any number of properties at once.

-- editing a bone named arm from a skeleton named paul
skeletor:editBone('paul.arm', {
	length = 100,
	sx = 2,
	wireColor = {123, 255, 255},
	wireWidth = 5
})

Deleting a bone

skeletor:deleteBone(path) is used to delete bones.

-- deleting a bone named toe on bone foot from skeleton jack
skeletor:deleteBone('jack.foot.toe')

Drawing skeletons and their bones

To draw skeletons and their bones, you simply run skeletor:draw() inside Love's draw function.

function love.draw()
	skeletor:draw()
end

Running unit tests

To run unit tests, make this call in main.lua

require 'skeletor.unittests.run'

If nothing happens, the unit tests have passed. If they fail, an error will be reported.