I think the fact that LÖVE games are made with different object systems hurts the community because it needlessly increases the difficulty of understanding other people's code. Also some people use absolutely horrible systems..
You can implement a perfectly adequate system with a small amount of Lua code so adding one would not meaningfully increase the size of LÖVE. I thus would like to suggest the addition of a default object system to LÖVE. I don't care whether the devs choose an existing one or write a new one, as long as it is easy to understand and efficient. The primary target audience of LÖVE must be considered here. Efficient because it will be used for real-time games and easy to understand because many/most LÖVE users are not professional programmers or even just experienced amateurs. It is amazing how many C++ class features one can implement in Lua .. resist the temptation.
A related suggestion would be the addition of a container lib written in Lua. Again this requires very little code because of the high-level nature of Lua. I found myself writing my own container classes for my game.. which is just ridiculous. Lua tables are powerful but using them for everything does not lead to good code. You might argue that hobby coders shouldn't be bothered with countless data structures and I would even agree. I don't suggest making the other LÖVE functions depend on them. I suggest adding them as an optional component for advanced users e.g. like the physics libs or the pixel shaders. Thus far I have successfully ignored the very existence of those!
At least data structures commonly used in games should be added e.g. priority queue (pathfinding), matrix/multi-dimensional array (grid-based games), vector/one-dimensional array (pretty much everywhere), ..
Before someone points out that Lua tables include vector-like behavior as a subset: the issue is that Lua tables don't have methods. LÖVE in general uses OOP syntax i.e. you get objects and call their methods. It feels wrong to switch to old procedural style - i.e. you pass objects to functions which operate on them - to work with tables. Also the Lua table library is minimalist, you could certainly add some functions there. Personally I love functional-like constructs however I don't want to suggest adding them because that would make things unnecessary confusing for most LÖVE users. There should be one way to do things and we are not going to get rid of loops, so having both loops and map/filter/reduce would be evil complexity bloat in case of LÖVE.
Damn, that was a long post
P.S.: For your general amusement here is some code from my current project. I wrote my own object system, container lib, etc. Unfortunately that means other LÖVErs cannot copypasta my code.. or even just understand it
Area.lua
Code: Select all
-- Area class
--[[
== Size
The area size is fixed because it is more efficient to reuse the same area
object again and again than to continuously create and destroy areas of
different sizes. Thus we use one Area object whose size equals the size
of the largest supported area. To create the illusion of differently sized
areas we use View objects to limit how much of the area is visible
== Map
The area map has three layers: actor, item, terrain (see AreaCell.lua)
actor: an entity with AI
item: an entity without AI
terrain: an immutable entity without AI
when terrain "changes" it is simply replaced by different terrain
== Actors
In addition to the map the area has a container containing references to
all actors within the area. We can use it to quickly iterate over all of
them
--]]
-- Dependencies
require "Lib.Gex"
local Class = require "Lib.Class"
local Dimensions = require "Lib.Dimensions"
local Matrix = require "Lib.Matrix"
local AreaCell = require "System.AreaCell"
local Area = Class.Singleton({
-- Map of the area
map = Matrix(Dimensions(64, 64)),
-- Contains references to all actors within the area
actors = Vector()
})
-- Initialization
Area.Initialization = function(self)
self.map:Fill(AreaCell)
end
-- Clears the area
Area.Clear = function(self)
self.map:Apply(AreaCell.Clear)
self.actors:Clear()
end
return Class.Finalize(Area)
Code: Select all
-- Area cell class
--[[
We implement map layers through multi-layer map cells.
See Area.lua for details about these layers
--]]
-- Dependencies
require "Lib.Gex"
local Class = require "Lib.Class"
local AreaCell = Class.UniqueObjects({
-- Entities in the cell
actor = false,
item = false,
terrain = false
})
-- Clears the area cell
AreaCell.Clear = function(self)
self.actor = false
self.item = false
self.terrain = false
end
return Class.Finalize(AreaCell)
Code: Select all
-- Rope class
-- Dependencies
require "Lib.Gex"
local Class = require "Lib.Class"
local String = require "Lib.String"
local Vector = require "Lib.Vector"
local Rope = Class.Extends(Vector)
-- Adds a fragment
Rope.Add = function(self, fragment)
self[#self + 1] = String(fragment)
end
-- To string
Rope.ToString = function(self)
return table.concat(self)
end
-- To string + clear
Rope.Yield = function(self)
local yield = self:ToString()
self:Clear()
return yield
end
return Class.Finalize(Rope)