"BoolValue.Changed:Wait()" strange behavior

I recently encountered a peculiar behavior while working with the BoolValue.Changed:Wait() function in my code. It seems that the expression repeat task.wait() until IsChasingBoolean.Value is functioning correctly, but when I attempt to achieve the same functionality using the code snippet if not IsChasingBoolean.Value then IsChasingBoolean.Changed:Wait() end, it does not yield the expected results.

Let me provide some context to better understand the issue. I have a BoolValue variable called IsChasingBoolean, which I use to determine whether an entity in my game is currently engaged in chasing behavior. In certain parts of my code, I want to pause execution until the IsChasingBoolean value changes to true.

Initially, I implemented a repeat loop with task.wait() to continuously check the IsChasingBoolean.Value until it becomes true. Surprisingly, this approach works as intended and effectively waits until the value changes before proceeding with the rest of the code.

However, in an effort to optimize my code (the CPU on lower end devices spiked like crazy) and make it more readable, I tried using an if statement in conjunction with IsChasingBoolean.Changed:Wait() instead. The idea was to wait for the value change event to occur and then continue executing the code. Unfortunately, this alternative approach did not produce the desired outcome, and the animations for my entity do not play.

I find this behavior rather puzzling since both the repeat task.wait() until IsChasingBoolean.Value and the if not IsChasingBoolean.Value then IsChasingBoolean.Changed:Wait() end expressions should theoretically achieve the same result.

Here is my code:

local SCP096 = script.Parent.Parent
local Humanoid = SCP096:WaitForChild("Humanoid")

local Run = Humanoid.Animator:LoadAnimation(SCP096:WaitForChild("Run"))
local AttackRunStart = Humanoid.Animator:LoadAnimation(SCP096:WaitForChild("AttackRunStart"))
local AttackRun = Humanoid.Animator:LoadAnimation(SCP096:WaitForChild("AttackRun"))

Run:AdjustSpeed(2)
AttackRunStart:AdjustSpeed(2)
AttackRun:AdjustSpeed(2)

local IsChasingBoolean = SCP096:WaitForChild("IsChasing")
local CanSeeBoolean = SCP096:WaitForChild("CanSee")

local PlayedAttackRunStart = false

while true do
	--// this is too CPU heavy but works repeat task.wait() until IsChasingBoolean.Value

	if not IsChasingBoolean.Value then --// new code
		IsChasingBoolean.Changed:Wait()
	end

	print("Chasing") --//Actually prints for some reason

	repeat --// this doesn't seem to do anything now
		if IsChasingBoolean.Value == true then --// Basically I have a chasing animation for when my entity can see you, and a separate one for when he cannot. That's what this loop is for.
			if CanSeeBoolean.Value == false then
				AttackRunStart:Stop()
				AttackRun:Stop()
				Run:Play()
				Run:AdjustSpeed(2)    
				Run.DidLoop:Wait()
			elseif CanSeeBoolean.Value == true then
				if PlayedAttackRunStart == false then
					PlayedAttackRunStart = true
					Run:Stop()
					AttackRunStart:Play()
					AttackRunStart:AdjustSpeed(2)
					repeat task.wait() until AttackRunStart.IsPlaying == false
				elseif PlayedAttackRunStart == true then
					Run:Stop()
					AttackRun:Play()
					AttackRun:AdjustSpeed(2)
					AttackRun.DidLoop:Wait()
				end         
			end
		end
		task.wait()
	until IsChasingBoolean.Value == false

	print("hmm") --// never actually prints (it shouldn't), main script disables this one before it can print anything.

	Run:Stop()
	AttackRunStart:Stop()
	AttackRun:Stop()
	
	PlayedAttackRunStart = false
end

Remember, all of this functioned properly previously.

I would greatly appreciate any insights or suggestions on why the better approach fails to function as expected. Perhaps there’s a subtle nuance that I’m overlooking, or there’s a better approach to accomplish the desired functionality. Thank you in advance for your assistance!


TL; DR:
For some reason “repeat task.wait() until IsChasingBoolean.Value” is working but “if not IsChasingBoolean.Value then IsChasingBoolean.Changed:Wait() end” is not or is breaking my code.

3 Likes

I’ve tried replicating this myself and it seems like the code works just fine. Have you tried changing the connection to GetPropertyChangedSignal("Value"):Wait()?

It seems like what might be happening is one of two things.

  • Something is changing another property of the boolValue so it unyields the thread

  • The boolValue is true to begin with, and the task.wait() from your repeat loop causes the thread to yield a frame, and in that time the boolValue gets set to false just in time for the until condition to catch the change.

Can you confirm these by printing the boolValue’s Value property before and after yielding for both the Changed signal, and the repeat loop?

