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?
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
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
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.
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.
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.
If you’re specifically looking for running, there are two conditions you need to meet
Are they on the ground?
We can check if they are by using raycasts (booo ) or by checking the floor material (much smart ). Remember, if they are not on the ground, floormaterial is nil.
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!
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?
Thank you for letting me know these tips! I’ll try my best to address/implement them the best way I can!
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.
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.
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