Value changing but function not listening

Hello! I’m working on a sword system and I’ve run across this bug which is baffling me!
Basically, I have a “Busy” variable that’s script only (The script is a local script.) It prints perfectly fine, as if the busy variable is changing and working. However the function that checks if busy == false plays whether busy == false or true!

THE PROBLEM AREA
script.Parent.Activated:Connect(function() -- SWING
	if busy == false then
		if swingCD == 1 then
			animtrack2:Play()
			swingCD = 2
			animtrack2.Stopped:Wait()
			--busy = false
		elseif swingCD == 2 then
			animtrack3:Play()
			swingCD = 3
			animtrack3.Stopped:Wait()
			--busy = false
		elseif swingCD == 3 then
			animtrack4:Play()
			swingCD = 1
			animtrack4.Stopped:Wait()
			--busy = false
		end
	end
end)
THE WHOLE SCRIPT
--GENERAL
UIS = game:GetService("UserInputService")
local swingCD = 1
busy = true

--ANIM 1: IDLE
local anim = script.Parent.Animations.Idle
local char = game.Players.LocalPlayer.Character or game.Players.LocalPlayer.CharacterAdded:Wait()
local load = char:WaitForChild("Humanoid"):FindFirstChild("Animator")
local animtrack = load:LoadAnimation(anim)

--ANIM 2: BLOCKING
local anim1 = script.Parent.Animations.Block
local animtrack1 = load:LoadAnimation(anim1)

--ANIM 3: SWING1
local anim2 = script.Parent.Animations.Swing1
local animtrack2 = load:LoadAnimation(anim2)

--ANIM 4: SWING2
local anim3 = script.Parent.Animations.Swing2
local animtrack3 = load:LoadAnimation(anim3)

--ANIM 5: SWING3
local anim4 = script.Parent.Animations.Swing3
local animtrack4 = load:LoadAnimation(anim4)
--------------------------------------------------

script.Parent.Equipped:Connect(function() -- EQUIP IDLE ANIM
	animtrack:Play()
end)

script.Parent.Unequipped:Connect(function() -- UNEQUIP STOP IDLE ANIM
	animtrack:Stop()
end)

UIS.InputBegan:Connect(function(input, gameProcessedEvent) -- BLOCK START
	if busy == false then
		if gameProcessedEvent then return end
		if UIS:IsKeyDown(Enum.KeyCode.F) then
			animtrack1:Play()
			busy = true
		end
	end
end)

UIS.InputEnded:Connect(function(input, gameProcessedEvent) -- BLOCK END
	if gameProcessedEvent then return end
	if UIS:IsKeyDown(Enum.KeyCode.F) then else
		animtrack1:Stop()
		busy = false
	end
end)

script.Parent.Activated:Connect(function() -- SWING
	if busy == false then
		if swingCD == 1 then
			animtrack2:Play()
			swingCD = 2
			animtrack2.Stopped:Wait()
			--busy = false
		elseif swingCD == 2 then
			animtrack3:Play()
			swingCD = 3
			animtrack3.Stopped:Wait()
			--busy = false
		elseif swingCD == 3 then
			animtrack4:Play()
			swingCD = 1
			animtrack4.Stopped:Wait()
			--busy = false
		end
	end
end)

Some solutions I’ve tried;

  • Making a boolvalue and basing the if busy == false check on that.
  • Using a server script to change the boolvalue and using RemoteEvents from the local script (problem script) to the server script.

I’ve also had strange behavior when it comes to using a boolvalue in explorer, namely the value randomly switching between true and false. There is no other script that is effecting the busy value.

Here is the strange behavior:
https://gyazo.com/aafa80b447cf341fb7c71a2aef790026
Here is the value printing right (The script still plays the part that’s not supposed to play when busy == true):
https://gyazo.com/98a7f473f3c8feebddae7ef7a5889b60

2 Likes

