Using Magnitude to stop player WalkSpeed is not precise?

In my script, the WalkSpeed has to be precisely changed to zero when the magnitude of the player to the part is within the range of 6 to 6.5. Using the code stated below, when walking up to the part, the script works fine and stops the character from moving. However, the problem arises when you tell the command bar to print the magnitude between the player and the part. According to the command bar, the magnitude that the player was currently at was a 4.20, nowhere near 6.

Replication:
Here’s the code:

wait(2)
local Part = script.Parent
local Players = game:GetService("Players"):GetChildren()

for i,v in pairs(Players) do
	local character = v.Character or v.CharacterAdded:Wait()
	local UpperTorso = character.UpperTorso
	while true do
		wait()
		local Magnitude = (Part.Position - UpperTorso.Position).magnitude
		if Magnitude < 6.5 and Magnitude > 6 then
			print("In Magnitude")
			character.Humanoid.WalkSpeed = 0
		end

	end
end

After entering the game, walk towards the part until the script activates and your character is stopped.
In the command bar, write: print((game.Workspace.[Part].Position - game.Workspace.[Player Name].UpperTorso.Position).magnitude). (Replace the words in square brackets). Notice that the magnitude is different from the one specified in the code.

Is there any way I can reduce the difference between where the player actually stops and the specified magnitude? Also, shouldn’t the player stop walking immediately when the criteria is met?

2 Likes

The UpperTorso is animated, and might swing back and forth because of that. Try using the HumanoidRootPart instead.

I’ve tested it out with the HumanoidRootPart and the same problem is still occurring. After printing, magnitude is at ~4.09. I’m thinking that it has something to do with how Roblox stops the character or something. Thanks for the reply though.

1 Like

Maybe a similarly named part is on the Workspace and the command bar picks that part up instead of the one you’re thinking of?

Are we comparing apples to apples here, or oranges? What command did you use to find the distance on the command bar? If you put that script in the command bar into a while loop and continue printing the distance out for a couple seconds, does the “In Magnitude” message appear before, during, or after the desired period? 0.5 studs is a small target, so it may be traveling a large distance between wait’s.

Also, have you considered using Humanoid:MoveTo(point, relativeToPart)? It is also best practice when finding the distance to a player to use the method for it: Player:DistanceFromCharacter(point).

This probably happens because of latency between the server and the client.

Character movement is controlled by the client, and I’m assuming that you’re running the magnitude check on the server.

Because of this, there’s a slight delay between the WalkSpeed being set on the server and it replicating to the client. This means that the character will be able to continue walking towards the part for a split second.

This might explain it better:
image

5 Likes

@PlantChampion I’ve checked the code and I don’t think that is the case. Thanks for the suggestion though.

@IdiomicLanguage The command that I was using was stated in the first post. It finds the magnitude just like how the main script finds for magnitude.

I have also tried changing the print(“In Magnitude”) to print((game.Workspace.[Part].Position - game.Workspace.[Player Name].UpperTorso.Position).magnitude), similar to the command bar but inside the script instead. The script ends up printing the correct magnitude of 6.5, but as Coeptus said, it could be due to the latency between server and client afterwards.

As for :DistanceFromCharacter, I have never used it before. I do want to ask if it has any differences from magnitude and could you use it to solve the problem stated above as compared to magnitude?

Other than that, thanks for the suggestions!

@Coeptus Ahh, I see. Is there any way I can solve this problem? Could I run the magnitude checks in the client instead to get better results?

Thanks for the well thought out explanation!

I’m currently still stumped about how to solve this problem (is this problem even solvable?). If anyone has had any experience with this, could you share your suggestions?

If you just need the player to be 6.5 units away when they stop, why not just teleport them 6.5 units away?

Then latency wont matter.

1 Like

Could you clarify about what you are saying? Currently, for my script, I require the player to walk up to the object before the script sets the player walk speed to 0. It also has to appear realistic and shouldn’t see the player suddenly move back a few studs due to teleportation.

I could be wrong - but it seems like you are emulating a wall or a bear trap or something? The player walks up and then stops dead at a fixed distance?

Because players are physics objects with velocity and momentum (and animation and latency) detecting where they are and sending a message to “stop!” is not going to work accurately.

So you’ll need to live with a bit of a teleport on trigger.

Well, you are rather close to what I’m trying to make. Could you further elaborate on your concept? When should you be teleporting the player?

Excuse me for typing this out on my phone hehe. Totally pseudocode.

—if player is in range of sticky sphere

local vec = stickyPos - playerPos

vec = vec.Unit * stickyRadius

Character.HumanoidRootPart.CFrame = CFrame.new(stickyPos + vec) —angle will be 0 but you get the idea
Character.HumanoidRootPart.Velocity =Vector3.new(0,0,0)
—set walkspeed to 0 too

1 Like

I have never thought of it like that. I’ll try it out and see what happens. Thanks for the new insight into another perspective of scripting!

1 Like

There is more than one problem with your script.
Firstly, you should use the HumanoidRootPart for measuring, because the character parts are animated and could cause problems in your case.

Secondly, you are using a while loop in a for loop, thus you are blocking the for loop from finishing and is applying the while loop to one player only.

Third, you are only getting the players once which is 2 seconds after the script executes. So any new player that joins will be ignored, and when a player leaves the script will probably brake.

This is a fixed script, hopefully, it’s what you’re looking for

wait(2)
local Part = script.Parent
local Players = game:GetService("Players")

while wait() do
 for i,v in pairs(Players:GetChildren()) do
  local character = v.Character or v.CharacterAdded:Wait()
  local RootPart = character.HumanoidRootPart
  local Magnitude = (Part.Position - RootPart.Position).magnitude
   if Magnitude < 6.5 and Magnitude > 6 then
	print("In Magnitude")
	character.Humanoid.WalkSpeed = 0
   end
 end
end

Though, a better way would be using the player and character added events.

I’m not 100% sure whether or not this is true, but when I previously tested playing my animation and detecting if the player’s hand touches the part, when the player visibly touches the part, the script doesn’t fire. This has led me to the conclusion that the player’s part position is fixed and does not change with animation. (I’m not the most certain, but I’m pretty sure.)

As for the second problem, thank you for pointing that out. I didn’t realise that this would only apply to one player. thankfully, I only use:

in play Solo which is only for one player, so it doesn’t affect multiplayer. The reason why I don’t use the PlayerAdded and CharacterAdded events would be because the scripts and player character load in different timings for Play Solo and in actual server which pops up with various errors when I’m using Play Solo and I don’t want to keep opening up a new server to test as it takes a long time. As such, I stick with this until the script is ready to transition to a “real” server script which can detect for all players. I do want to thank you for checking my errors, I’ve learnt more about how to tweak my script from the helpful example that you have provided. Thanks!

Ah alright, and you are always welcome.

1 Like

Update: I’ve tried out the method that you have given me but when I use it in Play Solo, the character glitches into the floor and out continuously, have I done something wrong?

It’s a logic error, as always. Strip the Height, or whatever else you don’t want:

P = Part.Position
T = UpperTorso.Position

local Magnitude = (Vector3.new(P.x, 0, P.z) - Vector3.new(T.x. 0, T.z)).magnitude

Good one Rocket,

Anytime you move anything, the last thing u gotta do, after the thing is close enough is give the absolute final position, as the final adjustment.