Make player slower without modifying WalkSpeed

Hello. I’ve been making a Cobweb block similar to Minecraft and it has it’s functions such as: slowing down the player. However, it currently uses WalkSpeed and it causes issues due to players being able to sprint, crouch and other things that can interrupt the transition of speed when leaving.

local InCobweb = {}
game:GetService("RunService").Stepped:Connect(function()
	pcall(function()
		for i,v in pairs(workspace:GetPartBoundsInBox(script.Parent.CFrame, script.Parent.Size)) do
			if v:IsA("BasePart") and v.Anchored == false and v.Parent:FindFirstChildOfClass("Humanoid") and not table.find(InCobweb, v.Parent) then
				table.insert(InCobweb, v.Parent)
				--local lastspeed = v.Parent:FindFirstChildOfClass("Humanoid").WalkSpeed
				task.spawn(function()
					script.Parent.Destroying:Connect(function()
						v.Parent:FindFirstChildOfClass("Humanoid").WalkSpeed = 16
					end)
					pcall(function()
						while v.Parent and v.Parent:FindFirstChildOfClass("Humanoid").Health > 0 and (v.Parent.PrimaryPart.Position - script.Parent.Position).magnitude <= 3 and table.find(InCobweb, v.Parent) do
							pcall(function()
								v.Parent:FindFirstChildOfClass("Humanoid").WalkSpeed = 2
								for e,t in pairs(v.Parent:GetDescendants()) do
									if t:IsA("BasePart") and t.Anchored == false then
										t.AssemblyLinearVelocity = Vector3.new(0, t.AssemblyLinearVelocity.Y * 0.2, 0)
									end
								end
							end)
							task.wait()
						end
					end)
					for i,e in pairs(InCobweb) do
						if e:IsDescendantOf(v) or e == v.Parent then
							table.remove(InCobweb, i)
						end
					end
					v.Parent:FindFirstChildOfClass("Humanoid").WalkSpeed = 16
				end)
			end
		end
	end)
end)

Note that I don’t want the player to be fully unable to move, just really slow as shown in this previous code.

When this happens, have a value thats called stun, that prevents the player from running.

Ideal solution is to have a walkspeed handler. In the handler, allow other scripts to toggle modifiers to the walkspeed.

Each time a modifier is toggled, the handler takes into consideration the wholistic state of the player and determines and absolute walkspeed. This is a very traditional way to implement your goal.

Want assistance with the handler script?

1 Like

but in mc you can still sprint/crouch while in a cobweb, @mc3334’s solution is better

I absolutely second what the person above me said, though I’d also like to add onto it.

There’s two ways to approach this. The quick way is as follows:
To avoid speed effects overriding one another, change walkspeed by multiplying it by a number rather than hardcoding a new value. This allows speed effects to stack.

Or, you could set up a handler for long-term efficiency. This would apply statuses and update a player’s speed. This is important if you plan on making this a long-term/large project.

Just requires 2 modules and 1 controller. SpeedController will access ProfileService (our module) that’ll be used to initialize a player’s stats upon joining a game and create a place for us to store data about a player, such as statuses or speed multipliers. ProfileService will then access SpeedSystem (another module) that would handle updating a player’s speed based on whatever speed multipliers they have active (which we’ll pass into SpeedSystem from ProfileService). We’ll apply speed multipliers via SpeedSystem since it seems like a relevant task for the system to handle.

Here’s what a module script for storing data about a player may look like.

-- just standard stuff for setting up modules
local ProfileService = {}
local SpeedHandler = require(SpeedHandler) -- imagine this is a module script reference
ProfileService.__index = ProfileService -- lets us set up inheritance for efficiency later

local profiles = {} -- We'll store our player profiles here for access later

function ProfileService:setupTracking()
    self.movementStats = {}
end

function ProfileService.new(player) -- Our constructor for player data objects
    profiles[player] = {}    
    local self = setmetatable(profiles[player], ProfileService) -- inheritance set up
    self.name = player.Name
    self.player = player -- just fields for easy access later
--  could add self.character field later and setup tracking so it is updated upon reset

-- We're using proxies and the __newIndex metamethod to track when player speed/jumppower
-- in their profile object changes and then transferring that change to humanoid
    self.movementStats = {}
    local proxy = {}

    setmetatable(proxy, {
      __index = self.movementStats,
      __newindex = function(tbl, key, value)
           local old = self.movementStats[key] -- old value of changed field
           if old ~= value then -- check if a new value was assigned
                 self.movementStats[key] = value
                 SpeedHandler:updateStats(self, key, value) -- update humanoid stats
           end
      end
    })
    self.movementStats = proxy -- just setting up tracking logic w/ the above few lines

    self.speedMultipliers = {}
    local proxy2 = {}
    
    setmetatable(proxy2, {
         __index = self.speedMultipliers,
         __newindex = function(tbl, key, value)
               self.speedMultipliers[key] = value
               SpeedHandler:updateSpeed(self)
         end

    })

    self.speedMultipliers = proxy2
    return self
end

--- These methods update our list of speedMultipliers, triggering the __newIndex
-- metamethod that will update our speed based on our new list of multipliers.
-- Also this is why we needed inheritance, these methods would be executed on profile objects

function ProfileService:addSpeedMultiplier(key, multiplier)
      self.speedMultipliers[key] = multiplier -- will now automatically update speed
end

function ProfileService:removeSpeedMultiplier(key)
      self.speedMultipliers[key] = nil -- updates speed
end

function ProfileService:get(player)
      return profiles[player] --- gives profile
end


return ProfileService

Our SpeedHandler may look like:

local SpeedHandler = {}
SpeedHandler.trackedStats = {
     speed = "WalkSpeed",
     jumpPower = "JumpPower",
}
function SpeedHandler:updateStats(profile, stat, newValue)
      local character = profile.player.Character -- you should probs add onto profileService
--- later so you can just access a character field in it
-- maybe even add a method for getting player character and checking if it's nil or not
      if not character then return end
      local statToUpdate = self.trackedStats[stat]
      local humanoid = character:FindFirstChild("Humanoid")
      if humanoid and statToUpdate then
          humanoid[statToUpdate] = newValue
      end
end

function SpeedHandler:updateSpeed(profile)
       local multiplier = 1
       for _, change in pairs(profile.speedMultipliers) do
             multiplier = multiplier * change
       end
       local speed = profile.movementStats.speed or 16 -- in case of nil
       profile.movementStats.speed = speed * multiplier
end

return SpeedHandler

The gist of this was just to show how you could create a speed handler system that updates speed whenever you change the player’s speed value and takes into account all the various things the player is dealing with. Keep in mind the example hasn’t been refactored or tested at all, it’s just me showing you how you might set this up in your own game.

After this, all you’d need to do to apply a slowness effect properly w/o it being overridden is just get the player’s profile and then add a speed multiplier or remove one.

Why go through all of this? Why do all this modular coding when you could just multiply the walkspeed of the humanoid by 0.1 when going through a cobweb and call it a day? Well…

Modular coding makes debugging easier, testing easier, extending on existing systems a matter of adding data rather than restructuring current code, allows for consistent logic across multiple systems, improves abstraction and readability in code, and allows you to divide-and-conquer large tasks.

IMO, if you’re approaching this minecraft game as a long-term project or plan for it to get large, modular programming is how you’ll make your life a lot easier in the long run.

LMK if you have any questions.