Calling Function from Collided Object in Windfield?

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
namelessarcana
Prole
Posts: 5
Joined: Thu Sep 08, 2022 2:17 am

Calling Function from Collided Object in Windfield?

Post by namelessarcana »

Hi all,

I am a super-newbie, so hopefully I can articulate this question properly, but I am running into an area involving collisions between objects in my test project. Basically, I am using Windfield so help with collisions in a top-down game, and I have a "player.lua" file with a "player" table, and another file, "yarrow.lua" that has a table for an object that the player, in theory, will be able to interact with.

Basically, I am using a function inside player.lua for "interacting" with objects and am checking for collisions with "queryCircleArea" (relevant area of function bolded below):

Code: Select all

function playerInteract(x,y)
    local harvestables = world:queryCircleArea(x, y, 10, {"Harvestable"})
    [b]if #harvestables > 0 then
        for key,value in ipairs(harvestables) do
            --CALL FUNCTION IN COLLIDED OBJECT
            value:harvested()
        end
    else[/b]
        --nothing
    end
end
The "value:harvested()" line is attempting to call the corresponding function in "yarrow.lua." Each "yarrow" object is created containing the "yarow:harvested()" function, bolded below:

Code: Select all

function spawnYarrow (x, y, width, height)
    local yarrow = {}
    if width > 0 and height > 0 then
        yarrow.collider = world:newRectangleCollider(x, y, width, height, {collision_class = "Harvestable"})
        yarrow.yarrowSheet = yarrowSprites.yarrowSheet
        yarrow.yarrowSheet:setFilter("nearest", "nearest")
        yarrow.collider:setFixedRotation(true)
        yarrowX = x
        yarrowY = y
        yarrow.harvested = 0
        yarrow.collider:setType('static')
        yarrow.animation = yarrowAnimations.unharvested

        [b]function yarrow:harvested()
            if yarrow.harvested == 0 then
                yarrow.harvested = 1
            end   
        end[/b]

        table.insert(yarrows, yarrow)
    end
end
All the function is supposed to do currently is change the yarow.harvested field value, which should swap between a "harvested" and "unharvested" sprite for the yarrow object.

Right now, when the "player" queryCircleArea() gets to the point where it tries to call the "value:harvested()" function, it returns the error: "Attempt to call method 'harvested' (a nil value)."

I was under the impression that "queryCircleArea()" returned a table containing all collided objects, which according to my understanding would be the "yarrow" object containing "yarrow:harvested()" but this is clearly not the case, and I am not entirely sure what the next step to resolve this would be. I am sort of at an "I don't know what I don't know" point when it comes to this issue, so any guidance would be appreciated.

Apologies if this doesn't make sense, I am more than happy to clarify anything above. Thanks in advance.
User avatar
pgimeno
Party member
Posts: 3657
Joined: Sun Oct 18, 2015 2:58 pm

Re: Calling Function from Collided Object in Windfield?

Post by pgimeno »

namelessarcana wrote: Sat Sep 17, 2022 5:27 pm All the function is supposed to do currently is change the yarow.harvested field value, which should swap between a "harvested" and "unharvested" sprite for the yarrow object.
I haven't dug into anything else, but you have named your function the same as a field, and that means you have a namespace collision. The error does not correspond to that (I would expect "Attempting to call a number value" rather than a nil value).

Functions are values that you can set variables to. If a variable contains a function, then it behaves like a function. Same goes for table values. This line:

Code: Select all

function yarrow:harvested()
is exactly equivalent to this line:

Code: Select all

yarrow.harvested = function(self)
If you later do `yarrow.harvested = 1` then you're removing the function value and assigning a number value to that field, and next time you attempt to call it, it will say "Attempting to call a number value". Just change the name of either the function or the variable to solve it.

But again, that doesn't explain the "Attempting to call a nil value" error that you're getting; however I haven't dug into it.
namelessarcana
Prole
Posts: 5
Joined: Thu Sep 08, 2022 2:17 am

Re: Calling Function from Collided Object in Windfield?

Post by namelessarcana »

Okay, I changed the function name from "harvested()" to just "harvest()" to avoid the namespace collision, thank you for letting me know that would be an issue.. Unfortunately, the "Attempt to call method 'harvest' (a nil value)" error is still occurring.
User avatar
ReFreezed
Party member
Posts: 612
Joined: Sun Oct 25, 2015 11:32 pm
Location: Sweden
Contact:

