I have an issue where if a object is very far from a player and not streamed in then sound needed to be created would not replicate to that client.
I’m replicating sound from the server to the client using FireAllClients and playing the sound from there. When a sound is replicated from the server to the client and the client tries to access the object to put the sound into, it is returned as nil.
Obviously this is because the object is not streamed in on the client.
My main question is how could I get around this issue?
i got you , you want to play a sound for all player at once , it is simple put the sound in the “SoundService” then use the :Play() method in a script to play it for all players.
Keep audios in replicated, make client pull audio from replicated and place the audio into a dummy object (like an invisible part) and place that part at the location where the audio should be played from.
I assume the reason why you’re doing it like this is because you need the audios to be played with their location (direction and distance) in mind.
When players enter a plane the server detects it and tells all the clients to play an engine sound. This work perfectly.
However my issue is when the server tells all the clients to play an engine sound, some clients have not yet streamed parts in, meaning, when the server sends the part that sound should be played in to all the clients, it is nil on clients that have not yet streamed some parts in.
There is where my issue actually is, I seem to have worded this topic badly.
What I mean is that if the part is nil on the clients (ie it hasn’t been streamed in), there wouldn’t really be a need to play it right? You could just check that the part was not passed to the client (it doesn’t exist)
Or does it absolutely have to be played? If that’s the case, you can do what clearly said, pass a CFrame, and then create a dummy part or attachment at that CFrame, then parent the sound to that instead.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Debris = game:GetService("Debris")
local CollectionService = game:GetService("CollectionService")
local Resources:Folder = ReplicatedStorage:WaitForChild("Resources",math.huge) :: Folder
local Events:Folder = ReplicatedStorage:WaitForChild("Events",math.huge) :: Folder
local SFX:Folder = Resources:WaitForChild("SFX",math.huge) :: Folder
--[[
Example Folder Hierarchy:
-ReplicatedStorage
--SFX
---EngineSounds
----Engine1
----Engine2
---CrashSounds
----Crash1
----Crash2
--Events
--soundEvent
--explosionEvent
]]
local soundEvent:UnreliableRemoteEvent = Events:WaitForChild("soundEvent",math.huge) :: UnreliableRemoteEvent -- Unreliable events would fit your use case more, although if you REALLY want to replicate these 100% of the time, go with normal remote events.
local function createNode(position:Vector3)
if not position then position = Vector3.new(0,0,0) end -- Defaults to world origin, change this to player possibly?
local node = Instance.new("Part",workspace) -- Could also make a nodes folder.
node.Size = Vector3.new(1,1,1)
node.CanCollide = false
node.Anchored = true
node.Transparency = 1
node.CFrame = CFrame.new(position)
CollectionService:AddTag(node,"SoundNode")
--[[
By adding nodes to a tag, you can get all members of a tag for later use,
In a different script, you can do this:
for i, node in pairs(CollectionService:GetTagged("SoundNode")) do
print(`This is a SoundNode at: {node.Position} `)
end
This will get all sound nodes that exist in the Game instance, and print out their position.
]]
return node
end
soundEvent.OnClientEvent:Connect(function(asset:string, position:Vector3)
if not asset or not position then
return
end
local find_asset:Sound = SFX:FindFirstChild(asset,true) :: Sound -- This finds the sound object recursively inside the SFX folder
if find_asset and find_asset:IsA("Sound") then
local node = createNode(position)
local new_sfx:Sound = find_asset:Clone() :: Sound
new_sfx.Parent = node
new_sfx:Play()
new_sfx.Stopped:Once(function() -- This function will run ONCE, and only once, the sound has stopped playing.
node:Destroy()
end)
end
end)
Yeah so if I dont play the sound then when the plane gets closer to the player and gets streamed in, then the player wont be able to hear that planes engine.
I’ve heard about VFX being done on the client, and it makes sense because you need them to move smoothly. But I don’t think doing sounds on the client is bad practice per se, especially if you’re doing it conservatively. I think it really comes down to weighting pros and cons of each method and whichever will be best for you.
Sure, it will take some load off the server to create the sounds on the client, but I wouldn’t think it would be a such significant amount for it to be worth it over simply relying on streaming to replicate the sound and properties for you, especially when you have factors like moving parts.
That being said, if you do still want to avoid doing it on the server (and because I now know it’s a constantly moving part), I’m thinking you could come up with something something using collection service and adding an attribute to these sound parts to find what ID to play and when the sound was played. Then, on the client, reconstruct the sound from these attributes, this would completely negate the need for remotes for these instances.
That being said, it’s going to take a bit of brainpower so let me know if this seems like a more feasible option and I’ll try coming up with something.
Its a great idea, here how I was originally thinking of doing it:
Server sends info to client to make sound and a table of all its properties
Client makes the sound and applies to properties
If client sees that the parent the sound was supposed to be parented to is nil then it creates a connection that detects when a part is added to workspace
If this part added to the workspace matches the parent it then it fires a remote to the server asking if the sound should still be played.
The client receives the go-ahead and plays the sound in the newly streamed in part
One of my concerns however is if there is a better way to know when a part has been streamed in other than DescendantAdded for performance reasons.
Also, this system is not just for sounds, I’m replicating explosions and other vfx aswell.
I think this is the best option, unless you could come up with a better idea
I don’t think there is a signal to know when a part is streamed in, however if you added a tag to it on the server, it should fire the relevant CollectionService:GetInstanceAddedSignal on the client
Yes, but as I said in the replies above, there is quite a lot of these objects and they will be constantly moving so I don’t want them all to be rendered in