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?
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
I might be missing something extremely basic here and if I am I apologize for wasting people's time.Passing arguments to a function used as an argument?
Passing arguments to a function used as an argument?
- Attachments
-
- TopDown.zip
- (209.54 KiB) Downloaded 204 times
- Gunroar:Cannon()
- Party member
- Posts: 1088
- Joined: Thu Dec 10, 2020 1:57 am
Re: Passing arguments to a function used as an argument?
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
Is that it? So maybe now you could add a table with the sounds you want to play and use it ...
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) .
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?
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
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
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?
Re: Passing arguments to a function used as an argument?
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.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?
MaybeIs that it? So maybe now you could add a table with the sounds you want to play and use it ...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
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) .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
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?
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.
Re: Passing arguments to a function used as an argument?
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...:
In this example, the arguments are passed as a table.
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
Re: Passing arguments to a function used as an argument?
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 damageXii 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...:
In this example, the arguments are passed as a table.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
- Gunroar:Cannon()
- Party member
- Posts: 1088
- Joined: Thu Dec 10, 2020 1:57 am
Re: Passing arguments to a function used as an argument?
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
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
Oh my...it's true, sorry. It seems when I typed mine I put there .wav and that brainwashed me
Re: Passing arguments to a function used as an argument?
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
Who is online
Users browsing this forum: Google [Bot] and 61 guests