#Better way to get players in my 'zones'?

1 messages · Page 1 of 1 (latest)

strong meadow
#

I have these 'zones', which are just parts i have placed and rotated, with collision turned off and invisible. Child parts of these 'zones' should be considered as part of their parent 'zone'.

I tried to use OnTouched/OnTouchEnded, but it was unrelaible. So now I'm trying this, is this ok to do? It happens once a second since I need to be checking these zones for players constantly. I don't expect many zones, but they might be a bit big at times?

Anyway here's the lua:

for _, zoneData in spawnData do -- zone data includes some info about the zones
        
  local playersInZone = {}
  for _, part in ipairs(zoneData.parts) do -- parts is the zone itself and 'sub-zones' which are considered the same zone
    overlapParams:AddToFilter(PlayerService:GetPlayers()) -- overlapParams created at the start of the script so it's not constantly created
    local foundPartsOfPlayers = workspace:GetPartBoundsInBox(part.CFrame, part.Size, overlapParams)
            
    for _, fpp in ipairs(foundPartsOfPlayers) do
      local character = fpp.parent
      local player = character and PlayerService:GetPlayerFromCharacter(character)
      if player and not playersInZone[player] then -- dont want duplicates
        table.insert(playersInZone, player)
      end
    end
            
  end
end
#

I've commented the lua to help you guys understand it easier, hopefully it was helpful. The zones purely consist of cube parts.

strong meadow
#

I realize that didnt work, here is one that actually works:

for _, part in ipairs(zoneData.parts) do
  local characterParts = {}
  for _, player in PlayerService:GetPlayers() do
    local bp = player.Character:FindFirstChildWhichIsA("BasePart")
    table.insert(characterParts, bp)
  end

  overlapParams:AddToFilter(characterParts)
  local foundPartsOfPlayers = workspace:GetPartBoundsInBox(part.CFrame, part.Size, overlapParams)

  for _, fpp in ipairs(foundPartsOfPlayers) do
    local character = fpp.Parent
    local player = character and PlayerService:GetPlayerFromCharacter(character)
    if player and not seenPlayers[player] then
      seenPlayers[player] = true
      table.insert(playersInZone, player)
    end
  end
end
shut python
#

use .touched and .touchended

#

they are not unreliable, you're just using them wrong

#

tbh just use ZonePlus module

strong meadow
#

.touched and .touchended are definitely unreliable. I used them and it sometimes didnt remove all the tracked parts of the player when i walked out

shut python
#

zoneplus is a battle-tested solution to doing zones with parts in roblox with .touched/touchended, it's fairly reliable

strong meadow
#

I google it and i find other people say they are unreliable too

#

how am i supposed to use it?

shut python
#

saves the trouble of re-inventing the wheel yourself

shut python
strong meadow
#

i add a function to be called on on touched and on touch ended, but on touch ended is not always called for every part.

strong meadow
#

let me see if i can undo far enough

shut python
#
strong meadow
#

doing it myself helps me learn lua more

shut python
strong meadow
#

then freaking tell me instead of just subtely insulting me???????? I read the documentation for it. Here is the code I wrote.

local PlayerService = game:GetService("Players")
local ServerAttributes = require(game:GetService("ServerScriptService"):WaitForChild("ServerAttributeNames"))

local ZoneCollider = {}
ZoneCollider.__index = ZoneCollider

local function getPlayer(hit)
    local character = hit.parent
    local player = PlayerService:GetPlayerFromCharacter(character)
    return player
end

local function OnTouch(self, hit)
    local player = getPlayer(hit)
    if player == nil then return end

    player:SetAttribute(ServerAttributes.RuntimePlayerAttributes.zone, self.zoneName)
    local attachments = player:GetAttribute(ServerAttributes.RuntimePlayerAttributes.zoneAttachments)
    player:SetAttribute(ServerAttributes.RuntimePlayerAttributes.zoneAttachments, attachments + 1)
end