2 Likes

Screen Shot 2023-05-29 at 4.41.27 PM

Ok, good.

Screen Shot 2023-05-29 at 4.41.39 PM

Right…

It never prints the third one because the script is disabled before it has the chance to. And yes, I have double checked if the script is getting disabled too early (it wasn’t).

As for GetPropertyChangedSignal("Value"):Wait(), it has the same results.

I tried printing “Looped” below task.wait() in the running loop, but it only printed once which is… interesting.

3 Likes

wait doesn’t this just mean it’s working perfectly then?
before waiting it’s false, and after it’s true… that means it yielded until the value changed to true.

2 Likes

No, because the animations do not play at all.

Edit: just realized I never specified that :sweat_smile:…

2 Likes

That isnt true,
ValueBase Instances have a Different .Changed Property Compared to other Instances, For ValueBase Instances, when .Changed is fired, it will only detect changes from the Value rather than the Property, which is why you usually see something along the lines of newValue in .Changed Events.

2 Likes

The issue you might be facing is that you’re telling LUA that it must only wait IF the value itself is completely nonexistant.

Im a bit confused why you’re having LUA wait for the value itself, because LUA thinks your telling it that you have to wait until the .Value parameter exists, which means your previous method worked because you were waiting for the parameter to UPDATE whereas the method that doesnt work waits for its EXISTANCE


Previous method

local StartClock = tick() repeat task.wait() until workspace.Foobar.Value ; print("Waited for: "..math.floor(tick() - StartClock).." seconds.")

Prints after Value updates accurately
image


Nonfunctional method

local StartClock = tick() if not workspace.Foobar.Value then workspace.Foobar.Changed:Wait() end ; print("Waited for: "..math.floor(tick() - StartClock).." seconds.")

Does not wait for value to update since LUA is told not to wait since the value exists
image


Try this method instead, it waits for the value to change, then the loop checks if the boolean is true, if it’s not, the loop goes back to the top and waits for the boolean to change once again. It will also return back to the top portion if the repeat loop breaks itself after the boolean changes

local SCP096 = script.Parent.Parent
local Humanoid = SCP096:WaitForChild("Humanoid")

local Run = Humanoid.Animator:LoadAnimation(SCP096:WaitForChild("Run"))
local AttackRunStart = Humanoid.Animator:LoadAnimation(SCP096:WaitForChild("AttackRunStart"))
local AttackRun = Humanoid.Animator:LoadAnimation(SCP096:WaitForChild("AttackRun"))

local IsChasingBoolean = SCP096:WaitForChild("IsChasing")
local CanSeeBoolean = SCP096:WaitForChild("CanSee")

local PlayedAttackRunStart = false

while true do
	task.wait()
	IsChasingBoolean.Changed:Wait()
	
	if IsChasingBoolean.Value == true then
		print("Chasing")
		
		repeat
			task.wait()
			
			if IsChasingBoolean.Value == true then --// Basically I have a chasing animation for when my entity can see you, and a separate one for when he cannot. That's what this loop is for.
				if CanSeeBoolean.Value == false then
					AttackRunStart:Stop()
					AttackRun:Stop()
					Run:Play()
					Run:AdjustSpeed(2)    
					Run.DidLoop:Wait()
				elseif CanSeeBoolean.Value == true then
					if PlayedAttackRunStart == false then
						PlayedAttackRunStart = true
						Run:Stop()
						AttackRunStart:Play()
						AttackRunStart:AdjustSpeed(2)
						repeat task.wait() until AttackRunStart.IsPlaying == false
					elseif PlayedAttackRunStart == true then
						Run:Stop()
						AttackRun:Play()
						AttackRun:AdjustSpeed(2)
						AttackRun.DidLoop:Wait()
					end         
				end
			end
		until IsChasingBoolean.Value == false
	end


	--Just remove this code snipped below the "hmm" print, there's no reason for it to be here, and it interferes with the new loop method
end

Im sorry if this does not turn out to work, reply with your results

1 Like

Interesting. As of now, I cannot test this, but I can provide some info on what I was trying to do there

For if not IsLookingBoolean.Value then seems to check if it’s not true. If I were to do if IsLookingBoolean.Value ~= true then iirc it had the same results.

I am checking for this just in case somehow the boolean was true before this code ran, which may be unnecessary.

Will follow up tomorrow if this works or not. Thank you!

1 Like

I see

Please feel free to check this variant, though, it limits the amounts of commands ran per second significantly, which may be of some benefit to you.

Cheers!

1 Like

Unfortunately, it did not work. I even added a task.wait(1) before the boolean was set to ensure that this worked properly. I have no clue what’s going on, since the code after it runs, but that animations do not play whatsoever.

1 Like