Function within a function

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
onedaysnotice
Citizen
Posts: 63
Joined: Sun May 13, 2012 2:49 am

Function within a function

Post by onedaysnotice »

what does it do? I've only ever called a function, so I'm not clear on what putting a function in a function does.

like for santos' example before,

Code: Select all

function createSwervingEnemy(image, x, ySpeed)
   local enemy = createEnemy(image, x, ySpeed)
   enemy.startTime = love.timer.getTime()
   enemy.fixedX = x

   function enemy.move(dt)
      enemy.x= enemy.fixedX + math.cos((love.timer.getTime() - enemy.startTime) * 10) * 10
      enemy.y = enemy.y + enemy.ySpeed * dt
   end

   return enemy
end
does the nested enemy.move override the base enemy.move or something? Whats the difference between that and just calling the function within the other function and just passing variables to it? :s
User avatar
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

Re: Function within a function

Post by Inny »

Lua functions are first-class objects, meaning that when you write the code function() end you're creating an object. That object inherits the lexical environment that it lives in, meaning that whatever variables were created in the same place it was, it can get at. This is called a "Closure" and the point of closures is to act as an alternate place to store data, as opposed to a table.

In the code you list, the enemy table has a function in it called "move". If you create a different enemy, then the move function in the second enemy will be different from the move function in the first enemy. Technically speaking, it's not even necessary to put all of that data in the enemy table, and we could write the function like this:

Code: Select all

function createSwervingEnemy(image, x, ySpeed)
   local enemy = createEnemy(image, x, ySpeed)
   local startTime = love.timer.getTime()
   local fixedX = x
   local getTime = love.timer.getTime -- Notice here that I store a function in a variable
   local cos = math.cos

   function enemy.move(dt)
      enemy.x= fixedX + cos((getTime() - startTime) * 10) * 10
      enemy.y = enemy.y + enemy.ySpeed * dt
   end

   return enemy
end
Now, startTime and fixedX are properties of the closure, and not of the enemy table. enemy.move is the only function who can access them and nobody else.

One important note about closures is that they gobble up memory at a faster rate than tables. That's why people tend not to use closures this way. If you do use them (and you technically always are), don't make them too often, or only use them in places where speed is more important than memory.
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Function within a function

Post by Robin »

onedaysnotice, you should be aware that this:

Code: Select all

   function enemy.move(dt)
      ...
   end
is the same as this:

Code: Select all

   enemy.move = function (dt)
      ...
   end
This function can access the enemy table, because it is local to the createSwervingEnemy function.

If the function didn't try to access that table, because it used an extra argument for example, you could use this to mean the same:

Code: Select all

function _move_common_function(enemy, dt)
   enemy.x= enemy.fixedX + math.cos((love.timer.getTime() - enemy.startTime) * 10) * 10
   enemy.y = enemy.y + enemy.ySpeed * dt
end

function createSwervingEnemy(image, x, ySpeed)
   local enemy = createEnemy(image, x, ySpeed)
   enemy.startTime = love.timer.getTime()
   enemy.fixedX = x
   enemy.move = _move_common_function
   return enemy
end
Then, of course, instead of calling enemy.move(dt), you would have to do enemy.move(enemy, dt), or in short enemy:move(dt).
Help us help you: attach a .love.
onedaysnotice
Citizen
Posts: 63
Joined: Sun May 13, 2012 2:49 am

Re: Function within a function

Post by onedaysnotice »

I'm still hugely confused about all this. :( What are some other ways create different behaviours for each enemy? :S
Santos
Party member
Posts: 384
Joined: Sat Oct 22, 2011 7:37 am

Re: Function within a function

Post by Santos »

onedaysnotice wrote:does the nested enemy.move override the base enemy.move or something?
Yes! :ultraglee:
onedaysnotice wrote:Whats the difference between that and just calling the function within the other function and just passing variables to it? :s
I'm not quiiite sure what you mean sorry, feel free to elaborate if you're still unclear.

So basically.

In that example, createEnemy is the main enemy factory. You tell it what you want in the form of (parameters, like, this), it gives you an enemy object.

:roll: <( Hi, uh, could I get an enemy, with an image of 'enemyWithNiceShirt.png', an x-position of 200, aaand a y-speed of like... I dunno... 100? )
:joker: <( YOU CERTAINLY CAN. HERE IT IS. I HAVE "RETURNED" IT. AN ENEMY OBJECT JUST FOR YOU. IT HAS TWO FUNCTIONS, "UPDATE" AND "DRAW". I HOPE YOU LIKE IT AS MUCH AS I LIKED CREATING IT. )
:o: <( Uh, th-thank you. )

thisIsAnEnemy = createSwervingEnemy('enemyWithNiceShirt.png', 100, 100)

