-
What do you want to achieve? Getting my NPC.Character.Humanoid SeatPart Property to their seat AND Seat.Occupant to the NPC. Because i want the NPC to sit down and stay at this seat.
-
What is the issue? The problem is that SeatPart and Occupant are read-only. I can’t figure how to achieve that, i have tried several solution.
-
What solutions have you tried so far? I tried using Seat:Sit
, but it just set the sit property of the Humanoid to True
, and the Occupant property of the currentSeat
is still nil
.
I also debbugged the code to see if currentSeat exist, it exist, and after that the code pass at currentSeat:Sit(humanoid)
nothing happen, only the humanoid Sit property is set to True
. No error is being raised. And somehow, some NPC have still have they Sit
property to False
. I also tried Humanoid.Sit = true, but it just force the property to be true and not truly sitting where i want. It says that “if the Humanoid isn’t attached to a seat while in its sitting state, it will trip over with no collision in its legs”, but i can’t find out how to attach the Humanoid to a sit. That’s why i tried Seat:Sit(Humanoid: Instance) but it just won’t work. So in summary i’m here to get how could i attach a sit to an humanoid.
I also wanna inform you that the script is running on client. And also i have some Beta Feature in Roblox Studio Enabled
Here is the code that i’m using with Seat:Sit (not working as explained above), if you need more for help me, ask me.
elseif boardingState.Value == "MovingToSeat" then
-- Check if we have a current position to compare
if lastPosition then
local currentPosition = passenger:GetPivot().Position
local movement = (currentPosition - lastPosition).Magnitude
-- If passenger hasn't moved
if movement < 0.1 then
stuckTime = stuckTime + 0.1
if stuckTime > 0.5 and currentSeat then
-- Use the Seat's Sit function directly
currentSeat:Sit(humanoid)
boardingState.Value = "Seated"
return false
end
else
stuckTime = 0
end
lastPosition = currentPosition
end
The issue of NPCs not sitting properly on seats in Roblox often stems from physics timing and synchronization limitations, especially when handling this client-side. When calling Seat:Sit(humanoid)
on the client, Roblox sets the NPC’s Humanoid.Sit
to true, but the attachment between the Seat
and the Humanoid
may fail to register properly… This occurs because seat assignments rely on server-side physics updates, which may not synchronize instantly with the client.
So in sumarry you mean that my script is unoptimized or that i need to use this on server-side ?
1 Like
Well, NPC handling on the client side is only useful if only 1 or a selected group of players needs to see the NPC. In a case with a global presence, it’s better to do a server-side call, in my opinion. Or you could use asynchronous to ensure the thing takes its time and adapts to its own timing.
EDIT: grammar…
The problem is that i’m doing AI NPC for a train game. But the train movement logic is on all client, because on server-side it’s lagging the whole server. So i want my NPC to move and seat on all client, rather than server and defaulty replicate on client. I don’t know anything about asynchronous thing, can we do it on client and if yes how can i implement it?
here is my whole function
-- Function to make passenger board the train
local function boardTrain(passenger, train, door)
local humanoid = passenger:FindFirstChild("Humanoid")
if not humanoid then return end
-- Set up collision immediately
setPassengerCollision(passenger)
-- Create a new state value if it doesn't exist
local boardingState = passenger:FindFirstChild("BoardingState") or Instance.new("StringValue")
boardingState.Name = "BoardingState"
boardingState.Value = "MovingToDoor"
boardingState.Parent = passenger
-- Variables to track movement
local lastPosition = nil
local stuckTime = 0
local currentSeat = nil
-- Movement update function
local function updateMovement()
if not passenger.Parent or not humanoid then
return false
end
-- Check distance from player for optimization
local playerCharacter = LocalPlayer.Character
if playerCharacter and playerCharacter:FindFirstChild("HumanoidRootPart") then
local distanceFromPlayer = (passenger:GetPivot().Position - playerCharacter.HumanoidRootPart.Position).Magnitude
if distanceFromPlayer > PASSENGER_LOAD_DISTANCE then
return true -- Keep the connection but don't process movement
end
end
if boardingState.Value == "MovingToDoor" then
-- Move to door center
humanoid:MoveTo(door.centerPoint)
-- Check if at door
local distance = (passenger:GetPivot().Position - door.centerPoint).Magnitude
if distance < DISTANCE_TO_DOOR then
boardingState.Value = "FindingSeat"
end
elseif boardingState.Value == "FindingSeat" then
local seat = findNearestSeat(passenger, train)
if seat then
currentSeat = seat
boardingState.Value = "MovingToSeat"
humanoid:MoveTo(seat.Position)
lastPosition = passenger:GetPivot().Position
end
elseif boardingState.Value == "MovingToSeat" then
-- Check if we have a current position to compare
if lastPosition then
local currentPosition = passenger:GetPivot().Position
local movement = (currentPosition - lastPosition).Magnitude
-- If passenger hasn't moved
if movement < 0.1 then
stuckTime = stuckTime + 0.1
if stuckTime > 0.5 and currentSeat then
-- Use the Seat's Sit function directly
currentSeat:Sit(humanoid)
boardingState.Value = "Seated"
return false
end
else
stuckTime = 0
end
lastPosition = currentPosition
end
elseif boardingState.Value == "Seated" then
return false
end
return true
end
-- Connect to MoveToFinished event for precise movement tracking
--[[humanoid.MoveToFinished:Connect(function(reached)
if boardingState.Value == "MovingToSeat" and reached and currentSeat then
humanoid.Sit = true
boardingState.Value = "Seated"
end
end)]]
-- Set up the movement loop
local connection
connection = RunService.Heartbeat:Connect(function()
if not updateMovement() then
connection:Disconnect()
end
end)
end
-- Main function to check for and manage boarding
local function startBoardingSystem()
-- Wait for character to load
if not LocalPlayer.Character then
LocalPlayer.CharacterAdded:Wait()
end
-- Main loop
while true do
local passengerFolder = workspace:FindFirstChild("Passengers")
if passengerFolder then
-- Set collision for new passengers immediately
for _, stationFolder in ipairs(passengerFolder:GetChildren()) do
for _, passenger in ipairs(stationFolder:GetChildren()) do
if not passenger:FindFirstChild("CollisionSet") then
setPassengerCollision(passenger)
-- Mark as processed
local marker = Instance.new("BoolValue")
marker.Name = "CollisionSet"
marker.Value = true
marker.Parent = passenger
end
end
end
local playerCharacter = LocalPlayer.Character
if playerCharacter and playerCharacter:FindFirstChild("HumanoidRootPart") then
local playerPosition = playerCharacter.HumanoidRootPart.Position
-- Check each passenger folder
for _, stationFolder in ipairs(passengerFolder:GetChildren()) do
-- Get average position of passengers in this folder
local folderPosition = getFolderAveragePosition(stationFolder)
if folderPosition then
local distanceToStation = (playerPosition - folderPosition).Magnitude
if distanceToStation <= PASSENGER_LOAD_DISTANCE then
-- Extract station info from folder name
local currentStation, headingStation = stationFolder.Name:match("(.+)To(.+)Passengers")
if currentStation and headingStation then
-- Get SIEL models for this route
local sielModels = getSIELModel(currentStation, headingStation)
-- Check each SIEL model
for _, sielModel in ipairs(sielModels) do
local trainValue = sielModel:FindFirstChild("TrainAtStation")
if trainValue and trainValue.Value then
local train = trainValue.Value
-- Check if train is ready for boarding
local boardingReady = train:FindFirstChild("BoardingReady")
if boardingReady and boardingReady.Value then
-- Find valid doors
local validDoors = findValidDoors(train)
if #validDoors > 0 then
-- Start boarding process for each passenger
for _, passenger in ipairs(stationFolder:GetChildren()) do
-- Skip passengers who are already boarding
if not passenger:FindFirstChild("BoardingState") then
-- Find nearest door
local nearestDoor = validDoors[1]
local shortestDistance = math.huge
for _, doorInfo in ipairs(validDoors) do
local distance = (passenger:GetPivot().Position - doorInfo.centerPoint).Magnitude
if distance < shortestDistance then
shortestDistance = distance
nearestDoor = doorInfo
end
end
-- Start boarding process
local humanoid = passenger:FindFirstChild("Humanoid")
if humanoid then
humanoid.WalkSpeed = WALKING_SPEED
boardTrain(passenger, train, nearestDoor)
end
end
end
end
end
end
end
end
end
end
end
end
end
task.wait(BOARDING_UPDATE_RATE)
end
end
The startBoardingSystem() function is called when the player character spawn.
I also read the Multithreading thing, if i place an actor as the parent of the client script it will be okay or do i need to do something else?
I’ll try to look at it today super character limit
1 Like
I a bit modified my script to use async or idk, and i also added an actor, and i can confirm that it’s a “performance issue”.
-- Async function to handle seating process
local function attemptToSeat(humanoid, seat)
local success = false
local attempts = 0
local maxAttempts = 5
-- Return a promise that resolves when seating is successful or max attempts reached
return {
await = function()
while not success do
attempts = attempts + 1
-- Attempt to sit
seat:Sit(humanoid)
-- Wait for physics to update
task.wait(0.2)
print("ya")
-- Check if actually seated
if humanoid.Sit and humanoid.SeatPart == seat then
success = true
break
end
-- If not successful, wait a bit before next attempt
task.wait(0.3)
end
return success
end
}
end
-- Function to make passenger board the train
local function boardTrain(passenger, train, door)
local humanoid = passenger:FindFirstChild("Humanoid")
if not humanoid then return end
-- Set up collision immediately
setPassengerCollision(passenger)
-- Create a new state value if it doesn't exist
local boardingState = passenger:FindFirstChild("BoardingState") or Instance.new("StringValue")
boardingState.Name = "BoardingState"
boardingState.Value = "MovingToDoor"
boardingState.Parent = passenger
-- Variables to track movement
local lastPosition = nil
local stuckTime = 0
local currentSeat = nil
-- Movement update function
local function updateMovement()
if not passenger.Parent or not humanoid then
return false
end
-- Check distance from player for optimization
local playerCharacter = LocalPlayer.Character
if playerCharacter and playerCharacter:FindFirstChild("HumanoidRootPart") then
local distanceFromPlayer = (passenger:GetPivot().Position - playerCharacter.HumanoidRootPart.Position).Magnitude
if distanceFromPlayer > PASSENGER_LOAD_DISTANCE then
return true -- Keep the connection but don't process movement
end
end
if boardingState.Value == "MovingToDoor" then
-- Move to door center
humanoid:MoveTo(door.centerPoint)
-- Check if at door
local distance = (passenger:GetPivot().Position - door.centerPoint).Magnitude
if distance < DISTANCE_TO_DOOR then
boardingState.Value = "FindingSeat"
end
elseif boardingState.Value == "FindingSeat" then
local seat = findNearestSeat(passenger, train)
if seat then
currentSeat = seat
boardingState.Value = "MovingToSeat"
humanoid:MoveTo(seat.Position)
lastPosition = passenger:GetPivot().Position
end
elseif boardingState.Value == "MovingToSeat" then
-- Check if we have a current position to compare
if lastPosition then
local currentPosition = passenger:GetPivot().Position
local movement = (currentPosition - lastPosition).Magnitude
-- If passenger hasn't moved
if movement < 0.1 then
stuckTime = stuckTime + 0.1
if stuckTime > 0.5 and currentSeat then
-- Start async seating process
boardingState.Value = "AttemptingToSeat"
task.spawn(function()
local seatingPromise = attemptToSeat(humanoid, currentSeat)
local success = seatingPromise:await()
print("bruh")
if success then
boardingState.Value = "Seated"
else
-- If seating failed, try finding another seat
boardingState.Value = "FindingSeat"
end
end)
return false
end
else
stuckTime = 0
end
lastPosition = currentPosition
end
elseif boardingState.Value == "Seated" then
return false
end
return true
end
-- Connect to MoveToFinished event for precise movement tracking
--[[humanoid.MoveToFinished:Connect(function(reached)
if boardingState.Value == "MovingToSeat" and reached and currentSeat then
humanoid.Sit = true
boardingState.Value = "Seated"
end
end)]]
-- Set up the movement loop
local connection
connection = RunService.Heartbeat:Connect(function()
if not updateMovement() then
connection:Disconnect()
end
end)
end
The problem is that we need to wait that it print around 2000 “ya”, and with our task.wait it will be long. If i remove the task.wait(), roblox freeze for 30sec or more i think, and the passenger seat correctly on the seat (succes = true). And the print are looking like this when the freezing end:
And sometimes, it’s my roblox that crash…
SO i don’t know what to do, BUT i want to make it worrk. i need help to make it work WITHOUT performance issue. Please help
Here is also the script performance at a moment when it freezing when there aren’t task.wait():
I know that i’s hitting 100%, but if i don’t want to use task.wait, is there a way to reduce the activity??
1 Like