Passing arguments to a function used as an argument?

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
Razvii
Prole
Posts: 5
Joined: Sat Jul 31, 2021 6:14 pm

Passing arguments to a function used as an argument?

Post by Razvii »

I know this is a more lua related question but I need it inside my love project, I am at least attempting to make my code a bit flexible, so i defined a function that gets called on object collision and takes in an argument of what object class it has to collide with and what function to execute, but im having trouble executing that function with it's own arguments, if I write the function as playSound("hurt.mp3") it gets executed regardless, but if I omit the parenthesis and/or quotation marks how can I pass it the argument of the sound it has to play?
image_2021-08-08_133628.png
image_2021-08-08_133628.png (18.49 KiB) Viewed 8272 times
I might be missing something extremely basic here and if I am I apologize for wasting people's time.
Would also appreciate comments about the way I'm writing the whole project so far and constructive criticism in general.
the function at hand is inside projectile.lua in the attached project file if you want to take a deeper look
Attachments
TopDown.zip
(209.54 KiB) Downloaded 204 times
User avatar
Gunroar:Cannon()
Party member
Posts: 1085
Joined: Thu Dec 10, 2020 1:57 am

Re: Passing arguments to a function used as an argument?

Post by Gunroar:Cannon() »

I don't really understand what you mean... like using a function as an argument and that function also needing arguments? Like if onCollision needs to know which sound to play other than hurt.wav?
Maybe

Code: Select all

function projectiles:update(dt)
    projectiles:onCollision("wall",playSound, "hurt.wav")
end

function projectiles:onCollision(collider, funct, filename)
    for i, v ...
        if v:...
            funct(filename) ---<<<<----
        end
    end
end
Is that it? So maybe now you could add a table with the sounds you want to play and use it ...

Code: Select all

local sounds = {
    wall      = "hurt.wav",
    enemy  = "wack.wav",
    spring  = "boing.wav"
}

function projectiles:update(dt)
    for name, sound in pairs(sounds) do
        projectiles:onCollide(name, playSound, sound)
    end
end
I don't know, that's just a rough example. If you only plan to use play sound for that then I don't even think you need the function argument, unless you want to use anpther function to be called with another argument like takeDamage(amount) :huh: .

As for the style, projectiles is a static class I guess. That's not a very good thing to use often. i.e. you use:

function projectile:update(dt)
projectile:onCollision(...) -- instead of self:onCollision
end


So now you can't call it with a projectile:new(), plus classes(static and non static) should be in capitals so that they aren't confused for normal instances ^^ .

And when you call ipairs(self) does the projectile class/table itself store values in an array. I think it's bad practice to mix these things but I guess it's creative. You could've given it a variable to hold things, like ipairs(projectile.instances)?

Oh, and wav files take up a lot of space. They can make small games huge. Maybe try ogg or mp3?
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
Razvii
Prole
Posts: 5
Joined: Sat Jul 31, 2021 6:14 pm

Re: Passing arguments to a function used as an argument?

Post by Razvii »

Gunroar:Cannon() wrote: Sun Aug 08, 2021 11:41 am I don't really understand what you mean... like using a function as an argument and that function also needing arguments? Like if onCollision needs to know which sound to play other than hurt.wav?
Maybe

Code: Select all

function projectiles:update(dt)
    projectiles:onCollision("wall",playSound, "hurt.wav")
end

function projectiles:onCollision(collider, funct, filename)
    for i, v ...
        if v:...
            funct(filename) ---<<<<----
        end
    end
end
Is that it? So maybe now you could add a table with the sounds you want to play and use it ...

Code: Select all

local sounds = {
    wall      = "hurt.wav",
    enemy  = "wack.wav",
    spring  = "boing.wav"
}

function projectiles:update(dt)
    for name, sound in pairs(sounds) do
        projectiles:onCollide(name, playSound, sound)
    end
end
I don't know, that's just a rough example. If you only plan to use play sound for that then I don't even think you need the function argument, unless you want to use anpther function to be called with another argument like takeDamage(amount) :huh: .

As for the style, projectiles is a static class I guess. That's not a very good thing to use often. i.e. you use:

function projectile:update(dt)
projectile:onCollision(...) -- instead of self:onCollision
end


So now you can't call it with a projectile:new(), plus classes(static and non static) should be in capitals so that they aren't confused for normal instances ^^ .

