[Game Dev Blog [Part 2]]: Engine Organization and Monkey Patching.

in #games8 years ago

TLDR;  He talks about why he's using Lua, building a file loading system and because it got a bit wordy he put a bunch of planet screenshots in to break it up a bit.


M Class Moon.

A quick note on the screenshots, all these planet textures are procedurally generated on game load.  At this stage in development I have to reload often to test things so I dropped the resolution of the maps to reduce my frequent load times, in the final release they will be less pixelated.  Anyway, on with the blog. :)

If you didn't catch part one of my blog you can catch up here -> NSFW.

So why Lua?

Well I started making this project in Unity at first but I ran into a little problem with decimals.  You see, at the time I started this project Unity would limit it's calculations to 6 decimal places.  0.000001 was the smallest number that you could express mathematically.  "So what?" I hear you say, well that is actually quite a problem in a game set in space you see because of the scale of a Galaxy.  I wanted a big Galaxy and I didn't want to use the "Jumpgate" method normally used where the world is split into a lot of smaller maps and you use the gates to navigate between them.

Unity would have forced my hand on this one as there is a maximum world size without implementing a custom coordinate system like they did in Kerbal space program. But Kerbal only represent one solar system in their game, I wanted many.  So after a little searching online I stumbled upon Löve2d and I haven't looked back, after a few hours of playing with it I was hooked.  I played around for a few months writing little tests, playing with box2d which is built in and just basically getting a feel for the API's till eventually the time came to actually knuckle down and start.

Protoplanet.

Engine Time! --Warning, code ahead!

Ok, so where do you actually start making a game?  Well I needed a bunch of stuff to be made obviously but where was I going to put it all?  I decided that Unity had the right idea here in that you put things in folders with specific names and the engine loads them into the right place based on the folder name you gave it.  It also does things recursively so you can put your folders with files in another folder to group them together and that won't break the loading system.

Here's what I came up with, a function to load a file the proper way regardless of it's type.  I only use 4 data types at the moment but you can easily expand this to more types.  You pass it a table to put the data into, the path to where the file is stored, the name of the folder it is in and the name of the file.

local function getFile(tab, path, folder, file)

  if tab[folder] == nil then

    tab[folder] = {}

  end

  local ext = string.sub(file, #file - 3, #file)

  file = string.sub(file, 1, #file - 4)

  local filePath = path.."/"..folder.."/"..file

  if ext == ".lua" then

    tab[folder][file] = require(path.."."..folder.."."..file)

  elseif ext == ".png" then

    tab[folder][file] = love.graphics.newImage(filePath..ext)

  elseif ext == ".sha" then

    tab[folder][file] = love.graphics.newShader(filePath..ext)

  elseif ext == ".wav" then

    tab[folder][file] = love.audio.newSource(filePath..ext, "static")

  end

end

So that'll load up any one file, great but what about that recursive stuff? Well for that I needed another function to check if the file is actually a folder or not.  You may notice though that I didn't force any kind of order on this system, you can use any folder name you like so adding new data is as easy as adding a folder and a file.

local function getFiles(tab, path, folder)

  local filePath = path

  if folder == nil then

    folder = ""

  else

    filePath = path.."/"..folder

  end

  local data = love.filesystem.getDirectoryItems(filePath)

  for _, file in pairs(data) do

    if love.filesystem.isDirectory(filePath.."/"..file) then

      if folder ~= "" then

        getFiles(tab, filePath, file)

      else

        getFiles(tab, path, file)

      end

    else

      getFile(tab, path, folder, file)

    end

  end

end

So after writing these two functions I can load all of my newly made data like this...

function love.load()

  math.randomseed(os.time())

  local modPath = fs.getSaveDirectory()

  fs.createDirectory("/Mods")

  getFiles(engine, "Lib")

  getFiles(engine, "Data")

  getFiles(engine, "Mods")

  engine.state.set(engine.currentState)

end


A short word about Monkey Patching and an abrupt end.

Moon.

Eagle eyed readers will have noticed that as this loading system uses the "require" keyword to load it's data and the Mods folder is loaded after the Data and the Lib folders that absolutely anything can be rewritten from my code base as a mod.  If you add something to the Mods folder with the same folder and name as a "Vanilla" object it will overwrite the core functionality.

Well I know I promised in part 1 to talk about the E.C.S system but it seems I've rambled on for way too long again.  If you made it this far then thank you for your time, if you have any questions or comments I'd love to hear from you.

Sort:  

Interesting article