Metatables: The Darkest Magic

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
Raylin
Prole
Posts: 30
Joined: Thu Dec 30, 2010 8:48 am
Location: Chicago
Contact:

Metatables: The Darkest Magic

Post by Raylin »

Howdy.
I'm trying to learn metatables and I would like a really simple breakdown on how reassigning functions work.
Like, there is another topic in this forum about changing the love:setFilter property and returning the imageData to something.
Could someone break that down or tell me what a metatable really does?

Thanks.
User avatar
Lafolie
Inner party member
Posts: 809
Joined: Tue Apr 05, 2011 2:59 pm
Location: SR388
Contact:

Re: Metatables: The Darkest Magic

Post by Lafolie »

I think you'll have to rephrase your question, I have no idea what that means.
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
Raylin
Prole
Posts: 30
Joined: Thu Dec 30, 2010 8:48 am
Location: Chicago
Contact:

Re: Metatables: The Darkest Magic

Post by Raylin »

Can someone give me a tutorial on metatables?
User avatar
micha
Inner party member
Posts: 1083
Joined: Wed Sep 26, 2012 5:13 pm

Re: Metatables: The Darkest Magic

Post by micha »

You can find a very good online book on Lua here: http://www.lua.org/pil/index.html
Scroll down, the first edition is free to read.

Chapter 13 deals with metatables. However I recommend to read the chapters before as well, as usually the chapters build upon each other.
User avatar
qaisjp
Party member
Posts: 490
Joined: Tue Sep 04, 2012 10:49 am
Location: United Kingdom
Contact:

Re: Metatables: The Darkest Magic

Post by qaisjp »

You can change love:setFilter (or the appropriate function) without metatables.

For example, this will print an output in the console whenever you create an image.

Code: Select all

love.image._newImage = love.image.newImage
function love.image.newImage(filename, ...)
    local obj = love.image._newImage(filename, ...)
    print( "Created an image, the filename may be: " .. tostring(filename) )
    return obj
end

Now for metatables:
setmetatable(tab, tab) is the function.
It returns a metatable, but it also makes the first argument a metatable. This makes it possible to put metatables on _G (which cannot be changed, and is a global variable holding all other variables) or set a variable to be a metatable directly.

Here is my fonts pseudolib:

Code: Select all

-- This code has been created by qaisjp and is part of Little Sticky Destroyer.
local fonts = {}

return setmetatable({}, {
	__index = function(_, fname)
		return setmetatable({}, {
			__index = function(_, k)
				if fonts[fname] and fonts[fname][k] then
					return fonts[fname][k]
				else
					if not fonts[fname] then
						fonts[fname] = {}
					end
					fonts[fname][k] = love.graphics.newFont("fonts/"..fname, k)
					return fonts[fname][k]
				end
			end
		})
	end
})
[code]

Basically, all you have to do is this:
fonts = require("fonts") (or whatever you named the file)

This'll return a metatable, as you can see the first argument is an empty table (not a variable) so for the metatable to take an effect I have to store it.
My library will allow you to directly get fonts instead of creating fonts, like so: 

love.graphics.setFont(fonts["DarkMagic.ttf"][60])

This'll create a font the first time that table is called and store it for safe keeping, this makes it possible to call it every frame without any inefficiency (once the font was created, it wont be created again but the old font will be returned)


The second argument is a table containing Metatable Events [url=http://lua-users.org/wiki/MetatableEvents][link][/url]
The table contains functions, but with a specific index (name of the key); I will explain __index table key.

setmetatable(_G, {
    __index = function(t, k)
    end
})

The above code essentially makes global variables stop working. Every table without a metatable actually has this as the __index:

__index = function(t, k)
      return rawget(t, k)
end

(actually the value of __index == rawget but you get my jist)
rawget has two arguments, table and key. it returns the value of a table with that key, ignoring metatable events. Since __index is called whenever you are trying to receive a variable ( that is, hello = love; because love is a global variable and all global variables are in the _G table ), trying to get the value wont have any effect unless you add it inside your __index.

This code will output the key and value of everything you try to get. Try it at http://lua.org/demo.html
[code]
setmetatable(_G, { __index = function(t, k)
      print(tostring(t) .. " ; " .. tostring(k))
      return rawget(t, k)
end})

-- there is a metatable for when setting, our code wont have any effect when setting.
hello_str, world_str = "hi", "universe"

-- this will make it ouput
abc, def = hello_str, world_str

-- should get an output!
If you read through my fonts code, the first instance of __index has fname, which is the font name (fonts["fontname.extension"])
fonts["fontname.extension"] returns a table, so you could essentially store the font in your script and get any size you want by this code:

Code: Select all

TestFont = fonts["TestFont.ttf"]

function love.draw()
    love.graphics.setFont(TestFont[30])
    love.graphics.printf("testcenter", 0, 0, 800, "center")

    love.graphics.setFont(TestFont[60])
    love.graphics.printf("testcenter big", 0, 90, 800, "center")
end
Now if you were to call fonts["myfont.ttf"][60], it's the same as this:
testfont = fonts["myfont.ttf"]
testfont_60 = testfont[60]

print( testfont_60 == fonts["myfont.ttf][60] )
-- > true

the second metatable checks if the font was already created and return the font that was already created (same filename and size checked)
if it wasn't already created, then check if there is already a font but with a different size, if there isn't already a font with that filename (regardless of the size), then create a font table for that filename inside the LOCAL fonts table (not the global fonts table).
create the new font instead the globalfonttable[filename][size]
return the new font

:) hope you like my tut
Lua is not an acronym.
User avatar
Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: Metatables: The Darkest Magic

Post by Roland_Yonaba »

Raylin wrote:Howdy.
I'm trying to learn metatables and I would like a really simple breakdown on how reassigning functions work.
Like, there is another topic in this forum about changing the love:setFilter property and returning the imageData to something.
Could someone break that down or tell me what a metatable really does?

Thanks.
Well, don't go with the false assumption that to change the behaviour of any Lua function packed in a table, you'll have to use metatables.
As Qaisjp said, just override this function. It works like a charm.
Now, if you do want to learn metatables for *another* purpose, here is a very friendly tutorial, written by BlackBulletIV.
User avatar
Lafolie
Inner party member
Posts: 809
Joined: Tue Apr 05, 2011 2:59 pm
Location: SR388
Contact:

Re: Metatables: The Darkest Magic

Post by Lafolie »

Could someone please enlighten me as to what the hell love:setFilter is?
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
Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: Metatables: The Darkest Magic

Post by Roland_Yonaba »

Maybe he wanted to mention Image:setFilter
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Semrush [Bot] and 8 guests