Distance finder not printing text - lua

I'm trying to make this sign that, when you step close enough, shows a GUI.
To test it, I made it print("It works!") but It won't print anything.
LocalScript:
local Players = game:GetService('Players')
local LocalPlayer = Players.LocalPlayer
for _,Player in next, Players:GetChildren() do
local character = Player.Character
if character and character.Parent and Player ~= LocalPlayer then
local Magnitude = (LocalPlayer.Character.HumanoidRootPart.Position - character.HumanoidRootPart.Position).magnitude
end
end
while true do
if Magnitude < 10 then
print("It's working!")
end
end

The issue is that you have put a LocalScript into a Part in the Workspace. If you check the LocalScript docs, you'll see that ...
a LocalScript will only run Lua code if it is a descendant of one of the following objects:
A Player’s Backpack, such as a child of a Tool
A Player’s character model
A Player’s PlayerGui
A Player’s PlayerScripts.
The ReplicatedFirst service
So to fix your issue, you either need to
A) convert your code to a Script and access the Player objects through the Players service. or
B) move the LocalScript to a place where it will start executing and update the code accordingly.
If you go the route of Option B, follow these steps :
First, move the LocalScript into StarterPlayer > StarterCharacterScripts. This will cause the script to execute when the player's character spawns into the world.
Then, update the code so that it can find the block and show the distance to the player.
local localPlayer = game.Players.LocalPlayer
-- locate the sign in the world
-- local signGui = game.Workspace:WaitForChild("Part"):WaitForChild("UI")
-- set the range where the sign should appear
local DISTANCE_TO_SHOW_SIGN = 10 -- studs
-- every game tick, calculate the distance between the part and the character
game.RunService.Heartbeat:Connect(function()
-- escape if the player's character doesn't exist
if not localPlayer.Character then
return
end
-- calculate the distance to each of the players
-- if two players are close enough to each other, show the sign
local shouldShowSign = false
local myPosition = localPlayer.Character.HumanoidRootPart.Position
for _, otherPlayer in ipairs(game.Players:GetPlayers()) do
if (otherPlayer ~= localPlayer) and (otherPlayer.Character) then
local theirPosition = otherPlayer.Character.HumanoidRootPart.Position
local distBetween = (myPosition - theirPosition)
-- optimization : when calculating distance, try to avoid using square roots
-- use pythagorean theorem : dist = sqrt(x^2 + y^2 + z^2)
-- instead, use dist^2 = x^2 + y^2 + z^2
if (distBetween * distBetween) < (DISTANCE_TO_SHOW_SIGN ^ 2) then
shouldShowSign = true
break
end
end
end
-- show the sign if two players are close enough together
if shouldShowSign then
print("it's working")
end
-- optimization : only set the visibility when it changes
-- if signGui.Visible ~= shouldShowSign then
-- signGui.Visible = shouldShowSign
-- end
end)

Your code has 3 main issues
1: Its a regular script, not a localscript
2: Your while true do loop will timeout when the server starts
3: Magnitude is only defined when doing for i,v
Try this code in a localscript instead
local Players = game:GetService('Players')
local LocalPlayer = Players.LocalPlayer
local Magnitude
while true do
wait()
for _,Player in next, Players:GetChildren() do
local character = Player.Character
if character and character.Parent and Player ~= LocalPlayer then
Magnitude = (LocalPlayer.Character.HumanoidRootPart.Position - character.HumanoidRootPart.Position).magnitude
end
end
if Magnitude ~= nil then
if Magnitude < 10 then
print("It's working!")
end
end
end

Related

How can I wait in a repeat loop in Roblox Lua

