Is there a better way to check if a person is running or not?

I did an elseif because I’m adding a few other things alongside those conditions.

Also, I’ve tried your script. It definitely stabilized a bit more, though now the problem is every time the user leaves the surface and goes back on it, it fires the events multiple times, multiplying the amount of particles when it shouldn’t do that. Here’s what I mean:

There’s also the problem that when it’s performing any other action (Jumping, FreeFalling, Climbing, etc) it still would theoretically emit particles. What do I do about that?

1 Like

Can you send your script(s)?

–Minimum characters

1 Like

Of course! The whole VFX script is in 2, let me send you both.

local char = script.Parent
local root = char:WaitForChild("HumanoidRootPart")
local humanoid = char:WaitForChild("Humanoid")
local fallDistance
local Event = game:GetService("ReplicatedStorage").BindableEvents.VFX

if humanoid and root then
    local headHeight
    humanoid.FreeFalling:Connect(function(state)
        if state then
            headHeight = root.Position.Y
        elseif not state and headHeight ~= nil then
            fallDistance = headHeight - root.Position.Y
            --print("Fall distance: " .. fallDistance)
        end
    end)
end

--if humanoid.FloorMaterial == nil then
--	return
--end

humanoid.StateChanged:Connect(function(old, new)
    if old == Enum.HumanoidStateType.Freefall and new == Enum.HumanoidStateType.Landed and fallDistance >= 22 then
        local animation = script.Parent:WaitForChild("Animate").land.landAnim
        local dance = humanoid:LoadAnimation(animation)
        dance:Play()
        Event:Fire("Stomp")
        task.wait(0.2)
    end
end)

local isRunning = false
local isIdle = false

local function onRunning(speed)
    if humanoid.MoveDirection.Magnitude > 0 then
        print("Person is running")
        if isRunning == true then
            return
        else
            isRunning = true
            Event:Fire("Run", isRunning)
        end
    else if humanoid.MoveDirection.Magnitude == 0 then
            print("Person stopped or in air")
            print(speed)
            isRunning = false
            Event:Fire("Idle", isRunning)
        end
    end
end

humanoid.Running:Connect(onRunning)

OnAction Script

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local VFXEvent = ReplicatedStorage.BindableEvents.VFX
local TweenService = game:GetService("TweenService")
local Player = game.Players.LocalPlayer
local Effects = ReplicatedStorage.VFX.Effects
local Actions = ReplicatedStorage.VFX.Actions

local UserRunning = false

-- Functions

local function Stomp()
    local VFXFolder = Actions.Stomp
    local InitialSize = Vector3.new(0.15, 1, 2.309)

    for _, child in pairs(VFXFolder:GetChildren()) do
        if child:isA("Model") and child.Name == "Triangles" then
            local Effect = Effects.Spark:Clone()
            Effect.Parent = Player.Character:WaitForChild("Torso")
            local ModelClone = child:Clone()
            ModelClone.Parent = workspace
            ModelClone:MoveTo(Vector3.new(Player.Character:WaitForChild("Torso").Position.X, Player.Character:WaitForChild("Torso").Position.Y - 7, Player.Character:WaitForChild("Torso").Position.Z))

            for _, child2 in pairs(ModelClone:GetChildren()) do
                child2.Size = InitialSize
                local RandomSize = math.random(4, 8)
                local Tween1 = TweenService:Create(child2, TweenInfo.new(0.2, Enum.EasingStyle.Quint, Enum.EasingDirection.InOut), {Size = Vector3.new(child2.Size.X, RandomSize, child2.Size.Z)})
                Tween1:Play()
                Effect.Enabled = true
            end
            task.wait(0.2)
            for _, child2 in pairs(ModelClone:GetChildren()) do
                local Tween2 = TweenService:Create(child2, TweenInfo.new(0.1, Enum.EasingStyle.Quint, Enum.EasingDirection.InOut), {Size = Vector3.new(InitialSize)})
                Tween2:Play()
                Effect:Destroy()
            end
            task.wait(0.1)
            ModelClone:Destroy()
        end
    end
end

local function Run()
    local Time = 0.6
    local VFXFolder = Actions.Run

    for _, child in pairs(VFXFolder:GetChildren()) do
        repeat
            local Leg = math.random(0,1)
            if Leg == 0 then
                local clone = child:Clone()
                clone.Parent = game.Players.LocalPlayer.Character:WaitForChild("Left Leg")
                clone.Position = game.Players.LocalPlayer.Character:WaitForChild("Left Leg").Position
                task.wait(0.4)
                clone:Destroy()
            else if Leg == 1 then
                    local clone = child:Clone()
                    clone.Parent = game.Players.LocalPlayer.Character:WaitForChild("Right Leg")
                    clone.Position = game.Players.LocalPlayer.Character:WaitForChild("Right Leg").Position
                    task.wait(0.4)
                    clone:Destroy()
                end
            end
        until UserRunning == false
    end
end

local function Idle(Condition)
    print("is Idle")
end

-- On Fired

VFXEvent.Event:Connect(function(Action, isRunning)
    UserRunning = isRunning
    if Action == "Stomp" then
        Stomp()
    end

    if Action == "Run" then
        Run()
    end

    if Action == "Idle" then
        Idle()
    end
end)

VFXController Script

To direct you a little, the concern in the first script is from the function OnRunning onwards.

As for the second script, the concern starts from the function Run

2 Likes

I think the first issue stems from the isRunning boolvalue and MoveDirection overlap. Because you’re using MoveDirection, I don’t think it’s necessary to have a valuable named isRunning.

For example, I might write your onRunning function like this:

local function onRunning(speed)
    if humanoid.MoveDirection.Magnitude > 0 then
        print("Person is running")
        Event:Fire("Run", true)
    else if humanoid.MoveDirection.Magnitude == 0 then
           print("Person stopped or in air")
           Event:Fire("Idle", false)
        end
    end
end
3 Likes

Oh! Alright, for some reason I thought it’d be necessary but now that I think of it, you’re right.

1 Like

robloxapp-20230715-1437106.wmv (997.6 KB)

The VFX is… overmultiplying to put it simply.

1 Like

The issue may be that it’s calling your Run() function multiple times, therefore multiple sets of particles are being created. You might to start checking if particles are already in the player and if so, return the function.

2 Likes

Is there a way to prevent it from firing multiple times?

1 Like
local funcRunning = false

local function Run()
    local Time = 0.6
    local VFXFolder = Actions.Run

    if funcRunning  then return end
    funcRunning  = true

    for _, child in pairs(VFXFolder:GetChildren()) do
        repeat
            local Leg = math.random(0,1)
            if Leg == 0 then
                local clone = child:Clone()
                clone.Parent = game.Players.LocalPlayer.Character:WaitForChild("Left Leg")
                clone.Position = game.Players.LocalPlayer.Character:WaitForChild("Left Leg").Position
                task.wait(0.4)
                clone:Destroy()
            else if Leg == 1 then
                    local clone = child:Clone()
                    clone.Parent = game.Players.LocalPlayer.Character:WaitForChild("Right Leg")
                    clone.Position = game.Players.LocalPlayer.Character:WaitForChild("Right Leg").Position
                    task.wait(0.4)
                    clone:Destroy()
                end
            end
        until UserRunning == false
    end
    funcRunning = false
end

Potentially something like this where you have a variable that has whether or not the function is running.

2 Likes

Oh wow! It works much better! Though the part where any action is committed is still a problem…

1 Like

What do you mean?

–Minimum character limit

1 Like

So, when a person jumps, freefalls, climbs, etc. The particles still emit because it still thinks the person is moving but it doesn’t detect what kind of action it is.

1 Like

How long does it take until it stops emitting? Could you send a more up to date video?

1 Like

If you’re specifically looking for running, there are two conditions you need to meet

  1. Are they on the ground?
    We can check if they are by using raycasts (booo :nauseated_face: ) or by checking the floor material (much smart :+1: ). Remember, if they are not on the ground, floormaterial is nil.
  2. Are they moving?
    The easiest way to do this is just to check if Humanoid.MoveDirection.Magnitude is > than 0.5.

All you need to do is plug it into a loop, such as RenderStepped or a connection where the HRP’s position changes.
Hope this helped!

2 Likes

Sorry for late response! Sure!
robloxapp-20230715-1542297.wmv (2.5 MB)
I hope this is somewhat of a clear footage.

It is slightly delayed, like by 0.5 seconds ish…

1 Like

Hi!

So, I’ve infact used Floor Material as I mentioned earlier. It didn’t go very well. I’ve tried manipulating a lot, checking if it’s Enum is Air, if it’s nil, if not Air, etc you get the gist.

As for Raycasts, I have NEVER tried raycasts. If I’m honest I’m scared to, they seem complicated LOL but I should check up on it!

As for the second point, why 0.5 if you don’t mind me asking? :slight_smile:

Thank you for letting me know these tips! I’ll try my best to address/implement them the best way I can!

1 Like

Ah, I see! Didn’t notice that you’d already tried it.
Raycasts are not worth it for this. Although they are really quite performance-efficient, it’s really just overdoing it when there is a specific property designed specifically for this job.
As for the 0.5, I honestly can’t remember. I believe it’s because if you rotate the character using shift lock vel sim, then it is 0.5 even if you aren’t “moving” per se.
Anyways, the total condition is simply:

if hum.MoveDirection.Magnitude > 0.5 and hum.FloorMaterial then
    -- lorem ipsum dolor sit amet
end

This needs to be put into some kind of loop though.

2 Likes


This would be theoretically correct right?

Also, shift lock is disabled on my game! Should I still apply the 0.5 rule you’ve mentioned?

1 Like

Yep! Just out of curiosity, are you firing to the server for the VFX to be played? It’s way, way less laggy if you play VFX on the client.
Also, I would recommend just using else instead of elseif (it’s elseif, not else if, btw) because these two conditions are the sufficient conditions for running. If either aren’t met, the player is not running.
p.s, just cause it looks cool, you don’t even need to specify if it is nil, you can just say if humanoid.MoveDirection.Magnitude > 0 and humanoid.FloorMaterial then.
If shift-lock is disabled, you can keep the > 0 instead of > 0.5.

2 Likes

Okay so to give you the reason why I’m using BindableEvents, the scripts are located in 2 locations, OnAction being in StarterCharacterScripts and VFXController on StarterPlayerScripts. The reason why I did this is because combining them to one wasn’t working out for me. If I placed the script in the PlayerScripts, it wouldn’t find character properly (not sure why) and if I put it in CharacterScripts, everytime the person dies, the script would reset and it would mess it up for some reason.

(I also made it that way because it felt a bit more organized, but if you have any ideas to make it into one without of any of the above mentioned happening, that would be great!)

Oh, alright! Thanks for letting me know the difference between else if and elseif!

It works like that? I didn’t even know! The more you know haha. Thanks for letting me know! (I love how I used know 3 times at one go)

P.S the onRunning() function looks something like this now:

local function onRunning(speed)
    if humanoid.MoveDirection.Magnitude > 0 and humanoid.FloorMaterial then
        print("Person is running")
        Event:Fire("Run", true)
    else
        print("Person stopped or in air")
        Event:Fire("Idle", false)
    end
end
1 Like