And when you call ipairs(self) does the projectile class/table itself store values in an array. I think it's bad practice to mix these things but I guess it's creative. You could've given it a variable to hold things, like ipairs(projectile.instances)?

Oh, and wav files take up a lot of space. They can make small games huge. Maybe try ogg or mp3?
This is really helpful advice, but yeah, that collision function is supposed to eventually do more than just play a sound, I just used a sound as an indicator while testing to make sure everything works, but if I ever need the collision function to do more than play a sound I can't have the sound argument be argument #3 of the original function.
Also mind explaining to me a bit more in detail what you mean by static and non static classes? The classes in this case are given by windfield and are only used to determine what can collide with that.
Sorry I'm a bit new to this and this is my best attempt at throwing stuff together to understand how it all works as a whole and then cleaning everything up and expanding on it, which i have started doing by having a collision function, then I ran into this issue.
As for the the wav file part, the hurt file is an mp3?
It's also temporary for checking collisions as I said, also the projectiles table originally ONLY held the projectiles and was used to remove ones that have a velocity under a certain value, which was all disabled for this since having the bullets never despawn helped with checking collisions, I just didn't change it yet.
User avatar
Xii
Party member
Posts: 137
Joined: Thu Aug 13, 2020 9:09 pm
Contact:

Re: Passing arguments to a function used as an argument?

Post by Xii »