game.Workspace.PetRooms.FireRoom.FireRoom.Floor.Touched:Connect(function(hit)
local player = game.Players:GetPlayerFromCharacter(hit.parent)
local char = hit.Parent -- Character
local hum = char:FindFirstChild("Humanoid") -- Humanoid
if hum then -- If humanoid then...
if hum.Health ~= 0 and player.Team == game.Teams.Scientists then -- Makes sure that character is not dead; makes sure that character is a scientist
repeat
wait (10)
hum.Health = hum.Health - 10 -- Kills the character slowly
until hum.Health == 0
end
player.Team = game.Teams.Infected -- Changes the player's team to infected AFTER they die
end
end)
The "wait(10)" is supposed to wait 10 second in between every "- 10" health but the code just waits 10 seconds and then kills the player quickly.
You have this callback connected to the Touched event, and the event will fire every time a player touches the part. If the player is walking across the floor, it will fire every time the player's foot hits it.
What is probably happening is that this event is firing multiple times, and you have a bunch of these repeat-while loops all running at the same time, causing the player's health to drop very quickly.
I would recommend debouncing the connection so that only one loop happens per player :
-- create a map of players touching the floor
local playersTouching = {}
local floor = game.Workspace.PetRooms.FireRoom.FireRoom.Floor
floor.Touched:Connect(function(hit)
-- check that the thing that hit is a player
local char = hit.Parent -- Character
local hum = char:FindFirstChild("Humanoid") -- Humanoid
if hum == nil then
return
end
-- escape if this player is already touching the floor
local playerName = char.Name
if playersTouching[playerName] then
return
end
-- the player was not touching before, so flip the debounce flag
-- everything after this point happens once
playerTouching[playerName] = true
-- check if character is not dead and a scientist
local player = game.Players:GetPlayerFromCharacter(hit.parent)
local isScientist = player.Team == game.Teams.Scientists
local isAlive = hum.Health > 0
if isScientist and isAlive then
-- start a loop to kill the player
while hum.Health > 0 then
wait(10)
hum.Health = hum.Health - 10
end
-- Change the player's team to infected
player.Team = game.Teams.Infected
end
-- clear the debounce flag
playerTouching[playerName] = false
end)
i can't really think of anything besides GetPropertyChangedSignal
try that maybe

Keycard door not working when i put the keycard at replicates storage

I have a key card door but it don't work when I put it in replicatesStorage (it is a gamepass key) can somebody help me it only work when put in starterPack it is currently being given with a localScript at StartGui ** here is code:
script.Parent.Touched:Connect(function(hit)
if hit.Parent.Name == "Clearance1" then
script.Parent.CanCollide = false
script.Parent.Transparency = 0.5
wait(0.5)
script.Parent.CanCollide = true
script.Parent.Transparency = 0
end
end)
StarterPack is already replicated, in a way.
If you use ReplicatedStorage, you'll have to add it to PlayerInstance.Backpack.
Also, script.Parent.Touched is server-side only.
If you wanna access the player's backpack, you can use game.Players.PLAYERNAME.Backpack
The way you're doing this is actually not a very good idea. (no offense)
I would recommend leaving the item inside the StarterPack. If you really don't want to, you can programmatically do it by putting it in their Backpack. Like this:
-- Server script
game.Players.PlayerAdded:Connect(function(player) -- Runs when a player joins the game
player.CharacterAdded:Connect(function() -- Runs when that player respawns or their character loads
local itemCopy = game.ReplicatedStorage.Clearance1:Clone() -- Creates a copy of the item
itemCopy.Parent = player.Backpack -- Puts the item in the player's backpack (inventory)
end)
end)
What that code does is: Every time the player spawns, it clones the item and puts it in their inventory.
Now to check if the user has the item when they touch the door, you can do something like this:
-- Server script
script.Parent.Touched:Connect(function(part) -- Activates when a part touches the doorf
local player = game.Players:GetPlayerFromCharacter(part.Parent) -- Gets a player from the part that touched
if player and player.Backpack:FindFirstChild("Clearance1") then -- Makes sure there is a player, and the player has the keycard in their inventory
script.Parent.CanCollide = false -- Makes the part uncollidable
script.Parent.Transparency = 0.5 -- Sets the part to have transparency
wait(0.5) -- Waits half a second
script.Parent.CanCollide = true -- Makes the part collidable
script.Parent.Transparency = 0 -- Makes the part not transparent
end
end)
Every time the part is touched, it checks if it's a player. If it is, it checks if the player has the item. If so, it runs the code.

How do I make a shield that lasts for 30 seconds in Roblox using a dev product?

