I am currently working on enabling StreamingEnabled in this game, to hopefully fix this issue(s).
I’ve experimented with StreamingEnabled on this place before, but I had some issues with it that made me postpone the update. However, I want to push this update out ASAP.
Once thing StreamingEnabled messed with was the way vehicles spawn in the game. The vehicle spawning system works by having an NPC near the garage/airfield, that waves at you when you are near, and you interact with its ProximityPrompt to open up the vehicle spawning GUI, as seen below:
All of this runs on a LocalScript, as seen here:
local animation = script:WaitForChild("Wave",2)
local rs = game:GetService("RunService")
local chars = {}
local player = game.Players.LocalPlayer
local function checkForNPC(child)
if child.Name == "vehicleSpawnNPC" then
local data = {plrNear = false,dummy = child}
chars[child] = data
local head = child:WaitForChild("Head",10)
local prox = head:WaitForChild("ProximityPrompt",3)
if prox ~= nil then
local playIt = child.Humanoid:LoadAnimation(animation)
prox.PromptShown:Connect(function()
chars[child].plrNear = true
playIt:Play()
playIt.Looped = true
prox.PromptHidden:Wait()
chars[child].plrNear = false
playIt:Stop()
end)
prox.Triggered:Connect(function(player)
print("success on the NPC")
if player.PlayerGui.vehicleSelector.Enabled == false then
player.PlayerGui.vehicleSelector.SpawningNPC.Value = child;
player.PlayerGui.vehicleSelector.Enabled = true
end
end)
end
end
end
for _,child in pairs(workspace.tycoons:GetDescendants()) do
print(tostring(child).." added")
checkForNPC(child)
end
workspace.tycoons.DescendantAdded:Connect(function(child)
print(tostring(child).." removed")
checkForNPC(child)
end)
workspace.tycoons.DescendantRemoving:Connect(function(child)
if child.Name == "vehicleSpawnNPC" then
chars[child] = nil
end
end)
rs.Stepped:Connect(function(Time, deltaTime)
for _,data in next,chars do
if data.plrNear == true then
local char = player.Character or player.CharacterAdded:Wait()
local npcPosition = data.dummy.HumanoidRootPart.Position
local playerPosition = char.HumanoidRootPart.Position
local _,Y = CFrame.lookAt(npcPosition,playerPosition):ToOrientation();
data.dummy.HumanoidRootPart.CFrame = CFrame.Angles(0,Y,0)+npcPosition;
end
end
end)
There is an issue, however, where this script will not run correctly when StreamingEnabled is on. The NPC won’t wave at the player, and the player will be unable to open the ProximityPrompt to spawn a vehicle.
The line number doesn’t match up with any of the :WaitForChild() calls in the original post, but i assume it is one of these:
Interestingly, it almost seems like that error shouldn’t even be possible, given that to reach that point in the function in the first place, it already has to check if child.Name == "vehicleSpawnNPC", meaning that the NPC should theoretically have already been streamed in.
By chance, does the game happen to teleport players away from the map upon joining (e.g. for the purpose of a menu screen)?
Consider setting the ModelStreamingMode property for the NPC to Atomic, as that would guarantee that once you’re able to access the Model, all of its descendants have already been streamed in.
Yep. Essentially, when they join the game, they spawn on a “neutral” team that has its spawns very far from the map, but they have their camera views set to rotate around a village until they pick a team.
I had initially thought that something like that could be causing the situation where the NPC is immediately streamed in to the client but then because they are teleported far way soon after, it streams out the NPC, leading to the outlined issues.
When StreamingEnabled is turned on, the default behavior is that each Model Instance on its own is replicated on player join, but its contents are not streamed in until it’s met the particular requirements to do so.
As a result, the loop that looks through the descendants of the tycoons as well as the function that’s listening for new descendants to be added to the tycoons will be able to reference the vehicleSpawnNPC model before it’s actually within range to have all of its descendants be fully streamed in to the client. This means that if a player does not get within range of each vehicleSpawnNPC within 10 seconds from the moment that the checkForNPC finds the model, the first :WaitForChild() will timeout, causing the code to proceed and error.
This leads me to believe that the suggestion in my previous post of setting the ModelStreamingMode of each vehicleSpawnNPC to Atomic will be the solution to this, as that would ensure that the checkForNPC function will not be called for the NPC until both it and its descendants have been replicated to the client.
Yeah, the game was made not using StreamingEnabled originally, so going back and trying to fix things retrospectively for StreamingEnabled has been a MAJOR pain in the ass.
I was going to test your suggested fix, and another bug occurred:
As you can see, once I picked a team, the spinning effect of the camera stopped, my character died/was force respawned and… it gets stuck.
This is not a consistent bug, sometimes it works fine, as seen below:
Hmmm, that’s really strange. Are the camera effects / respawning somehow tied together / linked to the LocalScript from the original post?
Oh and did that bug happen before making the change to the NPCs, or after? And are there any warnings / errors that appear in the Developer Console or Output when that occurs?
Without having a better idea of how the spawning functionality generally works, I don’t immediately know what could be causing that to happen. *However, one thing to try could be to force “reset” the player’s camera back to a view of their Character model upon respawning, since maybe it remained in the Scriptable CameraType from the menu cutscene.
Of course you don’t need to (and probably shouldn’t) share large sections of code from such a fundamental part of the game just in case someone finds a way to take advantage of that, so if you would be able to at least offer a brief description of how the team selection and / or respawning process is structured, that would make it easier to think of possible solutions.
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local cameraEffect = workspace:WaitForChild("cameraEffect",100)
local target = cameraEffect:WaitForChild("Part",10)
local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable
local rotationAngle = Instance.new("NumberValue")
local tweenComplete = false
local cameraOffset = Vector3.new(0, 10, 12)
local rotationTime = 40 -- Time in seconds
local rotationDegrees = 360
local rotationRepeatCount = -1 -- Use -1 for infinite repeats
local lookAtTarget = true -- Whether the camera tilts to point directly at the target
local function updateCamera()
if not target then return end
camera.Focus = target.CFrame
local rotatedCFrame = CFrame.Angles(0, math.rad(rotationAngle.Value), 0)
rotatedCFrame = CFrame.new(target.Position) * rotatedCFrame
camera.CFrame = rotatedCFrame:ToWorldSpace(CFrame.new(cameraOffset))
if lookAtTarget == true then
camera.CFrame = CFrame.new(camera.CFrame.Position, target.Position)
end
end
-- Set up and start rotation tween
local tweenInfo = TweenInfo.new(rotationTime, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, rotationRepeatCount)
local tween = TweenService:Create(rotationAngle, tweenInfo, {Value=rotationDegrees})
tween.Completed:Connect(function()
tweenComplete = true
end)
tween:Play()
-- Update camera position while tween runs
RunService.RenderStepped:Connect(function()
if tweenComplete == false then
updateCamera()
end
end)
the gui with this script gets deleted at one point (hence why it stops spinning) and gets stuck.
This glitch happened before the change to the NPCs, and there was no available output to help diagnose this.
Updating the Camera.CameraSubject to the Character’s new Humanoid (after respawning)
If so, when is it instructed to run that code?
Using the code you provided, I tested the following situation:
Start a playtest
Delete the ScreenGui that contains the LocalScript
Reset my Character
Observe that the camera is stuck in the place that it was when the LocalScript was deleted
However, I then created another LocalScript that would update the CameraType to Custom and the CameraSubject to my Character’s Humanoid whenever something touched a part in the workspace. Then, I tested the following situation:
Start a playtest
Delete the ScreenGui that contains the LocalScript
Reset my Character
Observe that the camera is stuck in the place that it was when the LocalScript was deleted
Touch the part in the workspace
Observe that the view of the camera returns back to the player’s Character, even upon respawning again afterwards, without needing to touch the part.
Something I noticed (which might not contribute to those inconsistencies, but I figured I’d mention it anyway, just in case) is the following:
As far as I’m aware, when the repeat count for a Tween is set to -1, it will never fire the Tween.Completed event, so tweenComplete would permanently be false.
Additionally, based on some quick tests, it appears that infinitely looping Tweens are not automatically cancelled when the script that initially played it is destroyed. The RenderStepped function would be stopped from the deletion of the LocalScript, but for some reason, the Tween isn’t.
Although the behavior showcased in the video you posted doesn’t seem to be related to the Tween (since the camera is stationary rather than rotating around an object), that might be something to look into.
Correct, the tween goes on indefinitely because I want the camera spinning to occur as long as the player is in the lobby area.
I assume that, I should have a case when the parent script/GUI gets deleted, the tween is cancelled then?
It’s likely that a simple script that re-adjusts the camera to be the player’s character on respawn every time should suffice. I was under the impression ROBLOX does this anyway, since most of the time, when you respawn from the lobby, your camera is corrected.
Upon doing that just once (after the LocalScript that had set the CameraType to Scriptable for the TweenService cutscene was deleted), the code didn’t need to be called again for subsequent respawns of the Character to readjust the camera back to the Character model.
However, it would be wise to call it once upon *every respawn to avoid any edge cases where the camera gets stuck in place beyond the situation of choosing a team to join.
It does do this by default, unless the CameraType is set to Scriptable, as that one is specifically meant to be used by developers. I assume that the game internally check if the CameraType is set to Scriptable before respawning the Character, and if it is, it doesn’t change anything as to avoid interfering with the changes that the developer has made.
If the error persists and you end up in that static camera view again, try printing the value of CurrentCamera.CameraType in the Developer Console to see if it was still set to Scriptable. If so, then there was probably an issue at some point in the process where it wasn’t set back to Custom after respawning from the lobby.