Inspect Animation still plays even when I am not equipping the tool that has one

Once I equip my melee that has an inspect animation keybinded to F, once I unequip it, I can still press F to “inspect” nothing, here is a video of it.

Link

Here is my script if you need to analyze it.

local Humanoid = Character:WaitForChild("Humanoid")
local Animator = Humanoid:FindFirstChild("Animator")
UIS.InputBegan:Connect(function(Input)
	if Humanoid then
		if Animator then
			local AnimationTrack = Animator:LoadAnimation(InspectAnimation)
			if Input.KeyCode == Enum.KeyCode.F then

			AnimationTrack:Play()

			end	
		end
	end
end)

This is because you’ve not managed the RBXScriptConnection disposable you’ve created by calling ::Connect on UserInputService.InputBegan<RBXScriptSignal> . You need to disconnect these listener(s) when you’re finished with them.

Learn more about this in the following links: RBXScriptSignal and RBXScriptConnection.

e.g.

-- Note: make sure to create a new handler every time someone equips the tool ...
local beganHandler = UserInputService.InputBegan:Connect(function (input, gameProcessed)
  -- your handler here

end)

tool.Unequipped:Connect(function ()
  -- disconnect the handler so we're no longer listening
  -- to the input event(s)
  if beganHandler and beganHandler.Connected then
    beganHandler:Disconnect()
  end
end)

I don’t understand lol, and I think there’s an easier way.

That probably is the easiest way unless you want to check if the tool is equipped every time someone presses the ‘F’ key, but even then, managing and disposing connections etc is good practice to avoid memory leaks - see here.

Which part did you not understand so I can try to clarify?

I don’t really understand everything about what you said

I see, it’s a little tough to help without knowing exactly what you don’t understand.

See the following example but please do see the comments attached - they should be helpful in explaining what’s actually going on line by line.

Example Tool
--[!] SERVICES
--    i.e. any services we need

local PlayerService = game:GetService('Players')
local UserInputService = game:GetService('UserInputService')
local ReplicatedStorage = game:GetService('ReplicatedStorage')


--[!] UTILS
--    i.e. any utility functions

--[=[

  used to check whether a character is alive
    i.e. one that...

        1) has a humanoid with health greater than 0
           and has a state other than Enum.HumanoidStateType.Dead

        2) has a valid root part

  @param character Instance - the character model we want to check
  @returns boolean - reflects the alive status of a character instance

]=]
local function isAlive(character)
  if typeof(character) ~= 'Instance' or not character:IsA('Model') or not character:IsDescendantOf(workspace) then
    return false
  end

  local humanoid = character:FindFirstChildOfClass('Humanoid')
  local humanoidState = humanoid and humanoid:GetState() or Enum.HumanoidStateType.Dead
  local humanoidRootPart = humanoid and humanoid.RootPart or nil
  if humanoidState == Enum.HumanoidStateType.Dead or not humanoidRootPart then
    return false
  end

  return true
end


--[!] MAIN
--    i.e. the main script

local tool = script.Parent                -- the tool this LocalScript relates to
local player = PlayerService.LocalPlayer  -- our local player
local disposables = { }                   -- a list of disposable(s), see below for more information

