How to draw all objects on screen?

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.
User avatar
LeNitrous
Prole
Posts: 29
Joined: Tue Sep 08, 2015 3:25 am

How to draw all objects on screen?

Post by LeNitrous »

I'm currently making a prototype Visual Novel Engine.
What I'm looking for is to draw all objects on screen without having to call their draw function one by one. I'm using hump.class to handle my classes and this is what I got so far.

Code: Select all

function love.draw()
	actor1:draw()
	actor2:draw()
	actor3:draw()
end
Any help is appreciated. Thanks!
User avatar
Lafolie
Inner party member
Posts: 809
Joined: Tue Apr 05, 2011 2:59 pm
Location: SR388
Contact:

Re: How to draw all objects on screen?

Post by Lafolie »

Pop all your actors (or drawable actors) in a table and iterate over it, calling the relevant function. You can use this technique in many places and for many things. For example, updating all your actors could use the exact same table.

Code: Select all

actors = {} --this is our list of actors
love.load = function()
	table.insert(actors, player:new("Player 1", 16, 16)) --add a "player" actor to the table named 'Player 1, at x16:y16
end

love.update = function(dt)
	for n, actor in ipairs(actors) do
		actor:update(dt)
	end
end

love.draw = function()
	for n, actor in ipairs(actors) do
		actor:draw()
	end
end
This is a pretty basic example, obviously you need to consider how to 'skip' objects that don't get draw but how you do that is up to you. You could keep a separate table for drawing, a boolean property, check whether a draw function exists, or even something like only storing the functions themselves in the table (although this method has some caveats). Read up on polymorphism for really cool stuff you can do with tables of objects with common functionality.
Do you recognise when the world won't stop for you? Or when the days don't care what you've got to do? When the weight's too tough to lift up, what do you? Don't let them choose for you, that's on you.
User avatar
LeNitrous
Prole
Posts: 29
Joined: Tue Sep 08, 2015 3:25 am

Re: How to draw all objects on screen?

Post by LeNitrous »

Given above works well. Can it be more efficient by calling table.insert inside the object's init function instead of calling table.insert for every actor object?
Some psuedocode as it doesn't actually work but you'll get the gist of what I mean:

Code: Select all

-- inside actor.lua
actor = class{
	init = function(self, name, image)
		self.name	= name
		self.image	= image
	end

	table.insert(actors, self)
}

function actor:draw()
        -- some draw code here
end
User avatar
Smoggert
Prole
Posts: 29
Joined: Thu Nov 10, 2016 11:23 pm

Re: How to draw all objects on screen?

Post by Smoggert »

^ in order for this to work you would have to send the table reference (actors) with each actor you create.
Running the insert manually feels less convoluted, unless you would make a "actorhandler" class and run it with that.
User avatar
LeNitrous
Prole
Posts: 29
Joined: Tue Sep 08, 2015 3:25 am

Re: How to draw all objects on screen?

Post by LeNitrous »

I'll try to make an actor handler. For now I'll use this solution for prototyping.
User avatar
Sir_Silver
Party member
Posts: 286
Joined: Mon Aug 22, 2016 2:25 pm
Contact:

Re: How to draw all objects on screen?

Post by Sir_Silver »

Can it be more efficient by calling table.insert inside the object's init function instead of calling table.insert for every actor object?
Yes, but it probably won't matter too much how efficient your actor initialization and insertion is unless your going to be creating an absurd amount of them, or if you're creating many of them while the game is being played. I really like writing code examples so here's one showing how you can optimize table insertion.

Code: Select all

local test_table_1 = {}

local start = os.clock()

for i = 1, 1000000 do
	table.insert(test_table_1, i)  --Using table.insert 1,000,000 times.
end

print("Default use of table.insert time taken: ", os.clock() - start, "\n")

local test_table_2 = {}
local insert = table.insert

local start = os.clock()

for i = 1, 1000000 do
	insert(test_table_2, i)  --Using our localized version of table.insert 1,000,000 times.
end

print("Localized table.insert function time taken: ", os.clock() - start, "\n")

local test_table_3 = {}

local start = os.clock()

for i = 1, 1000000 do
	test_table_3[#test_table_3 + 1] = i  --Directly placing our values at the end of the table 1,000,000 times.
end

print("Direct insertion without function overhead time taken :", os.clock() - start, "\n")

--[[Default use of table.insert time taken: 	0.2400000000000002	
			
Localized table.insert function time taken: 	0.20999999999999996	
			
Direct insertion without function overhead time taken :	0.17999999999999972]]
This example shows that even with a million values to be inserted into a table, the difference between the normal way and the fastest way of inserting into a table yields only about 1/20th of a second gain. Pretty negligible for most application, but I think it's good practice to use this method when storing values in a table in love.update or love.draw
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: How to draw all objects on screen?

Post by ivan »

How about:

Code: Select all

local test_table_4 = {}

local start = os.clock()

local n = #test_table_4
for i = 1, 1000000 do
   test_table_4[n + i] = i  --Directly placing our values at the end of the table 1,000,000 times.
end
With multiple inserts, you don't need to get the table length (#) each time.
Note that n + i could be removed too with just:

Code: Select all

local n = #test_table_4
for i = n + 1, n + 1000000 do
   test_table_4[i] = i  --Directly placing our values at the end of the table 1,000,000 times.
end
User avatar
Sir_Silver
Party member
Posts: 286
Joined: Mon Aug 22, 2016 2:25 pm
Contact:

Re: How to draw all objects on screen?

Post by Sir_Silver »

Wow I'm surprised I've never tried to do it that way - nice work :)

Just came in here to edit it and say WOW massive optimization XD

Direct insertion without function overhead time taken : 0.2300000000000002 --This is how long my method took.
Ivan's way : 0.040000000000000036 --This is how long Ivan's method took.

Uh yeah wow... I feel embarrassed for even calling my method an optimization now. :emo:
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: How to draw all objects on screen?

Post by ivan »

Yep, "table.insert" is good when you want to put something in the front or middle of the table (incrementing all following indexes).
If you just want to append values to the end of the table then "#" is probably faster.
User avatar
LeNitrous
Prole
Posts: 29
Joined: Tue Sep 08, 2015 3:25 am

Re: How to draw all objects on screen?

Post by LeNitrous »

Thanks for the ideas but I forgot to mention that each object should have a unique name. This is what my main.lua looks like:

Code: Select all

actors = {}

-- actor(name, position, isVisible)
Chris = actor(Chris, "center", true)
Tristan = actor(Tristan, "left", false)

function love.load()
	table.insert(actors, Chris)
	table.insert(actors, Tristan)
	Chris:speak("Hello there!")
	Tristan:show()
	Tristan:speak("Hi there!")
end

function love.draw()
	for n, actor in ipairs(actors) do
		actor:draw()
	end
end
Unless there's a roundabout way, it would be nice.
Post Reply

Who is online

Users browsing this forum: Bing [Bot], Semrush [Bot] and 21 guests