Over the past few LÖVE projects I’ve constantly rewritten the way objects were declared. Each time I started a new project I found my previous method either unsuitable, or just plain ugly.

My first method:

function MakeBox(x,y,s,img,mass,r,g,b,a,sw,sh) -- Spawns a box: Start X, Start Y, Scale of img, image, Mass, Colour red, Colour green, Colour blue, Colour alpha, Width, Height
    --Base
    boxcount = boxcount + 1
    boxrealcount = boxrealcount + 1
    _G["bodybox"..boxcount] = love.physics.newBody( world1, x, y, mass, mass ) --Box
    _G["scalebox"..boxcount] = s
    _G["spawnmass"..boxcount] = mass

    --Colours
    _G["redbox"..boxcount] = r
    _G["greenbox"..boxcount] = g
    _G["bluebox"..boxcount] = b
    _G["alphabox"..boxcount] = a
   
    --Image
    _G["imagebox"..boxcount] = img
    _G["shapebox"..boxcount] = love.physics.newRectangleShape( _G["bodybox"..boxcount], 0, 0, sw , sh)
    _G["shapebox"..boxcount]:setData( _G["shapebox"..boxcount] )
    return boxcount
end

A new object would be assigned just a number (Lets say 1). So to manipulate the object  I would have to use “bodybox1” and “shapebox1”. I used it as a quick fix and as my code grew so did the problems.

If I wanted to create a box using this method, I would have to do this:

MakeBox( wX/2 , wY/2 , 1 , imgpork , 100 , 90 , 255 , 215 , 197, 70, 120)

Now while this looks simple enough, it is very confusing to understand.  What do the values represent? How I can tell what’s scale and what’s alpha? What if I want to add air resistance as a value? I’d often put a mass value in the wrong place, and to cater for an additional value like air resistance I’d have to edit all calls of MakeBox. This obviously wasn’t going to work.

The Solution:

One of the main limitations of the Lua language is the fact that it’s not, by default, object orientated. A quick look at the LÖVE wiki and I found exactly what I was looking for. The MiddleClass library. The MiddleClass library makes it easy to create classes, which are perfect for an entity system.

First, I created a “Body” base class in a lua file of it’s own.

function Body:initialize()
end

function Body:SetPos( x , y )
    if self.body == nil then
        self.body:setPosition( x , y )
    else
        self.spawnpos = x , y
    end
end

function Body:SetScale( scale )
    self.scale = scale
end

function Body:SetWorld( worldnum )
    self.world  = Worlds[worldnum]
end

function Body:SetMass( mass )
    self.mass = mass

    if self.body ~= nil then
        self.body:setMass( nil , nil , self.mass , nil )
    end
end

function Body:SetRotInertia( rotinertia )
    self.rotinertia = rotinertia
   
    if self.body ~= nil then
        self.body:setMass( nil , nil , nil , self.rotinertia )
    end
end

function Body:SetSprite( sprite )
    self.sprite = sprite
end

function Body:Spawn()
    self.body   = love.physics.newBody( self.world , self.spawnpos , self.mass , self.rotinertia )
end

This base class provides loads of useful functions, like SetMass() and SetPos(), but on it’s own the “Body” class creates just a blank body with some functions, there’s no shapes for collisions or for sprites to draw on. This is where subclasses come in.

Using a subclass of “Body” I can create any shape and define additional useful functions specific to that subclass. Here’s a look in my “Physics_Rectangle.lua” subclass:

function Physics_Rectangle:initialize()
    super.initialize( self )
end

function Physics_Rectangle:SetHeight( height )
    self.height = height
end

function Physics_Rectangle:SetWidth( width )
    self.width = width
end

function Physics_Rectangle:Spawn()
    --Create Body
    super:Spawn( self )
   
    --Define Defaults
    if self.width == nil then self.width = ( self.sprite:getWidth() * self.scale ) end
    if self.height == nil then self.height = ( self.sprite:getHeight() * self.scale ) end
   
    self.shape = love.physics.newRectangleShape( self.body, 0, 0, self.width , self.height )
end

Here I’m creating a body from Body.lua, inheriting basic functions and then defining two more useful functions, SetWidth() and SetHeight().

This is great because it means you can create a new subclass for anything, a subclass for a new shape, a subclass for a layout of shapes and even a subclass for the physics of a character. It’s easy.

If I wanted to create a physics box using this method it’s much much more customizable and legible:

phybox = CreateEntity( Physics_Rectangle )
phybox:SetPos(10,20)
phybox:SetWidth(5)
phybox:SetHeight(5)
phybox:SetSprite(imgbox)
phybox:Spawn()

These are the necessary functions needed to spawn a box, but you still have all the possible functions like SetRotInertia() and SetWorld() which you can either call before or after spawn. You can use any new subclass you create as an entity type.

Every created entity is added to an entity table, so you can easily get all entities or just search for specific entity types. The new entity system is massively customizable and easy to read. Job done.

- BAZ