Print the busy value before the if to see what it’s supposed to be, with an easily identified name (I know, Output will tell you the line number of the script, but if you have multiple ifs then it’s easier with the name of the function you’re dealing with.
For example in this function:

UIS.InputBegan:Connect(function(input, gameProcessedEvent) -- BLOCK START
    print("InputBegan, busy = ", busy)
	if busy == false then
    --code

Then do the same for all the other functions that check busy. It should tell you where the issue is.
Then while testing, select the busy bool in the Explorer and check the Properties to see what the bool is changing to and compare them.

1 Like

Hello, I forgot to mention that I’ve already done that and it still prints just fine. It goes busy when it’s supposed to and it goes un-busy when it’s supposed to. I also forgot to mention but I’ve return the value back to being a script-only thing without a bool, though it was showing the same thing.

1 Like

its possible im not correctly understanding the problem, but my initial thought is that shouldnt busy be set to true after the “if busy == false then” line? And then set it back to false at the end of that sequence?

Yes, and it is doing that as you described.

I haven’t tested this, but I wrote up a quick state machine implementation for this that should help organize your code and avoid bugs like this.

--!strict

local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")

local tool = script.Parent
local animationsContainer = tool:FindFirstChild("Animations")
local localPlayer = Players.LocalPlayer :: Player
local character = localPlayer.Character or localPlayer.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid") :: Humanoid
local animator = humanoid:WaitForChild("Animator") :: Animator

local BLOCK_KEY_CODE = Enum.KeyCode.F

local animationTracks: { [string]: AnimationTrack } = {
	Idle = animator:LoadAnimation(animationsContainer.Idle),
	Block = animator:LoadAnimation(animationsContainer.Block),
	Swing1 = animator:LoadAnimation(animationsContainer.Swing1),
	Swing2 = animator:LoadAnimation(animationsContainer.Swing2),
	Swing3 = animator:LoadAnimation(animationsContainer.Swing3),
}

type State = string
local states: { [string]: State } = {
	Idle = "Idle",
	Blocking = "Block",
	MidSwing = "MidSwing",
	Swung1 = "Swung1",
	Swung2 = "Swung2",
}

type Action = string
local actions: { [string]: Action } = {
	Block = "Block",
	Unblock = "Unblock",
	Swing = "Swing",
}

type Transition = () -> nil

local currentState: State = states.Idle

local function doBlock()
	animationTracks.Block:Play()
	currentState = states.Blocking
end

local function doSwingAsync(swingAnimationTrack: AnimationTrack, nextState: State)
	currentState = states.MidSwing
	swingAnimationTrack:Play()
	swingAnimationTrack.Stopped:Wait()
	currentState = nextState
	task.delay(1, function()
		if currentState == nextState then
			currentState = states.Idle
		end
	end)
end

local transitions: { [Action]: { [State]: Transition } } = {
	[actions.Block] = {
		[states.Idle] = doBlock,
		[states.Swung1] = doBlock,
		[states.Swung2] = doBlock,
	},
	[actions.Unblock] = {
		[states.Blocking] = function()
			currentState = states.Idle
		end,
	},
	[actions.Swing] = {
		[states.Idle] = function()
			doSwingAsync(animationTracks.Swing1, states.Swung1)
		end,
		[states.Swung1] = function()
			doSwingAsync(animationTracks.Swing2, states.Swung2)
		end,
		[states.Swung2] = function()
			doSwingAsync(animationTracks.Swing3, states.Idle)
		end,
	},
}

local function isEquipped()
	return tool.Parent == character
end

local function tryAction(action: string)
	if not isEquipped() then
		return
	end

	local transition = transitions[action][currentState]
	if transition then
		transition()
	end
end

tool.Equipped:Connect(function()
	animationTracks.Idle:Play()
end)

tool.Unequipped:Connect(function()
	animationTracks.Idle:Stop()
	tryAction(actions.Unblock)
end)

UserInputService.InputBegan:Connect(function(inputObject: InputObject, isProcessed: boolean)
	if isProcessed then
		return
	end

	if inputObject.UserInputType == Enum.UserInputType.Keyboard then
		if inputObject.KeyCode == BLOCK_KEY_CODE then
			tryAction(actions.Block)
		end
	end
end)

UserInputService.InputEnded:Connect(function(inputObject: InputObject, isProcessed: boolean)
	if isProcessed then
		return
	end

	if inputObject.UserInputType == Enum.UserInputType.Keyboard then
		if inputObject.KeyCode == BLOCK_KEY_CODE then
			tryAction(actions.Unblock)
		end
	end
end)

tool.Activated:Connect(function()
	tryAction(actions.Swing)
end)