Hi, I am trying to make a capturable base, that tweens a flag up a pole when its being captured. The conditions though are if there are two or more people in the zone than the base stops being captured, and if there is one person touching it the flag goes up, and if they leave the base the flag stops moving. The idea was to use .Touched for when the player gets on, and then Heartbeat to check who is in the zone. But this does not make much sense because then .touched will keep firing, and heartbeat will keep going off, and then the tweens are constantly played. All in all I have the idea, but I’m going about it fully wrong. All help is appreciated
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local flag = script.Parent.flag
local flagtop = script.Parent.top.Value
local flagbottom = script.Parent.bottom.Value
local fullWayTime = script.Parent.timeTotal
local totalDistance = flagtop - flagbottom
local function isPointInZone(point, zonePart)
local relPoint = zonePart.CFrame:PointToObjectSpace(point)
return math.abs(relPoint.X) < zonePart.Size.X * 0.5 and math.abs(relPoint.Z) < zonePart.Size.Z * 0.5
end
local tweenUp = TweenService:Create(flag, InfoUp, goalTop)
local tweenDown = TweenService:Create(flag, InfoDown, goalBottom)
local playersInZone = {}
local function onHeartbeat()
for i, player in pairs(game.Players:GetPlayers()) do
if player.Character and player.Character.PrimaryPart and isPointInZone(player.Character.PrimaryPart.Position, script.Parent) then
table.insert(playersInZone, player.UserId)
else
playersInZone[player.UserId] = nil
end
end
if #playersInZone > 1 then
tweenUp:Pause()
tweenDown:Pause()
elseif #playersInZone > 0 then
local pla = playersInZone[1]
if flag.Color == pla.Team.TeamColor then --if your colour then
-- flag up, otherwise down first, change colour and then up
tweenUp:play()
else
tweenDown:play()
end
else
tweenUp:Pause()
tweenDown:Pause()
end
end
local function onTouched(player)
RunService.Heartbeat:Connect(onHeartbeat)
end
script.Parent.Touched:Connect(onTouched)
With the new version I propose, it should better handle entry and exit. I’ve also fixed some of the tweens and made the table for playersInZone work properly. Some assumptions are made in the script; change the variables as needed.
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local flag = script.Parent.flag
local flagtop = script.Parent.top.Value
local flagbottom = script.Parent.bottom.Value
local fullWayTime = script.Parent.timeTotal.Value
local totalDistance = flagtop - flagbottom
local playersInZone = {}
local tweenUp
local tweenDown
local function isPointInZone(point, zonePart)
local relPoint = zonePart.CFrame:PointToObjectSpace(point)
return math.abs(relPoint.X) < zonePart.Size.X * 0.5 and math.abs(relPoint.Z) < zonePart.Size.Z * 0.5
end
local function updateTweens()
local flagPosition = flag.Position.Y
local timeUp = fullWayTime * (flagtop - flagPosition) / totalDistance
local timeDown = fullWayTime * (flagPosition - flagbottom) / totalDistance
local InfoUp = TweenInfo.new(timeUp)
local InfoDown = TweenInfo.new(timeDown)
local goalTop = {Position = Vector3.new(flag.Position.X, flagtop, flag.Position.Z)}
local goalBottom = {Position = Vector3.new(flag.Position.X, flagbottom, flag.Position.Z)}
tweenUp = TweenService:Create(flag, InfoUp, goalTop)
tweenDown = TweenService:Create(flag, InfoDown, goalBottom)
end
local function onHeartbeat()
local count = 0
for userId, _ in pairs(playersInZone) do
count = count + 1
end
updateTweens()
if count > 1 then
tweenUp:Pause()
tweenDown:Pause()
elseif count == 1 then
local userId = next(playersInZone)
local player = game.Players:GetPlayerByUserId(userId)
if flag.Color == player.Team.TeamColor then
tweenUp:Play()
else
tweenDown:Play()
end
else
tweenUp:Pause()
tweenDown:Pause()
end
end
local function onTouched(otherPart)
local character = otherPart.Parent
local player = game.Players:GetPlayerFromCharacter(character)
if player and isPointInZone(otherPart.Position, script.Parent) then
playersInZone[player.UserId] = true
end
end
local function onTouchEnded(otherPart)
local character = otherPart.Parent
local player = game.Players:GetPlayerFromCharacter(character)
if player then
playersInZone[player.UserId] = nil
end
end
script.Parent.Touched:Connect(onTouched)
script.Parent.TouchEnded:Connect(onTouchEnded)
RunService.Heartbeat:Connect(onHeartbeat)
Let me know if you need any clarification about the above code!
Thank you! That’s very helpful. I just wonder, is there a point in using tween if the tween is recreated every frame from heartbeat? Like would that hamper performance?
That’s a good point. I am not sure how better performant using a simple CFrame update per frame would be compared to tweens, but if I were to take a guess, I would probably say a simple CFrame change would be better.
Also, I noticed that this code would pause the movement of the flag if two people of the same team were present, essentially punishing having more of your own team in the area. I changed the code where having # people or more relative to the other team moves the flag at (#ofmoreplayers)^0.75 scale. ^0.75 is a root function that suppresses larger numbers at a lesser scale than the square root.
In the code below, I use displacement from the original spot (displacement = 0 at start) to determine where the flag is. Change the top displacement of the flag (currently at displacement = 4) to the amount you want the flag to move up from the bottom (in studs). Also, consider changing the direction values for up and down if you want going up to be faster/slower than going down, and also to determine how many seconds you want the capture to happen in. The scale moves the flag # studs per second. With the current code, a capture from starting point without interference from the other defending team would take 8 seconds (4/0.5).
I also use deltaTime as a parameter taken by the onHeartbeat function, to update the flag movement scale at the speed of the framerate. Thus, the flag will move at bigger increments if the framerate is lower than 60 FPS, which is intended to keep a constant speed since we are firing the onHeartbeat function every frame.
Here is the code below, let me know if you need any edits or changes to the way the speed of the flag is implemented (acceleration or deceleration at certain points?):
local RunService = game:GetService("RunService")
local flag = script.Parent.flag
local displacement = 0
local playersInZone = {}
local teamA = --certain directory to teams
local function isPointInZone(point, zonePart)
local relPoint = zonePart.CFrame:PointToObjectSpace(point)
return math.abs(relPoint.X) < zonePart.Size.X * 0.5 and math.abs(relPoint.Z) < zonePart.Size.Z * 0.5
end
local function onHeartbeat(deltaTime)
local countA = 0
local countB = 0
for userId, teamColor in pairs(playersInZone) do
if teamColor == teamA.TeamColor then
countA = countA + 1
else
countB = countB + 1
end
end
local scale = 0
local direction = 0
if countA > countB then
scale = (countA - countB) ^ 0.75
direction = 1
elseif countB > countA then
scale = (countB - countA) ^ 0.75
direction = -1
end
if scale > 0 then
local movementY = direction * scale * deltaTime
displacement = displacement + movementY
if displacement < 0 then
movementY = movementY - displacement
displacement = 0
elseif displacement > 4 then
movementY = movementY - (displacement - 4)
displacement = 4
print("Flag captured!")
end
flag.CFrame = flag.CFrame * CFrame.new(0, movementY, 0)
end
end
local function onTouched(otherPart)
local character = otherPart.Parent
local player = game.Players:GetPlayerFromCharacter(character)
if player and isPointInZone(otherPart.Position, script.Parent) then
playersInZone[player.UserId] = player.Team.TeamColor
end
end
local function onTouchEnded(otherPart)
local character = otherPart.Parent
local player = game.Players:GetPlayerFromCharacter(character)
if player then
playersInZone[player.UserId] = nil
end
end
script.Parent.Touched:Connect(onTouched)
script.Parent.TouchEnded:Connect(onTouchEnded)
RunService.Heartbeat:Connect(onHeartbeat)
Thanks! I like the delta time idea for heartbeat. I was just wondering how DeltaTime is expressed, like is it like a range of 0.9 or 1.1? I’m also curious, if there’s two players, one with a low frame rate, one with a high frame rate, then is the correct speed just replicated to each client from the server script, so it will go up at the same speed or are both FPS taken as input and then averaged? Or something else entirely?
deltaTime is a fraction (aka decimal). It is the amount of seconds it takes to render a frame. In an ideal condition, the game runs at 60 FPS, which means it takes 1/60 or ~0.0167 seconds per frame. This number is what deltaTime is. For eample, a game slowed to 45 FPS will have a value of 1/45 or ~0.0222 for deltaTime.
deltaTime is the server FPS, since Roblo’s model attempts to synchronize the FPS across all clients. Thus, to the best of ability, the client system will try its best to emulate the server FPS as the client FPS.