Getting characters inside of an object/area

  1. What do you want to achieve? Keep it simple and clear!

I’m trying to create a red light green light minigame in my game and i’m trying to have it to where only the players in the red light green light area are playing and die when moving

  1. What is the issue? Include screenshots / videos if possible!

It seems to ignore the code in the first photo, and then gives me this error for line 33 (“winner” is a instanced tag I give to the winning player)

rbx


rbx3

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I’ve looked all over the dev hub and couldn’t find anything. I’m new to Lua and most of Roblox Studio, so my abilities right now are a little limited.

This is a joke game I’m making for a close friend, it isn’t supposed to be super polished and if i’m being honest it doesn’t have to work super well as long as it just works.

Forgot to say but i uncommented the code in the first photo and ran it, idk why I had it screenshotted while it was commented

There are honestly a lot of alternatives you could go around with this here, are you just specifically looking for a way to check the first winner of the minigame, along with obtaining every Player that’s currently playing to teleport? If so, let’s start out with your loop here

image

With this line here, you’re attempting to find a String datatype with a table full of Instances hence why that error occurs, where you should instead be doing, is using GetPartsInPart instead to detect a Box Collision within its radius

This function here, requires 2 parameters to pass through which will then return back as a Table of BaseParts so we can check

local DetectBox = workspace.SquidGame:WaitForChild("DetectBox")

local Params = OverlapParams.new()
Params.FilterType = Enum.RaycastFilterType.Blacklist
Params.FilterDescendantsInstances = {DetectBox}

local CurrentParts = workspace:GetPartsInPart(DetectBox, Params) 
-- This will return back a Table of BaseParts that are currently colliding with the Box

Next, we’d want to go ahead and loop through our CurrentParts table cause we want to see if our Parts are specified Parents of Character, and we can call the GetPlayerFromCharacter function to return back as a Player Instance

But keep in mind! We do not want to add multiple Tags, so we create another table which will hold all of the Players that we’ve already searched for, and ignore lines of code if we’ve already confirmed that they’re within that table

  • We use table.find() to check if a BasePart’s Parent is equal to our Character to prevent duplication
  • We use table.insert() to add a value into our Table after confirming
local PlayingPlayers = {}

for _, Part in pairs(CurrentParts) do
    local Target = Part.Parent

    if not Target then return end

    local Plr = game.Players:GetPlayerFromCharacter(Target)
    local DuplicateCheck = table.find(PlayingPlayers, Target)

    if Plr and DuplicateCheck == nil then
        table.insert(DuplicateCheck, Target)

        local Tag = Instance,new("BoolValue")
        Tag.Name = "playing"
        Tag.Parent = Plr
    end
end

Next up, is your ClickDetector function here:

You can create something that’s called a debounce which only fires once the moment it activates, and cannot fire again until you re-define it back

local Debounce = false

Debounce = true

task.wait(1)

if Debounce == true then
    print("Debounce is equal to true, so we already know this fired!")
else
    print("Debounce is equal to false, that means no one has fired it yet!")
end

Within your Script I’d assume, you can implement a simple sanity check to see if debounce == true if it’s already been run or not to guarantee a certified winner

local Debounce = false
local Part = script.Parent

local function Clicked(Plr)
    if Debounce == true then return end 
    -- Debounce == false would mean it can be fired
    -- Debounce == true would mean the function would cancel, and would only run if Debounce is back equal to "fase"

    Debounce = true -- We set this to true so that our if statement above doesn't run again

    local Winner = Instance.new("BoolValue")
    Winner.Name = "winner"
    Winner.Parent = Plr

    print("The round has ended, and we have a winner,", Plr.."!")
end

Part.ClickDetector.MouseClick:Connect(Clicked)

And if you want to check when the round is over, you could create a Bool Value within your game that’ll check if the game is over or not to reset everything back to square one!

Something like this could do nicely:

local GameEnd = workspace:WaitForChild("GameEnded") -- This is a Bool
local playing -- Whatever you define playing as

while GameEnd.Value == false and playing > 0 do -- This will run until 1 of the 2 arguments have not been met
    task.wait(1)
end

print("Game has ended!")

Of course, these are all just broad examples but hopefully you should have a better understanding on how to implement something like this :slight_smile:

And do make sure to look more into the Documentation as well if you’re ever confused on anything!

1 Like

To start with a few things to clear about your misunderstandings:

  1. The players don’t just magically belong to a box because they are in a particular area. Essentially Player Characters are children of the workspace, with a Player themselves being told which character is their Character to begin with. As such doing DetectBox:FindFirstChild(v) is not going to yield anything productive.
  2. Never do game.Players:GetChildren(), a method game.Players:GetPlayers() has been provided to allow for only the players to be grabbed accurately.
  3. A player object in game.Players and the Player Character model are not the same thing. Instead you can do in my case: game.Players.6Clu.Character in order to get my character in particular.

So what are we going to do instead?

Lets go through it step by step:

local Players = game.Players -- Lets keep a clean reference to stop repeat typing
local DetectBox = workspace.SquidGame.DetectBox -- Lets reference our box early, after all it isnt going to change
local Playing = {} -- Instead of our BoolValue, lets use a table where their general presence means they are playing

for _, Player in ipairs(Players:GetPlayers()) do -- We do it like this as it gives reference to the fact they are Players, its easier for our eyes
   local Character = Player.Character -- This is an accurate reference to the current Character Model
   local CharacterMainPart = Character.PrimaryPart -- This is the HumanoidRootPart by default

   local distance = (CharacterMainPart.Position - DetectBox.Position).Magnitude -- Get the distance
   if distance < 10 then -- We are going to do a simple radius bounding box, its a lot cleaner to code
      table.insert(Playing, Player) -- Add the player reference to the playing table, we can call of these as they are data pointers.
  end
end

So what did we just achieve?
As you can see in this clear snippet of code, we have defined players who are now playing the game if they are within 10 studs. Additionally you could do a bounding box (compare the x and z values to make sure they are in the area) but this is more complex.

Now instead of having to check if players are playing, we also have a list of people playing. You can also simply refresh this list with Playing = {}

NOTE: RayCasting is a poor method to use here, as a lot of unessential parts will be collected and you’ll be forced to create a blacklist (which takes up more memory and time then speed iteration).


For your game counters:
Frankly a winner should be defined as #Playing == 1, you can use the tables documentation to learn how to remove players yourself.


For the wait loop:
Please do:

while not #Playing == 1 do task.wait() end

References used:

Tables
Pairs and iPairs

Reading the other response, its acceptable but some of the code methodology wouldn’t conform to good time complexity logic.

I’m seeing a lot, but I’m pretty sure you could just use these: