Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

how to have recursive local functions? (Advanced)

Asked by 4 years ago
Edited 4 years ago

to make a long story short: i keep getting a "attempt to index nil value" for a line of code where none of the values are nil. the function actually works if i call it by itself using a click detector, but when i try to call it from other functions in my script, it doesn't. the function has no parameters.

my best guess as to why this is something about local vs. global function/variable interaction and LUA wanting everything to be the same scope

to make a long story detailed: I've been trying to write a (fairly rudimentary) enemy AI. I'm talking about a script inside the humanoid of a generic dummy made with the rig builder.

pretty much all its values and functions are local because that's what the pathfinding examples show. so i have a "check surroundings" function near the bottom of the script since it calls all the other functions that handle things like wandering, searching for players, and pathfinding. the check function basically runs other functions to find a target and seek them or find a point to wander to if there are no valid targets.

this is where the problem is, it's a local function and it's at the bottom, but it needs to be run recursively so the enemy is constantly checking for it's surroundings. i at least wanted to be able to run it say, whenever a path finding destination was reached (the function at the top of the script)

making the function global doesn't seem to help, and frankly i don't have enough experience with LUA so most of my attempts to mess with global variables are shoddy at best.

in the code shown here i tried to get around it using a bindable event, but no dice. it still told me it was an attempt to index a nil value. irrelevant code has been omitted, but it's in the same order it is in the real script.

-- VARIABLES

local CheckEvent = script.CheckArea
local Wander = 0
local WanderGoal = Instance.new("Part",workspace)
local enemy = script.Parent.Parent
local humanoid = enemy.Humanoid
local destination = WanderGoal
local moving = false -- should our boy be moving?
local state = 0 -- 0 is wander, 1 is follow


--FUNCTIONS

--this is the function that's called when a path's waypoint is reached, modified from the       Roblox documentation

local function onWaypointReached(reached)
print("clickboy reached a waypoint")
--irrelevant code redacted


if FindDistance(enemy.HumanoidRootPart.Position, destination.Position) > chaseRange then
    --out of chase range, stop following and reassess state
    print("target is now out of range")
    print("Distance: ",FindDistance(enemy.HumanoidRootPart.Position, destination.Position))

    --this does not work VVV
    CheckEvent:Fire()
    --this is where problems are now arising. we need to once again call the function, basically, to make this recursive, which it needs to be.

    moving = false
    return 

end
--irrelevant code redacted

end

--here's the check surroundings function at the bottom of the script

local function CheckArea()
    print("Checking area!")

    --destination marks current target
        --error happens on the line below
    destination = FindClosestPlayer(enemy.PrimaryPart.Position).PrimaryPart -- find a valid target and assign destination to that
if destination~= nil then
    --we have a target
    state = 1
    followPath(destination) -- start pathfinding it
else
    --wander
    if  state ~= 0 then
        state = 0
        FindWanderDestination() -- determine a wander point and pathfind to it
    end
end

end

--this runs the function just fine with no errors

 local function Clicked()
    print("got a click")
--calling the function here works just fine
    CheckEvent:Fire()
end


--EVENT CONNECTIONS

-- Connect Waypoint reached to a pathfinding waypoint being reached
humanoid.MoveToFinished:Connect(onWaypointReached)

script.Parent.Parent.ClickDetector.MouseClick:Connect(Clicked)

CheckEvent.Event:Connect(CheckArea)

here's the output from clicking on the dummy, it checks surroundings starts pathfinding me (the closest player) once i leave it's range, it's supposed to stop chasing and check surroundings again, but then i get an error

got a click

Checking area!

clickboy reached a waypoint (x15)

target is now out of range

Distance: 31

Checking area!

12:58:03.747 - Workspace.clickboy.Humanoid.Pathfind Enemy:159: attempt to index a nil value

my main question is is there a way to make it not an issue without having to change all my variables and functions to global?

p.s.: sorry, i'm new here :(

0
it appears as if you never defined the "FindClosestPlayer" function theking48989987 2147 — 4y
0
irrelevant code was redacted, the full script was almost 200 lines long and i didn't think people would need to comb through all that, the function is defined and also works no errors happen when i click on the model, it works as intended. but when it gets to the point where it's supposed to check it's surroundings on it's own, then that line gives an error. NeonTadpole 2 — 4y

1 answer

Log in to vote
0
Answered by 4 years ago
  • If FindClosestPlayer returns nil, CheckArea will error before you have a chance to check if destination is nil (since you always index the result with .PrimaryPart)

"this is where problems are now arising. we need to once again call the function, basically, to make this recursive, which it needs to be."

You should be careful using recursion here - Roblox removed tail calls, meaning that if your algorithm uses recursion potentially indefinitely (never returning from your function), you'll eventually run out of stack memory. However, using that CheckEvent BindableEvent will not cause this problem.

0
that was exactly my problem, thank you so much. the difference was that there were no nearby players so the find player was returning nil. i instead had it return the enemy itself in the event that it found nothing and it works just fine. and thank you for the tip, i'll keep that in mind from now on. :) NeonTadpole 2 — 4y
Ad

Answer this question