Images displaying in the WRONG order

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
meiows
Prole
Posts: 6
Joined: Sun Aug 23, 2015 8:15 pm

Images displaying in the WRONG order

Post by meiows »

This is the final bug of my little dress up game for FEMICOM gamejam.

I can't seem to get these images to draw in the right order! As this is a dress up game, order is very important. Socks should be under shoes, shirts should be over shorts and skirts. However, only one pair of socks goes under ALL of the shoes... the other two pairs only go under ONE pair of shoes.. they're on top of the rest. Same goes for the shorts and skirts and how they should be going under the shirts.

I've tried adding a z variable and ordering by that and even messing around with draw order, but this problem just won't be fixed! I just need a solution that won't break the dragging function because that was my problem last time.

It's much easier to see the problem if you open my file! Any help or tips is greatly appreciated. :neko:
Attachments
Dress Up.love
(4.81 MiB) Downloaded 81 times
:emo:
User avatar
Jasoco
Inner party member
Posts: 3726
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Images displaying in the WRONG order

Post by Jasoco »

Because you can only sort an indexed table where every row is a number. You can't sort a table where each row is a word or string.

Also you're just sorting by layer, which is just numbered in the order of the images being loaded.

When you iterate through a table using pairs, there is never really a predictable order that each item will be hit. They don't go in the order they were added to the table. So you'd need to use a numbered list.

What I do is add all the drawables to a "draw pool" table at the beginning of the game loop (First clear it completely) using a number and referring to each drawable you want to draw. Then sort that table by its "z".

It's complicated to explain quickly. And I've explained it before.

Here is where I originally explained it: viewtopic.php?p=72457#p72457

And here is a message I sent to another member when he asked about how to do it:
It's definitely near perfect. But it's not perfect. It's also very simple.

Basically I don't draw anything directly to the screen. I add each drawable's arguments to a numbered table and give it a type (Rectangle, polygon, image, circle, any other drawable) and a "distance from camera" value which ends up as the sort order.

I then sort this table by it's "distance" value.

And lastly, run through the table, check the type of object, and draw that object of that type using the arguments.

I kind of created a sort of library which I call "drawPool" which demonstrates what I mean. Here's the code to look at. Try putting it in a .lua and see if you can use it as is.

Code: Select all

--[[
	2014-2015 Jason Anderson

	This is a library I guess. Not really. It doesnt have a license because I dont know anything about licenses. I guess whatever license lets you use it as you wish. I dont claim to own any of this code as it's just code. I did write it myself, but it's nothing special. It also may not work as-is without other stuff or modification, but it probably will actually. Feel free to add your own "kinds" and delete code you want. Just I guess keep my name somewhere or whatever. I dunno.


	How to use:

	1) Require the library in your main.lua as such:
	drawPool = require 'drawPool'

	Or you can load it at any time and as many times as you need like this:
	drawPool = love.filesystem.load("drawPool.lua")()

	This allows you to have as many pools as you need to and layer them separately. For instance, background layers and foreground layers. Example:

	drawPool = {}
	for i = 1, 3 do
		drawPool[i] = love.filesystem.load("drawPool.lua")()
	end

	As long as you run through the drawPool table for each :prepare() and :present() and you :push() the proper objects to the proper drawPool[#] then it'll work perfectly.


	2) Call drawPool:prepare() at the beginning of your :update() function. No arguments are required.


	3) Any time before presenting, use the drawPool:push() function to add objects to the pool. The arguments should be in the form of a table. Use the standard naming conventions like x or y or w or h (Not width or height, unless it's the text kind. (Note: I use "kind" instead of "type" to avoid conflicting with the type() function.) Sorry, it's inconsistent.) And use the layer argument to determine what the sort order is. Remember, pool objects with the same sort order may fight for priority due to how table.sort works. So I find it better to add a tiny random decimal amount to each object's layer to try and make sure they wont fight.

	Examples:
	drawPool:push { kind = "rect", x = 100, y = 100, w = 100, h = 100, color = {255,0,0}, layer = 100 }
	drawPool:push { kind = "circle", x = 200, y = 120, rad = 80, color = {0,0,255}, layer = 95 }
	drawPool:push { kind = "text", x = 50, y = 200, width = 300, align = "center", shadow = true, font = your_font, color = {255,255,255}, layer = 103 }
	drawPool:push { kind = "image", x = 80, y = 60, image = fluffyBunny, quad = bunnyQuad[1], rot = 0, sx = 1, sy = 1, iox = 0, ioy = 0, layer = -304.43 }

	Where sx/sy is Scale and iox/ioy is Image Offset. Quad is optional. Font is optional. Shadow is optional.
	setScissor is not really required in anything. I added it as a hack for my UI system but dont really think I need it now anyway. It's too hacky and clunky. It can probably be removed.
	Colorize requires a shader I made but isnt an important or needed part of the library.


	4) In your :draw() function, call drawPool:present(), but only when you're ready to draw everything. Any :push() calls made after :present() will not happen.


	What happens is every frame, a table is emptied and stuff is added to it, sorted, then iterated through in order to determine what to draw in what order. This lets you basically "draw" any object at any time without worrying whether it's going to be drawn in front of or behind another object. As long as it gets a proper "layer" it'll draw at the right time. You can set layers to really high amounts for certain things (Like UI) and lower amounts for others (Game stuff) for example.

	Also note, this method can create a lot of garbage if used too much each frame. But it's usually not a problem.

	You can use this method to completely replace Löve's drawing frontend if you need to and push every single UI element into a pool if you really wanted to. You can use it for the game graphics or for the UI if you need to. I've used it for flat-shaded and textured polygonal 3D games myself. It was originally concieved for a 2D side-scrolling beat-em-up (Like TMNT) to make sure characters drew in the correct order. Something a lot of NES games in the day didnt bother to do.

	There is officially more text in this README than the entire library.
]]