Re: Calling Function from Collided Object in Windfield?

Post by ReFreezed »

queryCircleArea() returns a list of colliders, not yarrows, so for each collider you'll have to look through your list of yarrows to find the one with the relevant collider (yarrow.collider).
Tools: Hot Particles, LuaPreprocess, InputField, (more) Games: Momento Temporis
"If each mistake being made is a new one, then progress is being made."
namelessarcana
Prole
Posts: 5
Joined: Thu Sep 08, 2022 2:17 am

Re: Calling Function from Collided Object in Windfield?

Post by namelessarcana »

Okay, I was misunderstanding what queryCircleArea() returned, thank you. Will I need to add a require("yarrow") in player.lua, since all the code for the yarrow object is in the yarrow.lua file? Or is there a way to get them to "talk" to one another without adding a 'require' line? I think that's the part I am really hung up on.
User avatar
ReFreezed
Party member
Posts: 612
Joined: Sun Oct 25, 2015 11:32 pm
Location: Sweden
Contact:

Re: Calling Function from Collided Object in Windfield?

Post by ReFreezed »

All require() does is run the code in the specified module/file if it hasn't already run, and returns whatever the module returned (if anything). Since your code (in the posted snippets, at least) defines global functions, anyone can access those functions as long as those modules were required at some point (presumably in main.lua). When it comes to the yarrow objects (with the :harvested() method etc.), as long as the other code has access to the objects themselves then nothing more is needed.

(Not sure I'm explaining things well.)
Tools: Hot Particles, LuaPreprocess, InputField, (more) Games: Momento Temporis
"If each mistake being made is a new one, then progress is being made."
namelessarcana
Prole
Posts: 5
Joined: Thu Sep 08, 2022 2:17 am

Re: Calling Function from Collided Object in Windfield?

Post by namelessarcana »

No, that makes sense. So then player.lua should have access to the harvest() function. This might be a dumb question, but how would I iterate through the "yarrows" table from inside the "playerInteract()" function? Since the "ipairs(harvestables)" just contains colliders but not the actual "yarrow" object being collided with? Can I get to the specific "yarrow" object being collided with using the collider "value" in the ipairs that the code is detecting?

Sorry for all the questions, like I said, I am a super-newbie. I appreciate everyone's help, having better idea of what queryCircleArea() returns and what "require(...)" actually does is extremely helpful.
User avatar
ReFreezed
Party member
Posts: 612
Joined: Sun Oct 25, 2015 11:32 pm
Location: Sweden
Contact:

Re: Calling Function from Collided Object in Windfield?

Post by ReFreezed »

Just use another ipairs loop, something like this:

Code: Select all

function getYarrowByCollider(collider)
	for i, yarrow in ipairs(yarrows) do
		if yarrow.collider == collider then
			return yarrow
		end
	end
	return nil -- No yarrow found!
end

function playerInteract(x, y)
	local colliders = world:queryCircleArea(x, y, 10, {"Harvestable"})
	for i, collider in ipairs(colliders) do
		local yarrow = getYarrowByCollider(collider) -- Note that we assume the function doesn't return nil.
		yarrow:harvest()
	end
end
We could have put the the logic of the getYarrowByCollider function right before yarrow:harvest(), but since you probably want the same functionality elsewhere (find a yarrow by its collider) it's good to have a dedicated function for it.

Edit: It's also worth mentioning that you could just store a reference to the related yarrow on the collider object itself since it's just a table in this case (i.e. yarrow.collider.yarrow=yarrow), but it's a bit of a hack and has the risk of interfering with the Windfield library unless you know how it works internally (unless there's an official way to attach custom data to colliders). I would advice against this until you know better what you're doing. (Gotta know the rules to know when to break them.)
Tools: Hot Particles, LuaPreprocess, InputField, (more) Games: Momento Temporis
"If each mistake being made is a new one, then progress is being made."
namelessarcana
Prole
Posts: 5
Joined: Thu Sep 08, 2022 2:17 am

Re: Calling Function from Collided Object in Windfield?

Post by namelessarcana »

Thank you so much. I have the functionality working just how I was wanting it to now, thank you so much! I might see if there is a further way to "genericize" the "getYarrowByCollider" function when I am ready to add more things for the player to interact with, but your suggestion is working absolutely perfectly. Thank you all so much!
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 9 guests