Fruitilities
Under construction !!!!11!1
Fruitilities are being rewritten. This page is entirely outdated and wrong.
Overview
Fruitilities is a vanilla lua compatible set of libraries made for the LÖVE game engine.
Made to work in harmony
I was a bit upset at how some libraries for LÖVE seemed to not cooperate with each other too well, which ended up forcing compromises to happen. While this was rare and not often an issue, I still wanted to make a set of libraries designed specifically to work together well.
(the real reason for making these is because i was too lazy to learn how to use other peoples' libraries so i just made my own)
Compatibility
For all libraries where it made sense to do so, I've made them vanilla lua compatible. Meaning that while these were made for Love2D, you can use them to aid you in your Lua shenanigans in any context.
Specifically, the libraries were made to work with Lua 5.1 (LuaJIT), but most likely work in newer versions too.
Cocollision
Performs collision calculations.
You start by creating a hitbox and adding shapes to it. The shapes a hitbox can have are an axis aligned bounding box, a convex polygon, a point, a line, a line segment, and a raycast. You then create so-called controllers from these hitboxes, which you can use directly as in-game objects. These controllers are also designed to work in harmony with sprite controllers - more on that later.
Usage example below.
local coco = require("cocollision")
local mouseSquare
local passiveSquare
function love.load()
local box
box = coco.newHitbox(50, 50) -- Set the origin to be in the center (50px, 50px)
-- Any amount of shapes can be added
-- Only ever adding one increases simplicity
box:addShape("polygon", 0, 0, 100, 0, 100, 100, 0, 100)
mouseSquare = box:createController()
passiveSquare = box:createController() -- Any amount of controllers can exist using a given hitbox
passiveSquare.x = 200
passiveSquare.y = 275
end
function love.update()
local mx, my = love.mouse.getPosition()
mouseSquare.x = mx
mouseSquare.y = my
mouseSquare.angle = mouseSquare.angle + 0.001
-- We know a pushVector will always be returned on hit
-- because the only shape we have is a polygon, and that returns a pushVector
local hit, _, _, pushVector = mouseSquare.intersects(passiveSquare)
if (hit) then
passiveSquare.x = passiveSquare.x - pushVector[1]
passiveSquare.y = passiveSquare.y - pushVector[2]
end
end
function love.draw()
mouseSquare.drawHitbox() -- Draw functions for debugging purposes
passiveSquare.drawHitbox()
end
Animango
Takes care of taking sprites from spritesheets, cropping them out, and animating them. Sprites have the ability to have "animation events", to trigger things to happen once an animation gets to a specific frame. This is useful for, for example, syncing footstep sounds with a walk animation.
This is the only library that is not vanilla lua compatible. This is because it's so graphics-heavy (which vanilla lua doesn't have a way to do by itself), so making this library work in other environments would get rid of the vast majority of features it provides.
Spritesheet used, usage, and result below.
love.graphics.setDefaultFilter("nearest", "nearest") -- Configure LÖVE for pixelart
local mango = require("animango")
local sprite
local controller
function love.load()
local spriteImage = love.graphics.newImage("symbol.png")
-- Create sprite from the image,
-- set the size of the sprite within the spritesheet as 8x8px,
-- set the origin to X=0.5, Y=0.5 (center),
-- set the "crop" value to 1px
-- (usually the cropping would be removing pixels which are there to prevent bleeding)
sprite = mango.newSprite(spriteImage, 8, 8, 0.5, 0.5, 1)
-- Add animation at 5fps including all frames in the spritesheet at
sprite:addAnimation("cycleSymbols", 5)
-- Add a similar animation that only includes letters, not numbers (frames 1-26)
sprite:addAnimation("cycleLetters", 5, 1, 26)
-- Set up a controller
controller = sprite:createController()
controller.setAnimation("cycleLetters")
controller.setScale(10)
controller.x = 250
controller.y = 250
end
function love.update(dt)
controller.update(dt) -- Necessary for animations
end
function love.draw()
controller.draw()
end
Animango & Cocollision
Cocollision and animango are designed to work together. Both work by creating controllers that you can then manipulate with and call functions on.
The way these libraries synergise is that when creating a controller with one of them, you can pass in an already existing controller previously made with the other. This way you end up with a single controller controlling both collisions and animations, since those two have many shared values, such as the position or scale of the controller.
So a controller created this way will have both the methods for manipulating its visual and collision aspect, but the values for those two are shared among them. This is best explained with an example, provided below.
love.graphics.setDefaultFilter("nearest", "nearest") -- Configure LÖVE for pixel art
local coco = require("cocollision")
local mango = require("animango")
local sprite
local hitbox
local controllers
function love.load()
local spriteImage = love.graphics.newImage("symbol.png")
-- Create sprite
sprite = mango.newSprite(spriteImage, 8, 8, 0.5, 0.5, 1)
sprite:addAnimation("cycleSymbols", 5)
-- Create hitbox
hitbox = coco.newHitbox(3, 3)
hitbox:addShape("rectangle", 6, 6) -- 1px smaller than the size set in the sprite because we cropped it
-- Create a few controllers
controllers = {}
controllers[#controllers+1] = hitbox:createController(sprite:createController()) -- Combine them in one line
controllers[#controllers+1] = hitbox:createController(sprite:createController())
controllers[#controllers+1] = hitbox:createController(sprite:createController())
-- Scale them up, set their animations, move them more to the center
for _, controller in ipairs(controllers) do
controller.setScale(10)
controller.setAnimation("cycleSymbols")
controller.x = 400
controller.y = 250
end
end
function love.update(dt)
-- Update all controllers
for index, controller in ipairs(controllers) do
if (index == 1) then
-- Move first controller with mouse
local mx, my = love.mouse.getPosition()
controller.update(dt, mx, my)
else
-- Just update the others
controller.update(dt)
end
-- Solve collisions
for _, otherController in ipairs(controllers) do
if controller == otherController then break end
local hit, _, _, pushVector = controller.intersects(otherController)
if hit then
controller.x = controller.x + pushVector[1] / 2
controller.y = controller.y + pushVector[2] / 2
otherController.x = otherController.x - pushVector[1] / 2
otherController.y = otherController.y - pushVector[2] / 2
end
end
end
end
function love.draw()
-- Draw all controllers
for _, controller in ipairs(controllers) do
controller.draw()
end
end
This is quite a bit of a longer example for the sake of showing something slightly more robust. In this setup, the "controllers" table can have any controllers whose hitbox only has 1 shape that is either a rectangle or a polygon. They will all collide together correctly and animate whatever sprite is attached to them.
Each controller's animation can be controlled seperately, although in this example, all are set to the same sprite and animation at the same speed.
Other Libraries
I mainly wanted to cover Animango and Cocollision so far, as those are more or less done. When Fruitilities as a whole are ready, each library will be covered.