A function when passed around is just the function. If you also want to pass around arguments, you have to pass them separately (as you've been trying), or you can wrap the function in a table. In other programming languages this is called a closure or a delegate.

This code is a bit weird for educational purposes, to illustrate how what I'm talking about works. You wouldn't probably actually define functions such as these specifically...:

Code: Select all

function get_delegate(func, args)
	return {func, args}
end

function call_delegate(delegate)
	return delegate[1](delegate[2])
end

function test_func(args)
	for text,_ in pairs(args) do
		print(text)
	end
	return true
end

d = get_delegate(test_func, {"hello world"})
result = call_delegate(d)
assert(result)
-- prints hello world
In this example, the arguments are passed as a table.
Razvii
Prole
Posts: 5
Joined: Sat Jul 31, 2021 6:14 pm

Re: Passing arguments to a function used as an argument?

Post by Razvii »

Xii wrote: Sun Aug 08, 2021 3:54 pm A function when passed around is just the function. If you also want to pass around arguments, you have to pass them separately (as you've been trying), or you can wrap the function in a table. In other programming languages this is called a closure or a delegate.

This code is a bit weird for educational purposes, to illustrate how what I'm talking about works. You wouldn't probably actually define functions such as these specifically...:

Code: Select all

function get_delegate(func, args)
	return {func, args}
end

function call_delegate(delegate)
	return delegate[1](delegate[2])
end

function test_func(args)
	for text,_ in pairs(args) do
		print(text)
	end
	return true
end

d = get_delegate(test_func, {"hello world"})
result = call_delegate(d)
assert(result)
-- prints hello world
In this example, the arguments are passed as a table.
I am gonna keep the second method in mind, it sounds really useful for a lot of applications, but about the first thing you mentioned, How would I pass the arguments as well? calling function1(arg1,function2(argument2)) calls function 2 on every frame, unless I can function1(arg1,function2) and call function2 as function2(arg2) within function 1, which is not useful since function 2 needs to be able to be interchanged with a lot of other functions with their own arguments, from playing sound to.... idk cause damage
User avatar
Gunroar:Cannon()
Party member
Posts: 1085
Joined: Thu Dec 10, 2020 1:57 am

Re: Passing arguments to a function used as an argument?

Post by Gunroar:Cannon() »

Razvii wrote: Sun Aug 08, 2021 2:16 pm Also mind explaining to me a bit more in detail what you mean by static and non static classes? The classes in this case are given by windfield and are only used to determine what can collide with that.

In other languages, like C++ and Java, there are variables that are in a class that are shared by all instances of the class. And lua, being as flexible as it is, can "emulate" this if you choose to also "emulate" classes.

Code: Select all

--let's say you use a lua class lib
Fruit = class:extend("Fruit") --make a new class of name fruit
Fruit.count = 0 --static

--called when a new fruit is made
function Fruit:init(arg one, arg two)
    self.one = one
    self.two = two or one
    Fruit.count = Fruit.count+1
end

function Fruit:grow()
    self.one = self.one+1
end

local apple = Fruit:new(4,5)--apple is an instance of fruit
local orange = Fruit:new(5,6)--so is orange

assert(orange.one~=apple.one) --is true
assert(orange.count==apple.count) --also true

--Now, static variables are in the class itself, not instances.
--Languages with static classes don't allow use of the self keyword 
--and shouldn't be called by insances (because in lua they can be overwritten)

apple.count=5
assert(orange.count==apple.count) --no longer true, causes error. apple.count is overwritten
print(Fruit.count) --prints 2
Now a static class is a class that only uses static variables for some reason(maybe it's a bunch of tools, a special storage class or something).

Code: Select all

Tools = class:extend("Tools")
Tools.num = 0
--you'll have to call init by yourself
--not needed always
function Tools.init() 
...
end

function Tools.add(f,k)
    return f+k
end

function Tools.grow()
    Tools.num = Tools.num+1
end

--could also do:
function Tools:grow()
    --Though java won't allow this(using "self."/"this." in static functions)
    self.num = self.num+1
end
Razvii wrote: Sun Aug 08, 2021 2:16 pm As for the the wav file part, the hurt file is an mp3?
Oh my...it's true, sorry. It seems when I typed mine I put there .wav and that brainwashed me :P
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
User avatar
Xii
Party member
Posts: 137
Joined: Thu Aug 13, 2020 9:09 pm
Contact:

Re: Passing arguments to a function used as an argument?

Post by Xii »

Razvii wrote: Sun Aug 08, 2021 2:16 pm if I ever need the collision function to do more than play a sound I can't have the sound argument be argument #3 of the original function.
Why not? This solves your original question.
RNavega
Party member
Posts: 239
Joined: Sun Aug 16, 2020 1:28 pm

Re: Passing arguments to a function used as an argument?

Post by RNavega »

I'm not an expert and this is untested, just throwing around some ideas:

Code: Select all

projectiles.handlers = {
    ["ground"] = projectiles.onGroundCollision,
    ["enemy"] = projectiles.onEnemyCollision
}


function projectiles:makeCollisionInfo(projectile, type, target, extra)
    return {projectile=projectile, type=type, target=target, extra=extra}
end


function projectiles:update(dt)
    for index, projectile in ipairs(self.allProjectiles) do
        local collisionInfo = self:getBestCollision(projectile)
        if projectiles.handlers[collisionInfo.type] then
            projectiles.handlers[collisionInfo.type](collisionInfo, index)
        else
            error(string.format("Collision type not yet implemented: %s", collisionInfo.type))
        end
    end
end


function projectiles:getBestCollision(projectile)
    -- Test the projectile against all collidable objects.
    -- 1) The ground.
    if (projectile.y + projectile.height) > game.stage.groundHeight then
        return self.makeCollisionInfo(projectile, "ground", nil, nil)
    end
    -- 2) Enemies.
    for i, enemy in ipairs(game.actors.enemies) do
        if projectile:inside(enemy:getBoundingBox()) do
            return self.makeCollisionInfo(projectile, "enemy", enemy, nil)
        end
    end
    -- 3) (...)
end


function projectiles:onGroundCollision(collisionInfo, projectileIndex)
    local p = collisionInfo.projectile
    -- Check the collision info to see if the projectile hits the ground hard, to cause VFX.
    if p.velocity >= game.constants.groundColEffectVelocity then
        game.effects.new("sparks", p.x, p.y)
        game.sounds.play("collision_ground_sparks_01")
    else
        game.sounds.play("collision_ground_01")
    end
    -- Finally, remove the projectile.
    table.remove(projectiles.allProjectiles, projectileIndex)
end


function projectiles:onEnemyCollision(collisionInfo, projectileIndex)
    local p = collisionInfo.projectile
    local enemy = collisionInfo.target
    
    -- Ask the enemy to handle its own damage taking.
    local response = enemy:takeCollision(p)    
    if response == projectiles.constants.RESPONSE_BOUNCE then
        -- Make the projectile bounce away and not cause other damage.
        p:beginBounce()
    else
        table.remove(projectiles.allProjectiles, projectileIndex)
    end
end
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 40 guests