From bc3752da1bb6bd0b13262c2eca353068bf2d8250 Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Wed, 4 Dec 2019 18:11:11 +0100 Subject: [PATCH] Added several convenience features. Including allowing specifying number of tiles for each dimension, if for example it is known that the image will contain 16x16 sprites, but the size of the individual sprite is unknown. --- spritesheet.lua | 88 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 17 deletions(-) diff --git a/spritesheet.lua b/spritesheet.lua index 53b4ef4..eea094b 100644 --- a/spritesheet.lua +++ b/spritesheet.lua @@ -1,5 +1,14 @@ -local error = require 'errors' 'SpriteSheet2' +local error_orig = error +local error, error_internal do + error, error_internal = error_orig, error_orig + error_orig = nil + local success, errorlib = pcall(require,'errors') + if success then + error = errorlib 'spritesheet' + error_internal = error.internal + end +end -------------------------------------------------------------------------------- -- Util @@ -102,7 +111,7 @@ function Animation:generateImage () if self.wrap then t = t % self.duration end local quad = get_quad_based_on_time(self, t) - if not quad then error.internal('Could not determine quad when drawing animation. Time was %f.', t) end + if not quad then error_internal('Could not determine quad when drawing animation. Time was %f.', t) end love.graphics.draw(self.imagesheet.image, quad, x, y, 0, 1, 1, self.imagesheet.origin_x, self.imagesheet.origin_y) end end @@ -165,8 +174,8 @@ local function load_quads (image, quad_data, imagesheet) local image_width, image_height = image:getDimensions() local tile_width, tile_height = quad_data.tile_width, quad_data.tile_height - local tiles_pr_row = image_width/tile_width - local max_quad_id = tiles_pr_row * (image_height/tile_height) - 1 + local tiles_per_row = image_width/tile_width + local max_quad_id = tiles_per_row * (image_height/tile_height) - 1 local quad_cache = {} local function quad_from_id (id) @@ -177,7 +186,7 @@ local function load_quads (image, quad_data, imagesheet) -- Calculate if not quad_cache[id] then - quad_cache[id] = love.graphics.newQuad((id%tiles_pr_row)*tile_width, math.floor(id/tiles_pr_row)*tile_height, tile_width, tile_height, image_width, image_height) + quad_cache[id] = love.graphics.newQuad((id%tiles_per_row)*tile_width, math.floor(id/tiles_per_row)*tile_height, tile_width, tile_height, image_width, image_height) end return quad_cache[id] end @@ -226,15 +235,40 @@ local function load_quad_data (filename) local chunk, error_msg = love.filesystem.load(filename..'.lua') if chunk then local data = setfenv(chunk, SPRITESHEET_ENV)() + -- Error check - if type(data) ~= 'table' then error('Bad spritesheet "%s". Must return a table, but returned %s (%s)', filename, data, type(data)) end - if type(data.tile_width) ~= 'number' then error('Bad spritesheet "%s". The root table must contain key "tile_width", with integer value, but it was %s (%s)', filename, data.tile_width, type(data.tile_width)) end - if data.tile_width ~= math.floor(data.tile_width) then error('Bad spritesheet "%s". The root table must contain key "tile_width", with integer value, but it was %f (float)', filename, data.tile_width) end - if type(data.tile_height) ~= 'number' then error('Bad spritesheet "%s". The root table must contain key "tile_height", with integer value, but it was %s (%s)', filename, data.tile_height, type(data.tile_height)) end - if data.tile_height ~= math.floor(data.tile_height) then error('Bad spritesheet "%s". The root table must contain key "tile_height", with integer value, but it was %f (float)', filename, data.tile_height) end - if not (type(data.tile_names) == 'table' or type(data.tile_names) == 'number') then error('Bad spritesheet "%s". The root table must contain key "tile_names", with either a table or a number value, but it was %s (%s)', filename, data.tile_names, type(data.tile_names)) end - if data.tile_origin and type(data.tile_origin) ~= 'table' then error('Bad spritesheet "%s". If the root table contains key "tile_origin", it must be a table value, but it was %s (%s)', filename, data.tile_origin, type(data.tile_origin)) end - -- + if type(data) ~= 'table' then + error('Bad spritesheet "%s". Must return a table, but returned %s (%s)', filename, data, type(data)) + end + local l = {'Bad spritesheet "'.. filename.. '"'} + if data.tiles_per_row ~= nil and data.tile_width ~= nil then + l[#l+1] = 'Root table must not contain both keys "tiles_per_row" and "tile_width"' + elseif data.tiles_per_row == nil and data.tile_width == nil then + l[#l+1] = 'Root table must contain either keys "tiles_per_row" or "tile_width"' + end + if data.tiles_per_column ~= nil and data.tile_height ~= nil then + l[#l+1] = 'Root table must not contain both keys "tiles_per_column" and "tile_height"' + elseif data.tiles_per_column == nil and data.tile_height == nil then + l[#l+1] = 'Root table must contain either keys "tiles_per_column" or "tile_height"' + end + local INTEGER_TILESET_KEYS = {'tile_width', 'tile_height', 'tiles_per_row', 'tiles_per_column'} + for _, integer_key in ipairs(INTEGER_TILESET_KEYS) do + local v = data[integer_key] + if v and (type(v) ~= 'number' or v % 1 ~= 0) then + l[#l+1] = string.format('Key "%s" in root table must map to integer value, but it was %s (%s)', integer_key, v, type(v)) + end + end + if not (type(data.tile_names) == 'table' or type(data.tile_names) == 'number') then + l[#l+1] = string.format('Root table must contain key "tile_names", with either a table or a number value, but it was %s (%s)', data.tile_names, type(data.tile_names)) + end + if data.tile_origin and type(data.tile_origin) ~= 'table' then + l[#l+1] = string.format('Key "%s" in root table must map to a table value, but it was %s (%s)', 'tile_origin', data.tile_origin, type(data.tile_origin)) + end + + -- Throw error or return + if #l > 1 then + error(table.concat(l, '\n ')) + end return data end print(error_msg) @@ -253,18 +287,38 @@ local SpriteSheet = {} function SpriteSheet.new (filename) local quad_data = load_quad_data(filename) + -- NOTE: `force_uneven_tile_size` in quad_data can be used to + -- ignore the image size-tile size divisibility check. Edit the + -- spriteimage itself if you can, as it will silently ignore + -- several errors. + -- Set info local self = setmetatable({}, SpriteSheet) self.filename = filename self.image = love.graphics.newImage(filename..'.png') self.origin_x = 0 self.origin_y = 0 - self.tile_width = quad_data.tile_width - self.tile_height = quad_data.tile_height + + -- TODO: Give warning/error due to rounding down. + quad_data.tile_width = quad_data.tile_width or math.floor(self.image:getWidth() / quad_data.tiles_per_row) + quad_data.tile_height = quad_data.tile_height or math.floor(self.image:getHeight() / quad_data.tiles_per_column) + print(self.tile_width, self.tile_height) + + self.tile_width = quad_data.tile_width + self.tile_height = quad_data.tile_height -- Error checking - if self.image:getWidth() % self.tile_width ~= 0 then error('Bad spritesheet "%s". Image size (%i, %i) must be dividable by tile size (%i, %i), but width leaves a remainder of %i.', filename, self.image:getWidth(), self.image:getHeight(), self.tile_width, self.tile_height, self.image:getWidth() % self.tile_width) end - if self.image:getHeight() % self.tile_height ~= 0 then error('Bad spritesheet "%s". Image size (%i, %i) must be dividable by tile size (%i, %i), but height leaves a remainder of %i.', filename, self.image:getWidth(), self.image:getHeight(), self.tile_width, self.tile_height, self.image:getHeight() % self.tile_height) end + do + local rem_width = self.image:getWidth() % self.tile_width + local rem_height = self.image:getHeight() % self.tile_height + if not quad_data.force_uneven_tile_size and (rem_width ~= 0 or rem_height ~= 0) then + local s = ('Bad spritesheet "%s". Image size (%i, %i) must be dividable by tile size (%i, %i)') + :format(filename, self.image:getWidth(), self.image:getHeight(), self.tile_width, self.tile_height) + if rem_width ~= 0 then s = s..('\n Width leaves a remainder of %i.'):format(rem_width) end + if rem_height ~= 0 then s = s..('\n Height leaves a remainder of %i.'):format(rem_height) end + error(s) + end + end -- Set origin if quad_data.tile_origin then