When someone buys my dev product I want them to get a visible shield that lasts for 30 seconds.
Here's the code I have tried:
local mpService = game:GetService("MarketplaceService")
local Debris = game:GetService("Debris")
local function giveForcefield(player, duration)
local character = player.Character
if character then
local forceField = Instance.new("ForceField")
forceField.Visible = true
forceField.Parent = character
if duration then
Debris:AddItem(forceField, duration)
end
end
end
mpService.ProcessReceipt = function(purchaceInfo)
local plr = game:GetService("Players"):GetPlayerByUserId(purchaceInfo.PlayerId)
if purchaceInfo.ProductId == xxxxxxx then
game.Players.PlayerAdded:connect(function(plr)
repeat wait() until plr.Character
local char = plr.Character
giveForcefield(plr, 30)
local forceField = Instance.new("ForceField")
forceField.Visible = true
forceField.Parent = char
end)
end
return Enum.ProductPurchaseDecision.PurchaseGranted
end
I can buy the dev product but after the code runs nothing happens.
I have tried a lot of things but I am a bit lost.
Can I get some help, please?
Both of the ways that you have coded for creating and destroying the ForceField are valid, but as Piglet has suggested, your code simply isn't getting called. Your code is saying, "once someone buys this product, wait for another player to join and spawn into the game, then give that new player the forcefield."
Often times, game.Players.PlayerAdded:connect(function(plr) is used as a way to get access to a Player object, but you have already gotten the Player object by calling GetPlayerByUserId, so you can just use that to get access to the Character model.
On an unrelated note, you should only mark a product as PurchaseGranted once the product has been successfully given. By having PurchaseGranted as the default return state, you run the risk of someone buying a product that hasn't been configured yet, and you will just end up taking their money without giving them anything in return.
local PlayerService = game:GetService("Players")
local Debris = game:GetService("Debris")
mpService.ProcessReceipt = function(purchaceInfo)
-- make sure that the player is still in the game
local plr = PlayerService:GetPlayerByUserId(purchaceInfo.PlayerId)
if not plr then
warn("Player could not be found. They might have left")
return Enum.ProductPurchaseDecision.NotProcessedYet
end
if purchaceInfo.ProductId == xxxxxxx then
-- check if their character has spawned
local char = plr.Character
if not char then
warn("Could not find player's character in game")
return Enum.ProductPurchaseDecision.NotProcessedYet
end
-- grant the force field
local forceField = Instance.new("ForceField", char)
-- destroy the force field after a few seconds
local duration = 30
Debris:AddItem(forceField, duration)
-- mark this item as granted
return Enum.ProductPurchaseDecision.PurchaseGranted
end
-- not sure what thing they bought
warn("Unprocessed item receipt", purchaseInfo)
return Enum.ProductPurchaseDecision.NotProcessedYet
end

Roblox game script to detect "sole survivor" winner

I am working on a 6 player rounds-based Roblox game with each round ending if either time runs out OR if every player except for one survivor remains. After each round players (and those killed during the round) are teleported back to a lobby, followed by a brief intermission, and then teleported bak into a map to start a new round (there are lots of different maps chosen randomly). My strategy for this game was to create a large region surrounding all the maps and during the rounds as the timer is counting down, continually detect the # players in the region. When that number gets to 1 the round would end and a winner would be declared. There are 2 problems with this approach:
I had to create a huge region to encompass all the different maps and as I add more maps the region will have to get bigger and bigger. Is detecting # players in such a large region when at most 6 players will be in only one map at a time?
My player detection script isn't working. It detects one player but when there are 2 players it still returns "1" as # of players. To trouble shoot this problem I created a simple game that detects when a player is in a region (gray baseplate) and when they leave the region (red baseplate). When I run the game - https://www.roblox.com/games/6060804433/Rounds - publicly with 2 players in the game it only returns 1 player in the game (which I display in an upper left GUI). My code is below - I think the problem is with how the table / dictionary playersFound works.
CODE:
regionPart = game.Workspace.RegionFromThisPart -- I created a transparent cube to define the region
pos1, pos2 = (regionPart.Position - (regionPart.Size/2)),(regionPart.Position + (regionPart.Size/2))
region = Region3.new(pos1,pos2)
Status = game.ReplicatedStorage.Status
wait(5)
while wait(1) do
partsInRegion = workspace:FindPartsInRegion3(region, game.Workspace.Baseplate,1000)
playersFound = {} -- table of players found
for i, part in pairs (partsInRegion) do
if part.Parent:FindFirstChild("Humanoid") ~= nil then
playersFound["playerinregion"] = part.Parent -- add players character to table
print (i, part) -- 0
end
end
function Length(playersFound)
local counter = 0
for _, v in pairs(playersFound) do
counter =counter + 1
end
return counter
end
Status.Value = Length(playersFound) .." players in region"
end
Bonus question: The GUI I created to show the number of players in the region shows on my screen but not the other player's scree (it says "label" on their screen).
I have spent an incredible amount of time spinning my wheels and would GREATLY appreciate help solving this problem.
Thank you!
You're absolutely right that your issue stems from how you're putting players into your playersFound table. Because you are using the same key in the table every time, you are overwriting the old player with any new one you find.
Instead, try using a unique key to hold onto the players. This could be i or better yet, the player's name. And since you are just looping over the table again, why not just keep a counter of how many players there are.
local regionPart = game.Workspace.RegionFromThisPart -- I created a transparent cube to define the region
local pos1 = (regionPart.Position - (regionPart.Size/2))
local pos2 = (regionPart.Position + (regionPart.Size/2))
local region = Region3.new(pos1,pos2)
local Status = game.ReplicatedStorage.Status
local BasePlate = game.Workspace.Baseplate
local time = 30 -- seconds
wait(5)
while wait(1) do
-- count how many players are left
local playersFound = {}
local totalPlayersFound = 0
local partsInRegion = workspace:FindPartsInRegion3(region, baseplate, 1000)
for i, part in pairs(partsInRegion) do
if part.Parent:FindFirstChild("Humanoid") ~= nil then
-- keep track of all the players still alive
-- since a player's character is made of many parts, each player will show up multiple times,
-- so only hold onto each player once
local character = part.Parent
if (playersFound[character.Name] == nil) then
playersFound[character.Name] = character
print ("Found Player : ", character.Name)
totalPlayersFound = totalPlayersFound + 1
end
end
end
-- EDIT BASED ON COMMENTS --
-- update the UI based on the game state
if time == 0 then
Status.Value = "Round over!"
break
elseif totalPlayersFound == 1 then
-- since we know that there's only one entry in the playersFound table,
-- grab it using the next() function
local playerName, character = next(playersFound)
StatusValue = "Winner : " .. playerName
wait(5)
break
--elseif totalPlayersFound == 0 then
-- make sure to check in case no players are left
else
Status.Value = tostring(totalPlayersFound) .. " players in round"
time = time - 1
-- DEBUG :
-- print("There are " .. tostring(totalPlayersFound) .. " players in the round")
-- for playerName, character in pairs(playersFound) do
-- print(" - " .. playerName .. " is still in the game")
-- end
end
end
As for your bonus question, you should ask a follow up question and post the code that shows how you're creating the label and updating it.

Low NumberValue does nothing for the script

Instead of depending on the Humanoid's health, I decided to go with custom health. The new health goes by value and works well and I wanted to make it where the character froze for a few seconds and then teleported them to a specific Vector3 value.
I tried writing the script differently, but all of them didn't work at all. I even tried to get it to where the player's position was different but that failed as well.
--Responsible for healing a player's humanoid's health
-- declarations
local Figure = script.Parent
local Head = Figure:WaitForChild("Head")
local Humanoid = Figure:WaitForChild("Humanoid")
local PlayerHealth = game.Players.LocalPlayer.Character.Data.Health
local Player = game.Players.LocalPlayer.Character.Humanoid
if PlayerHealth.Value < 30 then
Player.WalkSpeed = 0
wait(5)
Player.WalkSpeed = 16
end
The script refused to work in general. Even when it was enabled and put in the right place, it never worked.
If I understood it right, you want to teleport the character to a position when it's health reaches a value lower than 30 after frozing it for a few seconds. Then you should check the PlayerHealth value every time it's value changed by connecting it to a function to catch the moment where it health falls below 30:
local Figure = script.Parent
local Head = Figure:WaitForChild("Head")
local Humanoid = Figure:WaitForChild("Humanoid")
local Data = Figure:WaitForChild("Data") --In any case if the data loads after the script runs
local PlayerHealth = game.Players.LocalPlayer.Character.Data.Health
local Player = game.Players.LocalPlayer.Character.Humanoid
PlayerHealth.Changed:connect(function()--Here you check the value every time it changes.
if PlayerHealth.Value < 30 then
Player.WalkSpeed = 0
wait(5)
-- you can add teleportation here.
--Figure:MoveTo(Position)
Player.WalkSpeed = 16
end
end)
Here are some fixes, if this is a server script then change to this:
local Figure = script.Parent
local Head = Figure:WaitForChild("Head")
local Humanoid = Figure:WaitForChild("Humanoid")
local Player = game.Players:GetPlayerFromCharacter(Figure) --It will get the player from his character as server scripts can't access LocalPlayer
local Health = Player:WaitForChild("Data"):WaitForChild("Health")
Health.Changed:Connect(function()
if Health.Value < 30 then
Player.WalkSpeed = 0
wait(5)
-- Add more code here
Player.WalkSpeed = 16
end
end)
else if it was a local script then just change this
local Player = game.Players:GetPlayerFromCharacter(Figure)
to
local Player = game.Players.LocalPlayer
Hope it worked, don't forget to select it as a correct answer and please like it =D

Resources