Random Without Replacement in a BP

In the previous post we looked at how we can play back sounds from random locations as a way of improving the general ambience within an area.
This post will look at a way we can improve things further by allowing us to do ‘random without replacement’ so that the random locations chosen for playback aren’t repeated. This functionality already exists within SoundCues using the Random node.

Unfortunately, there is no standard node for doing this in a BP – so we’re going to have to build it ourselves…

The resulting system is a little large, in terms of BP nodes, so I’m going to create a new Macro and build within there – this will help to keep my BPs tidy and easily ‘readable’, it will also mean the new functionality can be reused across projects easily.
We will cover the creation of the random without replacement macro, but you can download the macro here if you want.

You can create a new macro directly from within a BP using the Add Macro option.

However, this macro would then only be accessible within that BP. If you want to create a macro that you can use across your project, and even share with other projects then you need to create a Macro Library within the Content Browser. Use the Add New – Blueprints – Blueprint Macro Library option.

You’ll then be asked to choose the ‘Parent Class’ for your macro – the advice from Epic is to use ‘Actor’ as this will enable you to do more in terms of accessing variables and functions.
Give your new macro library asset a name, and then open it for editing.

The first step is define the inputs and outputs of your macro. Select the Inputs node, and in the Details window create the following Inputs and Outputs:

The Exec In triggers the macro’s functionality.
The Input Array is the set of items that we want to randomly choose from. We’re using the Wildcard variable type so that the macro isn’t tied to a specific type of asset – the wildcard type changes to match whatever is connected to it.
The Rand Without Replace boolean will allow us to choose whether we want to turn the ‘without replacement’ functionality on or off.
The Exec Out is triggered after the macro has completed it’s task.
The Array Item output passes the randomly selected item.

The first step is generate a random number:

We get the Length of the input array, subtract ‘1’ from it (like we did in the previous blog post) and then use this to set the maximum of our random range – remember, array items are indexed starting with a value ‘0’, so the possible range needs to be 1 less than the length.
We’re using a Local Integer to store this random value, so that we can use it further down the chain.
Next we need to check the status of our ‘Rand Without Replace’ boolean:

And then set up what will happen for each state.
If the bool is set to ‘False’, it’s pretty simple – just use the randomly generated value to Get the appropriate item from the Input Array and pass it to the Output:

The system for ‘True’ is a bit more complex, so we’ll build it in stages…
First, create this:

We’ve create a Local Array of Integers to store the randomly generated value, and we check this array to see if it Contains the current random value already, if it does we simply generate another random value and check again (the Reroute node is useful when you want to loop back on yourself, or to help keep connections readable). If the random value is not present within the local array then we Add it for the next pass through.
Now extend to this:

We need to know when we’ve generated every possible value so that we can reset our system and go back through the whole Input Array again. So we compare the Length of the local array with that of the Input Array and if they are equal to each other (==) then we Clear the local array to empty it and start again. If the two lengths are not equal then there are still some values left and we can go straight to the output. At the output (like we did earlier) we simply Get the item from the Input Array and pass it out.

Now you can Save your macro, and access it from any BP within your project.

Here I’ve incorporated the macro into the random sound location system from the previous blog post.

By passing it out array of sound playback locations we can randomly select one every time we call the function but be sure that we won’t get any repetition of locations until the array has been exhausted.

Next time, we’ll look at a way of extending our random system further by incorporating the option to determine random weightings for each item in the input array…

Advertisement

So, you want to spawn a sound at random locations..?

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.

OSC Utility

So recently I’ve been working with some colleagues to produce some utilities for UE4 to create an electro-acoustic composition – there’ll more details about these in the near future… But suffice to say they involved spatialization of sounds, movement following pre-defined patterns and some serialism stuff.

As well as the various methods of playing back soundfiles, we also needed real time control / triggering of sounds using OSC data so that the project could be controlled by two performers using iPads. We used the excellent OSC Plugin created by Monsieurgustav and available here, and while it does work well and is easy to setup it didn’t quite offer the functionality that we needed – Namely the ability to save the OSC addresses used as control data, recall these between sessions and set up easily managed routing of the different incoming OSC data within the project.

So, I built something that did…

I produced a BP actor that can be dropped into the level and will sit there receiving any incoming OSC data and parsing it according to some user defined settings so that individual addresses can be used to control functions within the project.

