Tutorial: Basic Script - Poyo-SSB/osu-playground GitHub Wiki

Scripting

osu!Playground uses a subset of JavaScript for scripting. This is great because JavaScript is very easy to write. This is terrible because JavaScript has some very annoying limitations which will become apparent later.

That aside, let's write our first script.

The script we're about to make will be very simple—it will have two perfect-circle sliders, one being a mirror image of the other.

Important Functions

There are two functions which control the actual logic of our script. Let's add those:

function start() {

}

function update() {

}

start is called when the script is first loaded and is used for initializing variables. update is called every frame and is used for the drawing of hit objects.

Variables

The cool thing about osu!Playground is that it allows the live manipulation of objects in real time. This is done by having variables that are used by multiple objects. This eliminates the process of applying a transformation to multiple objects to see the results of a tweak.

For this script, we need only three variables, one for each point on the base circular slider.

This is done in the start function. Let's create them:

function start() {
    var start = Playground.AddVector2('start', new Vector2(0, 0));
    var middle = Playground.AddVector2('middle', new Vector2(100, 0));
    var end = Playground.AddVector2('end', new Vector2(200, 0));
}

If this looks complicated, don't worry. If this doesn't look complicated, also don't worry.

What we've just done is defining some global variables. We used the function Playground.AddVector2 with two parameters. The first is the name of the variable, which we use to reference the value of the variable later. The second is the default value, which I set arbitrarily to (0, 0), (100, 0), and (200, 0).

Positions in games are generally defined using vectors. osu!Playground, a 2D application, uses primarily Vector2 because it has two axes. Really, we could call it a "point," but vectors are used for other things, so programmers keep the name generic.

When we write new Vector2(200, 0), it just means that we're referring to a point located at (200, 0) (i.e. x = 200, y = 0).

The function used here returns a reference to the variable we've just created, and we capture those by using var.

Options

If you load this script in osu!Playground right now, you'll notice nothing. That's because—though we created the variables—we haven't told osu!Playground to let us mess around with them.

To do that, we'll have to add some more code to our start function:

    Playground.AddOptionVector2(start, 'Start');
    Playground.AddOptionVector2(middle, 'Middle');
    Playground.AddOptionVector2(end, 'End');

The function used here, Playground.AddOptionVector2, also has two parameters. The first is a direct reference to the variables we created earlier and the second is the name to actually display in the sidebar.

Remember the variables we defined earlier using var? We're using those references in the first argument of the function.

Now, loading the script will yield three handles to drag around and three green options in the sidebar that go with them.

Drawing Objects

Now, we'll begin adding code to the update function.

First things first—we need to store the values of the three variables we created earlier. That is done as follows:

function update() {
    var start = Playground.GetValueVector2('start');
    var middle = Playground.GetValueVector2('middle');
    var end = Playground.GetValueVector2('end');
}

Playground.GetValueVector2 takes only one parameter, the name of the variable of which to get the value. This is the name we defined when we used Playground.AddVector2 to make the variables. The variables we've defined using the var keyword are Vector2s.

Now for the fun part: it's time to draw the slider. Add this to the update function:

    Playground.AddSlider(CurveType.PerfectCurve, [
        start,
        middle,
        end
    ]);

This function, Playground.AddSlider, takes two parameters. The first is the type of curve to use. In this case, we want a circular curve, so the proper value here is CurveType.PerfectCurve. The second is the important one: the points of the slider. In this case, we grabbed our relevant values earlier using Playground.GetValueVector2, so we can just reference those values in an array.

If everything's been done properly, our script should now yield a neat circular slider with three manipulable points. Perfect! The final step is to add the second slider, which will be rotated around the center of the playfield by 180°.

Now... how do we rotate this slider? It's time to do some math.

For convenience, I've exposed a few utility functions that allow for manipulation of Vector2s without having to do the calculations oneself.

One such function is the rotate function, which rotates a Vector2 around another point. It takes three parameters: the first is the point to rotate, the second is the point to rotate around, and the third is the angle.

We've got the first parameter already, so that's out of the way. The second parameter will take a little more work, though. We need it to be the center of the playfield, but... oh no! I've forgotten what that is! Conveniently, we don't need to know the exact numbers.

The Playground provides a few constants we can reference. Here's what the center of the playfield looks like, using only basic math:

var center = new Vector2(Playground.PLAYFIELD_WIDTH / 2, Playground.PLAYFIELD_HEIGHT / 2);

There's nothing special happening there, it's exactly what it looks like: the width divided in half, and the height divided in half.

The third parameter, the angle of rotation, is in radians for mathematical convenience. Knowing that 2π = 360°, it logically follows that 180° is 1π. Where do we get π in our code, though? Do we need to be filsdelama and mash our keypad to get the value? Thankfully, no. JavaScript provides a very useful class called Math that contains many useful functions and constants.

To access π, we simply use Math.PI.

Now we have all of our parameters, so we can draw our second slider:

    Playground.AddSlider(CurveType.PerfectCurve, [
        rotate(start, center, Math.PI),
        rotate(middle, center, Math.PI),
        rotate(end, center, Math.PI)
    ]);

And that's it! Congratulations on your first script! If everything went as planned, the whole file should look something like this.

function start() {
    var start = Playground.AddVector2('start', new Vector2(0, 0));
    var middle = Playground.AddVector2('middle', new Vector2(100, 0));
    var end = Playground.AddVector2('end', new Vector2(200, 0));

    Playground.AddOptionVector2(start, 'Start');
    Playground.AddOptionVector2(middle, 'Middle');
    Playground.AddOptionVector2(end, 'End');
}

function update() {
    var start = Playground.GetValueVector2('start');
    var middle = Playground.GetValueVector2('middle');
    var end = Playground.GetValueVector2('end');

    Playground.AddSlider(CurveType.PerfectCurve, [
        start,
        middle,
        end
    ]);
    
    var center = new Vector2(Playground.PLAYFIELD_WIDTH / 2, Playground.PLAYFIELD_HEIGHT / 2);

    Playground.AddSlider(CurveType.PerfectCurve, [
        rotate(start, center, Math.PI),
        rotate(middle, center, Math.PI),
        rotate(end, center, Math.PI)
    ]);
}

If you haven't done so yet, save the script as a .js or .oss file. Now, you can load it in osu!Playground.