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.
Related
I'm trying to implement a system in Roblox Studio that allows a player to cause damage to all players nearby when they jump. I'm trying to develop code that is triggered when the player jumps, and as they land, 10 damage is caused to all players (and dummies for testing purposes) nearby. I've tried three or four different approaches, but none of them seem to work. Here's the latest version of my code:
function onJump(player)
-- Get the player's position
local playerPosition = player.Character.HumanoidRootPart.Position
-- Find all players and dummys within a 5 block radius of the player
local objectsInRange = game:GetService("Workspace"):GetDescendants()
local objectsToDamage = {}
for _, object in pairs(objectsInRange) do
if object:IsA("Player") or (object:IsA("Model") and object.Name == "RespawnDummy") then
local objectPosition = object.PrimaryPart.Position
local distance = (playerPosition - objectPosition).Magnitude
if distance <= 5 then
table.insert(objectsToDamage, object)
end
end
end
-- Damage all players and dummys within range
for _, objectToDamage in pairs(objectsToDamage) do
if objectToDamage:IsA("Player") then
objectToDamage.Character.Humanoid:TakeDamage(10)
elseif objectToDamage:IsA("Model") then
objectToDamage:BreakJoints()
end
end
end
-- Connect the function to the player's Jump event
game.Players.Player.Character.Humanoid.Jump:Connect(onJump)
The biggest challenge seems to be simply having the script identify when a player jumps. I'm not getting any error messages - but I'm also not getting any output if I add a 'print' command inside this function, so it doesn't look as though the jump is being picked up at all.
I was trying to make a code that if you touch a coin, then the value of your coins goes up 1 more, but i tried to make and haven't sucess. Here's the code.
script.Parent.Touched:Connect(function(Player)
if Player.Parent:FindFirstChild("Humanoid") then
local Players = game.Players:GetPlayerFromCharacter(Player.Parent)
local Money = Players:GetAttribute("Coin")
Players:SetAttribute("Coin", +1)
task.wait(.2)
script.Parent:Destroy()
end end)
You’ll want to add your previously amount + 1
local Money = Players:GetAttribute("Coin")
Players:SetAttribute("Coin", Money+1) -- Add Money + 1
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
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.
So I'm trying to develop a small coin collecting game on Roblox, and am pretty new to scripting. Basically Every 0.25 - 1.5 seconds, a small part is cloned from (-254, 2, -255) (one corner of the baseplate), to (254, 2, 255) (the opposite corner). That works, but im trying to loop over every object in workspace named coin, and when one is touched, run code (for now im just trying to destroy the object but ill probably just update the Coins leaderstat). It doesn't give me any errors, it just doesnt work. I've also looked all over the internet, and cant find anything.
Code in ServerScriptStorage (spawns cubes and already works, but showed it for help.):
local runservice = game:GetService("RunService")
local interval = math.random(0.25, 1.5)
local coin = game.ServerStorage.coin
local counter = 0
local x = math.random(-254, 254)
local z = math.random(-255, 255)
runservice.Heartbeat:Connect(function(step)
counter = counter + step
if counter >= interval then
counter = counter - interval
local copy = coin:Clone()
copy.Parent = workspace
copy.Position = Vector3.new(x, 2, z)
x = math.random(-254, 254)
z = math.random(-255, 255)
interval = math.random(0.25, 1.5)
end
end)
script in desktop that handles the touching:
for _, v in pairs(workspace:GetChildren()) do
if v.Name == "coin" then
print("foo")
end
end
I hope this is enough to help!
Well as you are new to scripting in roblox let me give you your answer with good practices that may help you a lot.
First in this scenario you dont need to use Heartbeat, instead you could simple use a while loop or a recursive function and a simple wait().
Also you better create a "Coins" Folder in workspace in order to not check other objects
local waitTime = math.random(25,150)/100 --random time between 0.25 and 1.5
while true do --forever loop
wait(waitTime) --waits desired time
local coin = game.ServerStorage.coin:Clone() --cloning your coin
coin.Parent = workspace.Coins --Coins folder
coin.Position = Vector3.new(math.random(0,10),2,math.random(0,10)) --you must use your own position
coin.Touched:Connect(function(hitPart) --here is the touched function
local plr = game.Players:FindFirstChild(hitPart.Parent.Name) --check if the hitPart is part of a player
if plr then
plr.leaderstats.Coins.Value = plr.leaderstats.Coins.Value + 1--here you can increment your coins value in your own value
coin:Destroy()--destroys the coin
end
end)
waitTime = math.random(25,150)/100 --set a new random value to wait next
end
Also you mentioned something about loop every coin in workspace, thats why I said it is better to create a separate folder. So I made a localscript inside StarterPlayerScripts with the following code:
local RunService = game:GetService("RunService") --service
RunService.RenderStepped:Connect(function() --function on every game frame
for i,v in pairs(workspace.Coins:GetChildren()) do --loop on every coin
v.Orientation = Vector3.new(v.Orientation.X,v.Orientation.Y+5,v.Orientation.Z) --increasing Orientation just on Y in order to rotate them
end
end)
I'm doing this on localscript because is just a visual effect and it is never a good idea to send that many functions that quickly serverside. Here is the game I made for you:
https://www.roblox.com/games/5842250223/Help-for-TextBasedYoutube
You can edit the place.
In other words to answer "How to run code when any object with the same name is touched?"
You need to set the function for the object when creating it.
Edit: Also is not a good idea to send to many request to the server in short periods of time, I would recommend you to create a coin ever 2 to 3 seconds or more.