The saving and recalling of OSC addresses was relatively simple and made use of the SaveGame class to create a data file that the OSC addresses could be written to (this is saved within the ‘Saved’ folder of your project). This is handled using the [Event BeginPlay] to check for an existing data file (one is created if not already in existence) and any saved data is recalled and stored in a temporary variable.

sshot-1

I added an OscReceiver component (part of the OSC plugin) to the BP actor so that it could receive OSC data.

sshot-3

If the actor is set to ‘Learn’ mode then the address of any incoming OSC data is stored in an array and saved – the addresses are checked against the contents of the array to ensure that there no repetitions.

sshot-4

If the actor is not set to ‘Learn’ mode then incoming OSC data causes an event dispatcher to be called so that the OSC data can be received in any BP within the project (eg. Level BP).

You can easily control systems within the project using individual OSC adresses through the use of a [Switch on Int] node – you can use the index of the current OSC address within the wider array of stored OSC addresses to control the [Switch on Int] node.

sshot-5

I’ve made the OSC Utility available here along with a copy of the OSC plugin, a demo project which illustrates how the actor functions and a .pdf detailing the systems in use.

3rd Person Camera – Audio Listener Position

When you create a 3rd person game in UE4, the Audio Listener is set by default to the position of the camera, which works fine for some scenarios but not for others. It can cause problems depending on the position of the camera relative to the player character (eg. hearing things that the character cannot actually see).
I was asked about this by one of our Masters students who needed to be able to set the position of the Audio Listener to somewhere in-between the camera and the player character in order to solve just this kind of problem.

This is my solution – it does not need to be in a separate BP, but by doing so it makes it easy to distribute and means it can simply by dropped into a project.

Within the BP, the Audio Listener is set to the position of the actor, which in turn has it’s position constantly updated so that it is always relative to both the player character and the player camera.

sshot-1

On every frame update the location of the player camera and character are obtained as well as the forward vector of the camera and it’s rotation. These positions are used to calculate the position of the actor.

The forward vector of the camera tells us the direction the camera is facing.
The distance between the player camera and the character is obtained and is scaled by a user-definable variable to determine how far along a line between the camera and character the actor should be placed.
This value is then used to scale the forward vector of the camera to move the actor along the axis of the camera towards the player character.

Finally I added in a [VInterp To] node to apply some level of interpolation on the actor’s location so that there is some degree of ‘inertia’ on the listener position’s movements – this smooths out any jumpy movements introduced by the camera.

The actor’s properties allow you to set:
–  The Listener Distance (where 0 = at the position of the player character, and 1 = at the position of the player camera)
– The Interp Speed (where 100 = very fast movement / no inertia, and 1 = very slow movement / high degree of inertia)

sshot-2

You can toggle the actor’s Hidden in Game state using the L key (obviously you can change this key event if you wish) so that you can visualize the listener position if needs be.

The actor can be downloaded from here: click me

If you find this useful and have any thoughts / suggestions / ideas for developing it further so that it can be of more use, I’d be interesting in hearing from you…

 

Visualising LPF Radii

So, this idea came about after a question on gameaudio.slack about whether there was a console command to enable debugging of LPF radii similar to that for volume attenuation radii using the ‘stat sounds -debug’ command.
Unfortunately there isn’t!

But, it got me thinking… it can’t be that hard to knock up a quick and dirty solution…
turns out it isn’t! So I thought I’d make the solution available in case anyone else finds it useful.

I created a placeable actor, using BluePrints, that can dropped into a map and then when a specific key is pressed it draws the LPF min and max radii for every AmbientSound within the level.
Here’s a screenshot of it doing it’s stuff ingame:

visualise_lpf_radii_ingame_sshot

The actual BP system is pretty simple…
When the key is pressed an array containing all of the AmbientSounds within the level is created. This is then looped through, and for each actor within the array the actor location, LPF radius min and LPF radius max are obtained. These values are then used to draw some debug spheres within the world.
Here’s a screenshot of the actor BP:

visualise_lpf_radii_actorbp_sshot
(The connection to the input of the [Get All Actors of Class] node is coming from a keypress event – it wouldn’t fit on the screen!)

As you can see, this is a very simple system!

Obviously this could just as easily be done within the Level BP, but making an actor made it easy to distribute the solution.

The actor can be downloaded from here: click me

If you find this useful and have any thoughts / suggestions / ideas for developing it further so that it can be of more use, I’d be interesting in hearing from you…