local lgr = love.graphics

local v = ... or {}
local drawPool = {
	name = v.name or "No Name"
}

function drawPool:prepare()
	self.pool = {}
end

function drawPool:push(v)
	if not v.layer then v.layer = 0 end
	if not v.color then v.color = {255,255,255} end
	if v.kind == "text" then
		if not v.font then v.font = font.tiny end
	elseif v.kind == "rect" or v.kind == "circle" then
		if not v.fill then v.fill = "fill" end
		if v.fill == "line" and not v.lineWidth then v.lineWidth = 1 end
	elseif v.kind == "line" then
		if not v.lineWidth then v.lineWidth = 1 end
	end
	self.pool[#self.pool+1] = v
end

function drawPool:present()
	-- print("Name:", self.name)
	table.sort(self.pool, function(a, b)
		-- print(a.layer, b.layer)
		return a.layer < b.layer
	end)
	for i = 1, #self.pool do
		local d = self.pool[i]
		if d.kind == "image" then
			if d.quad then
				lgr.draw(d.image, d.quad, d.x, d.y, d.rot or 0, d.sx or 1, d.sy or 1, d.iox or 0, d.ioy or 0)
			else
				lgr.draw(d.image, d.x, d.y, d.rot or 0, d.sx or 1, d.sy or 1, d.iox or 0, d.ioy or 0)
			end
		elseif d.kind == "rect" then
			if d.lineWidth then lgr.setLineWidth(d.lineWidth) end
			lgr.setColor(d.color)
			lgr.rectangle(d.fill, d.x, d.y, d.w, d.h)
		elseif d.kind == "line" then
			lgr.setLineStyle("rough")
			if d.lineWidth then lgr.setLineWidth(d.lineWidth) end
			lgr.setColor(d.color)
			lgr.line(d.points)
		elseif d.kind == "circle" then
			if d.lineWidth then lgr.setLineWidth(d.lineWidth) end
			lgr.setColor(d.color)
			lgr.circle(d.fill, d.x, d.y, d.rad, d.detail)
		elseif d.kind == "text" then
			local text = d.text or ""
			lgr.setFont(d.font)
			if d.width then
				if d.shadow then
					lgr.setColor(0,0,0)
					lgr.printf(text, d.x+1, d.y+1, d.width, d.align)
				end
				lgr.setColor(d.color)
				lgr.printf(text, d.x, d.y, d.width, d.align)
			else
				if d.shadow then
					lgr.setColor(0,0,0)
					lgr.print(text, d.x+1, d.y+1)
				end
				lgr.setColor(d.color)
				lgr.print(text, d.x, d.y)
			end
		end
	end
end

return drawPool
It's just my method for doing it. It's not the defacto standard for doing it. Good luck.
User avatar
arampl
Party member
Posts: 248
Joined: Mon Oct 20, 2014 3:26 pm

Re: Images displaying in the WRONG order

Post by arampl »

Which is why z have its uses even in 2D.
Post Reply

Who is online

Users browsing this forum: No registered users and 5 guests