createSwervingEnemy is like some... enemy customizer shop. It gets an enemy manufactured from the main factory like everyone else, keeps it in its studio locally, and then adds and replaces some parts of the object, and then returns its own, customized version!

:roll: <( Hey Swerve, could I get the usual? )
:emo: <( Aight. )

someEnemyWithTheirSwerveOnWhateverThatMeans = createSwervingEnemy('enemyWithQuestionableShirt.png', 152, 100)

:joker: <( GOOD EVENING MISTER SWERVINGSWORTH. HOW CAN I... )
:emo: <( 'enemyWithQuestionableShirt.png', 152, 100. Also don't call me that. )
:joker: <( ... K )

local enemy = createEnemy(image, x, ySpeed)
(Which is equivalent to...)
local enemy = createEnemy('enemyWithQuestionableShirt.png', 152, 100)

The enemy returned by createEnemy has the following elements:

x, which is 152
y, which is -50
ySpeed, which is 100
image, which is the image of enemy.png
move, which is a function
draw, which is also a function

Now it's time for createSwervingEnemy to do its thing.

:emo: <( I am tinkering on this object. And talking to myself apparently. )

Code: Select all

enemy.startTime = love.timer.getTime()
enemy.fixedX = x
enemy.move = function(dt)
      enemy.x= enemy.fixedX + math.cos((love.timer.getTime() - enemy.startTime) * 10) * 10
      enemy.y = enemy.y + enemy.ySpeed * dt
end
Now it has a couple of new variables, startTime and fixedX. As for the "move" already existing in the table to begin with, that's just like setting a variable from one value to another, just like this would print 20 instead of 10.

Code: Select all

x = 10
x = 20
print(x) --> 20
Like Robin said, enemy.move = function(dt) is another way of writing function enemy.move(dt), and I've written it like that above. It looks like just changing a variable! And it basically totally is!

So then createSwervingEnemy returns its customized enemy object, and... and everyone lived happily ever after.

I hope this helps... somehow! :ultraglee:

EDIT: I actually agree with Inny below if all this seems insanely confusing. You can see an example of using Middleclass here.
Last edited by Santos on Sat Jul 14, 2012 4:35 am, edited 1 time in total.
User avatar
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

Re: Function within a function

Post by Inny »

You'll probably want to skip on closures and go with Middleclass or SECS, Both available on the wiki: Use them to write a enemy class tree like you would in a more familiar setting. Lua's closures are good stuff, but can be a bit difficult to understand and work with and require a deeper lesson than I've provided here.
onedaysnotice
Citizen
Posts: 63
Joined: Sun May 13, 2012 2:49 am

Re: Function within a function

Post by onedaysnotice »

Thanks for all the help guys :D Gotta love youre explanation Santos :D. I think I have a somewhat clearer understanding of it, but I don't think I would be able to freestyle (do it without people babying me) with it yet :s. I've got my first class of enemies down thanks to you guys :D I've still got to implement their shoot functions though :S I just put them in random spots for now xD

(if it doesn't start, your computer probs doesn't support the 1280x800 resolution, at which case take it off fullscreen at conf :D). arrows and enter from menu navigation, and arrows and spacebar to shoot! :D Oh yeah mash s to spam ships lol.
Archive.love
(6.72 MiB) Downloaded 133 times
Also, do you guys know how to add more ships without going enemies[1], enemies[2], enemies[3], and so on. I usually do this by table.insert, but you can't insert a function, can you? O.O. I've also tried:

Code: Select all

function game.update()
   id = #enemies + 1
   enemies[id] = createEnemy()
   ...
end
but that didn't work :s EDIT: oh wait, now it works LOL I put 'i' instead of 'id'
Santos
Party member
Posts: 384
Joined: Sat Oct 22, 2011 7:37 am

Re: Function within a function

Post by Santos »

You can insert a function, but, in this case, you're actually not, you're inserting a table, because spawn_fighter returns a table.

Code: Select all

enemies = {}
table.insert(enemies, spawn_fighter(1200,50, 155))
Now, enemies[1] is a fighter enemy, and not the "spawn_fighter" function.

As for how to add ships without creating each one individually, you could use a for loop like:

Code: Select all

for i = 1, 10 do
    table.insert(enemies, spawn_fighter(1200, 50 * i, 155))
    -- Or you could do: enemies[i] = spawn_fighter(1200, 50 * i, 155)
end
Oh, and pressing "s" crashed the game for me, hehe. :D
onedaysnotice
Citizen
Posts: 63
Joined: Sun May 13, 2012 2:49 am

Re: Function within a function

Post by onedaysnotice »

Ohhh I get it now XD That'll be useful know for my next game :D My game is almost finished, check it out! :D

viewtopic.php?f=5&t=10015
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Google [Bot] and 46 guests