local function toolEquipped()
  local handle = tool:FindFirstChild('Handle')                                      -- the tool's handle
  local inspectAnimation = ReplicatedStorage:FindFirstChild('SomeInspectAnimation') -- i.e. your animation asset

  -- now that the player has equipped the tool
  -- we need to set up the listener(s) that we
  -- need to perform the action(s) that are required
  --
  local inputBegan = UserInputService.InputBegan:Connect(function (input, gameProcessed)
    -- first, let's make sure we ignore all input events that are related to other
    -- things e.g. when a user is typing on a keyboard in the /chat
    --
    if gameProcessed then
      return
    end

    -- now we need to confirm that the player is:
    --    1) is alive
    --    2) has a valid humanoid & an animator
    local character = player.Character
    local humanoid = isAlive(character) and character:FindFirstChildOfClass('Humanoid') or nil
    local animator = humanoid and humanoid:FindFirstChildOfClass('Animator') or nil
    if not animator then
      return
    end

    -- now let's check to see whether the user tried to do the inspect animation
    local keyCode = input.KeyCode
    if keyCode == Enum.KeyCode.F then
      -- if the animation doesn't exist then let's ignore it
      if not inspectAnimation then
        return
      end

      -- check to see if we already have an inspect track
      -- that we've loaded already
      local track = disposables._inspectTrack
      if not track then
        -- since we don't have the inspect animation track loaded & cached
        -- let's load that track now into the animator
        track = animator:LoadAnimation(inspectAnimation)
        disposables._inspectTrack = track
      end

      -- if the track is already playing then let's ignore this event
      if track.IsPlaying then
        return
      end

      -- play the track, taking 0.1s to fade in
      track:Play(0.1)
    end
  end)

  -- insert the `RBXScriptConnection` from UserInputService.InputBegan
  -- into our `disposables` list so that we can clean it up when the person
  -- unequips the tool
  --
  -- doing this means that we'll no longer be listening to the player
  -- pressing the "F" key so we'll stop trying to do the animation
  -- once the tool is unequipped
  --
  table.insert(disposables, inputBegan)

  -- e.g. do something else once the tool is unequipped...
  --
  --      note: see the unequip handler comments
  --            for why I added this particular example
  --
  if handle and handle:IsA('BasePart') then
    handle.Color = Color3.new(0, 1, 0)
  end
end


local function toolUnequipped()
  -- e.g. do something else when we unequip...
  --
  --      note: I added this so you could press `Backspace`
  --            to drop the tool in the world to visually
  --            show that the unequip handler was being
  --            called etc
  --
  local handle = tool and tool:FindFirstChild('Handle')
  if handle and handle:IsA('BasePart') then
    handle.Color = Color3.new(1, 0, 0)
  end


  -- now let's clean up all of our disposables
  --   i.e. things that we needed when the tool was equipped
  --        but can be disposed now that we're no longer equipping
  --        the tool
  --
  --   e.g. the Input listener(s) and any playing animation(s)
  --          that we added to the `disposables` list when the tool
  --          was equipped and the `toolEquipped()` function was called
  --

  -- first let's create a new disposables
  -- table while we clean up the old disposables
  local elements = disposables
  disposables = { }

  -- now we need to loop through all the element(s)
  -- in the disposables list and dispose of them
  --
  local k, v = next(elements)
  while k do
    local t = typeof(v)

    -- we need to dispose of these elements in different ways
    -- depending on their type
    if t == 'RBXScriptConnection' and v.Connected then
      -- we're a connection, e.g. UIS.InputBegan, so we need to disconnect
      -- the listener
      v:Disconnect()
    elseif t == 'Instance' then
      -- we're an instance so we need to call `:Destroy()` on the instance
      -- pcall is being used here to safely do this in case the instance is
      -- already destroyed
      --
      -- you can read about pcall here: https://create.roblox.com/docs/reference/engine/globals/LuaGlobals#pcall
      --
      if v:IsA('AnimationTrack') then
        -- ::Stop() before ::Destroy() because, atm,
        -- when you destroy an anim without stopping it
        -- the humanoid doesn't resolve the anim weights
        pcall(function ()
          v:Stop()
          v:Destroy()
        end)
      else
        -- we're something other than an `AnimationTrack`
        -- so we just need to destroy the instance instead
        --
        --     e.g. in the case you added a part to the disposable list
        --
        pcall(v.Destroy, v)
      end
    elseif t == 'function' then
      -- we're a function, so let's call that function safely
      -- to perform any arbitrary cleanup method we've added
      pcall(v)
    end

    -- get the next element in the disposables list
    k, v = next(elements, k)
  end
end


-- Now we need to listen to when our tool is equipped/unequipped
--
--   AFAIK the `RBXScriptSignals` from Tool.Equipped & Tool.Unequipped
--   don't need to be disposed of because `::Destroy()` is called on
--   the tool when you die and a new tool is inserted into the
--   player's `Backpack` from the `StarterPack`
--
tool.Equipped:Connect(toolEquipped)
tool.Unequipped:Connect(toolUnequipped)

Let me know if you have questions.


P.S. I should note that people usually use a library to handle this for them instead of using a disposable list as in the example above.

Popular examples that would be good for you to look at may include: Maid, Janitor, Trove. There’s also an article written by Quenty explaining Maid class usage here that may be helpful to you.

1 Like

I modified it a little but it works! Thank you

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.