Spawning sounds at random locations is a useful technique (especially when combined with random variations within the sounds themselves) as it can help reduce any sense of repetition and help to bring an environment to life. This is the first of a short series of posts that deal with this topic and will cover a few of the techniques involved, starting with the ability to randomly choose locations
As with any technique, there are multiple ways of achieving the same thing… You could do this:
But then you’d have to set up a system to define your x/y/z min/max values to appropriate ranges so that the sounds played back within the correct area of your level.
A better way might be this, as you can define specific locations by placing actors at the various points within your area:
Or this, using AmbientSound actors placed within the level:
Either approach produces the same end result, but it depends whether you want to have your sounds appear to come from specific actors within your level or from ‘unseen’ empty AmbientSound actors that you’ve placed appropriately.
These methods work fine, and there are probably more variations on this theme, but the problem comes if you want to add more possible locations within your level. You’d have to modify your BP to accommodate these changes by adding in more actor references, and extending the Select node.
Instead we can make our controlling Blueprint dynamically find all of our locations and store them in an array. We can then use a similar method, as illustrated above, to generate random numbers and select from the contents of the array – if we’re cunning about how we do this, we only need to build one system and we can add/remove locations as much we want without having to make any changes.
Firstly we need to ‘find’ all of our locations.
The Get All Actors Of Class does exactly what you would expect – it finds all of the actors within a level of a specified class and generates an array containing all found references. You can use whatever type of actor you want for your sound locations (as discussed above), but for this method it does make a bit of sense to create your own custom actor class so that you can be sure that the Get All node only finds those actors you specifically want to use as location markers.
Creating your own actor class is pretty simple – within your Content Browser, use Add New – Blueprint Class.
Then you can choose to base your custom actor off any class you like – I’ve chosen to use AmbientSound as this means I could use it’s available functionality to change how the sound is played in the future if I want.
Once you’ve got your Get All node configured, you can easily create a variable to store your generated array, by right clicking the ‘Out Actors’ outlet and selecting ‘Promote to Variable’. It would be useful to know how many actors are stored within the array – so that we can generate an appropriate random number, so we use the Length node and create an int variable to store the output.
Because the BP finds all of the specific actors within our level, and creates the array for us, we can add and remove actors and not have to worry about modifying the BP – which is nice!
The next step is to create the system that will select one of the locations at random and then play the sound from that location.
We subtract ‘1’ from the length of the array, and use this as the maximum of our random integer range, as the references within the array are stored at indices that start at ‘0’. So the first slot is ‘0’, the second is ‘1’, and so on…
We then use the Get node to access the reference stored at our random index and store that as a variable – we do this to ensure that any future use of this randomly generated reference remains consistent (if we just used the output of the Get node, then the Set Sound node would produce one random reference, and the Play node would produce another – they will likely be different!).
We then simply set the sound to be played by the actor and start it playing.
If at this point, you’ve been building and are testing your system, you may well notice that sometimes a currently playing sound location is retriggered – this is the beauty of ‘random’; the same thing can happen twice in a row (or more…). Ideally we would check to see if the randomly chosen location is playing, and if so generate another random number and try again.
This is pretty simple:
We can use the IsPlaying variable from the Audio Component to control a Branch node (this is one of the reasons I chose to base my custom actor off the AmbientSound class). If the randomly chosen location is not currently playing (ie. False) then we set and play the sound. If the chosen location is playing (ie. True) we just call the PlaySound function again and restart the system.
That’s the basics of playing back sounds at random locations. There will likely be other ways, this is just the way I’ve found works for me.
There are a number of improvements we could make to the system, the biggest one being the ability to generate random numbers without repetition (similarly to how the Random node within a SoundCue works), so the next blog post will deal with that.