local function OnTouchEnd(self, hit)
    local player = getPlayer(hit)
    if player == nil then return end
    
    print(player)
    
    local attachments = player:GetAttribute(ServerAttributes.RuntimePlayerAttributes.zoneAttachments)
    
    if attachments - 1 <= 0 then
        player:SetAttribute(ServerAttributes.RuntimePlayerAttributes.zone, "")
        player:SetAttribute(ServerAttributes.RuntimePlayerAttributes.zoneAttachments, 0)
    else
        player:SetAttribute(ServerAttributes.RuntimePlayerAttributes.zoneAttachments, attachments - 1)
    end
end

function ZoneCollider.new(zoneParts)
    local self = setmetatable({}, ZoneCollider)
    self.zoneName = zoneParts[1].name

    self.OnTouch = OnTouch
    self.OnTouchEnd = OnTouchEnd

    for _, part in ipairs(zoneParts) do        
        part.Touched:Connect(function(hit)
            self:OnTouch(hit)
        end)
        part.TouchEnded:Connect(function(hit)
            self:OnTouchEnd(hit)
        end)
    end

    return self
end

return ZoneCollider
shut python
#

set up lots of test situations, and print out lots of debug information to read exactly what is happening

#

so yea u did it rong

strong meadow
#

are you going to explain what I did wrong?

shut python
# strong meadow are you going to explain what I did wrong?

a player's character is comprised of a number of different parts, each on their own will trigger touched and touchended. Your mistake is assuming that touched is being fired for only one of the player's parts, when in reality it is firing for several. As a result, when the player stands on the boundary where some of their parts are inside the zone, and some are outside, you get undefined behavior because you aren't accounting for the multiple parts.

strong meadow
#

no, i am accounting for multiple parts

#

as long as the attachments attribute is above 0, i run other code based on the zone name attribute.

#

the issue was, attachments was not always set to 0 when walking out of the zone

#

like when i click on the character in the explorer and actually look at the attribute, it is not 0. One time it got left at 13, one time it got left at 3, one time it got left at 2. I'm pretty sure I accounted for tools being held and other stuff that could potentially dissapear without calling ontouchended by checking if the player is the direct parrent.

shut python
#

are the zones moving or anchored?

strong meadow
#

anchored. Here is a video of it, happens towards the end. I scroll down and check the attribute on the player, it's set to 13 and the zone name is still "TestZone"

#

happens at the start too but I dont check the attribute until later.

shut python
#

multiple parts or one part?

#

because boundary between 2 parts in this might have issue

strong meadow
#

what kind of issues? There is one part that is overlaping but I tested it before recording this video and it worked correctly, no changes between then and this video.

Also in the video it happens when entering/exiting one part. It says "TestZone" which is the name of that zone, but im in a childed part. However the collision code should only be called once for that part, since i never transitioned directly into another part from that one like I did at the start of the video.

shut python
#
    if attachments - 1 <= 0 then
        player:SetAttribute(ServerAttributes.RuntimePlayerAttributes.zone, "")
        player:SetAttribute(ServerAttributes.RuntimePlayerAttributes.zoneAttachments, 0)
    else``` the set instead of increment is often where misalignment like this comes from
#

methinks something to do with touchended being called twice on the same frame as a touched

#

if you change that to attachments-1 instead of setting to 0 in the <=0 case i think that will fix

#

there is no issue with this value going negative since it should self-correct

#

aka touchended is always paired after a touched so it should always balance to 0. not letting it go negative can theoretically break that

strong meadow
#

I also noticed that when I collide with an ore, touchended is called??

#

even though my touch hasnt ended with the thing im inside of?

#

maybe it was that combined with the set?

shut python
#

it can only happen if 2 touchended's are called in the same frame

#

coz of that set to 0

strong meadow
#

Well I dont see it breaking now but i also didnt see it breaking before i recorded that video, it just was pure chance it happened while I was recording. So maybe it's fixed? Idk.

#

While you're here, just one more question. Would you recommend just keeping a table somewhere (like maybe in my zoneCollider script?) or keep using attributes on the player like this.

shut python
#

i would think touchended is being called before touched, deffo add prints and check that

#

bit weird if roblox does that

shut python
strong meadow