Function - NoodleExtensionsCommunity/How-to-Noodle GitHub Wiki

Functions are a way to call an earlier written section of the code to be ran with only one line. Functions have custom parameters that can be set to different values every time it's called. For example, if you have a repeating effect, you can make a function for it so that you don't have to rewrite everything again. This also makes the script significantly shorter and cleaner.

Making a function

Let's say we wanted to create a function that randomizes the notes' path movement between the times 16 and 32. For the purpose of this example, we will be only using a _position animation.

function randomPath() {
    filterNotes(16, 32).forEach(n => {
        if (!n._customData._animation) n._customData._animation = {};
        n._customData._noteJumpStartBeatOffset = 2;
        n._customData._animation._position = [
            [random(-10, 10), random(-1, 10), random(-15, -5), 0],
            [random(-10, 10), random(-1, 10), random(5, 15), 0.25, "easeInCirc", "splineCatmullRom"],
            [0, 0, 0, 0.45, "easeOutCirc", "splineCatmullRom"]
        ];
    });
}

Here's a rough explanation for what the script does:

  • function randomPath() { we're declaring a new function called randomPath so that it can be called later using randomPath();. Everything after the curly bracket is the code that the function will run upon calling it.
  • filterNotes(16, 32).forEach(n => { targets all notes between times 16 and 32 and then performs the following action for each note targeted after the curly bracket.
  • if (!n._customData._animation) n._customData._animation = {}; is checking for the existance of an animation bracket in the targeted note, if it can't find one, it will make one.
  • n._customData._noteJumpStartBeatOffset = 2; sets the targeted note's spawn offset to 2.
  • n._customData._animation._position sets the _position animation for targeted notes.

Explanation for the _position animation:

  • The random() will pick a random integer between two given numbers.
  • _position has 4 values that it needs for an animation:
    • x (sideways movement)
    • y (vertical movement)
    • z (backwards/forwards movement)
    • time (the time of the animation point)

A time value, when it comes to individual objects is pretty much the following points:

  • 0 = object spawns in.
  • 0.5 = object is at the half of it's lifetime and at the position of the player.
  • 1 = object despawns out behind the player.
  • "easeInCirc" just sets an easing for the second point of the animation.
  • "easeOutCirc" just sets an easing for the last point of the animation.
  • "splineCatmullRom" is explained below.

If you have 3 different points in your animation, you can make a spline out of them by adding "splineCatmullRom" at the end of the last two animation points. Do not add it to the first one as it will not work.

A spline is essentially a curve made out of 3 different points.

An animation without a spline would look like this no spline

And if you add the spline to it, it would look like this spline

  • The }); means the end of note targeting.
  • The last curly bracket means the end of a function.

Using variables

We can change the previous function in a way where we use variables instead of typing out the whole n._customData every time we add something to the _customData of a note. We can replace that with just data.

function randomPath() {
    filterNotes(16, 32).forEach(n => {
        let data = n._customData;
        if (!data._animation) data._animation = {};

        data._noteJumpStartBeatOffset = 2;
        data._animation._position = [
            [random(-10, 10), random(-1, 10), random(-15, -5), 0],
            [random(-10, 10), random(-1, 10), random(5, 15), 0.25, "easeInCirc", "splineCatmullRom"],
            [0, 0, 0, 0.45, "easeOutCirc", "splineCatmullRom"]
        ];
    });
}

We can also make a variable for the animation so that we don't have to type data._animation but just anim instead.

function randomPath() {
    filterNotes(16, 32).forEach(n => {
        let data = n._customData;
        if (!data._animation) data._animation = {};
        let anim = data._animation

        data._noteJumpStartBeatOffset = 2;
        anim._position = [
            [random(-10, 10), random(-1, 10), random(-15, -5), 0],
            [random(-10, 10), random(-1, 10), random(5, 15), 0.25, "easeInCirc", "splineCatmullRom"],
            [0, 0, 0, 0.45, "easeOutCirc", "splineCatmullRom"]
        ];
    });
}

Now, assigning a variable for something we will use only once doesn't really make that much sense in this case. But what if we want the notes to dissolve in from nowhere too?

function randomPath() {
    filterNotes(16, 32).forEach(n => {
        let data = n._customData;
        if (!data._animation) data._animation = {};
        let anim = data._animation

        data._noteJumpStartBeatOffset = 2;
        data._disableSpawnEffect = true;

        anim._position = [
            [random(-10, 10), random(-1, 10), random(-15, -5), 0],
            [random(-10, 10), random(-1, 10), random(5, 15), 0.25, "easeInCirc", "splineCatmullRom"],
            [0, 0, 0, 0.45, "easeOutCirc", "splineCatmullRom"]
        ];
        anim._dissolve = [
            [0, 0],
            [1, 0.125, "easeOutQuad"]
        ];
        anim._dissolveArrow = [
            [0, 0],
            [1, 0.125, "easeOutQuad"]
        ];
    });
}

See? Now using variables has actually become useful in the script.

There's actually another type of variable we can use in a function.

We can make the function be called for different times and durations using them.

function randomPath(start, end) {
    filterNotes(start, end).forEach(n => {

By modifying the top of the function with those start, end variables (comma is a separator), we can call the function using randomPath(48, 64); to make it target notes between times 48 and 64 and not just between 16 and 32 like we had before.

We can also add an offset variable to the function if we want.

function randomPath(start, end, offset) {
data._noteJumpStartBeatOffset = offset;

Now, we can also set a custom offset for the notes every time the script runs by just adding another value after the end value. An example of this would be: randomPath(64, 128, 4); so now the offset is set to 4 for all notes affected by the function.

Remember to set the variable BEFORE it is used in the script.

Duplicating notes

You've probably seen those cool effects in maps where there's tons of fake notes. For example the arrows flying away in my Bloom. This can be easily done by using duplicate notes.

The next example is kind of the reverse of that effect, it's more of a trail of arrows following the note.

function arrowTrail(start, end) {
    filterNotes(start, end).forEach(n => {
        for (let i = 0.25; i <= 1; i += 0.25) {
            let dupe = JSON.parse(JSON.stringify(n));
            let data = dupe._customData;
            if (!data._animation) data._animation = {};
            let anim = data._animation;

            dupe._time += i;

            data._disableSpawnEffect = true;
            data._disableNoteGravity = true;
            data._disableNoteLook = true;
            data._fake = true;
            data._interactable = false;

            anim._dissolve = [0];
            anim._dissolveArrow = [
                [0, 0],
                [1, 0.125, "easeOutQuad"],
                [1, 0.4],
                [0, 0.5, "easeOutCirc"]
            ];

            _notes.push(dupe);
        }
    });
}

So, what's happening here is that we're using a for loop to create 4 arrows after the targeted notes. So, how it works is that we're creating a duplicate note with the let dupe = JSON.parse(JSON.stringify(n)); line. This creates a variable of the original note without affecting the real note itself but rather the fake note. Then we have all the rest of the variables and animation bracket set. After that we get to this dupe._time += i; line. What this does is essentially it increases the fake note's time by the value of i set in the for loop. So, for example if the dupe's _time would be 7 and i would be 0.5, the new time of the fake note will be 7.5. Then we set all the necessary properties for the note and after that we use _dissolve to only show the arrow of the note. _dissolveArrow is then used to dissolve the arrow in and out. Lastly, _notes.push(dupe); pushes the new duplicate note to the map itself.

This all is ran 4 times using a for loop that adds 0.25 to the value of i every time the loop runs until i is greater than 1.

We can also make the function above spawn in a custom amount of notes and how far apart they are spread.

First, we add the new variables we want to the function declaration.

function arrowTrail(start, end, amount, length) {

Then, we modify the for loop in a way where we use math to spread the notes over a specified amount of time.

for (let i = length/amount; i <= length; i += length/amount) {

The reason we used length/amount is because we want to divide the length by the amount to get the perfect distance between each note.

So, if we call the function using arrowTrail(10, 20, 8, 0.5); it will now spawn 8 notes after each targeted note. These notes will be spread equally within 0.5 beats.