Making a screensaver

I never thought I'd end up making something like a screensaver. I also never thought I'd end up having so much fun in the process.

Making a screensaver

Few days ago I decided to take part in the ScreensaverJam organized by JW and Kitty. The 2-days event was basically meant to celebrate those (mostly) ugly animations that were supposed to keep our monitors healthy, back in the days.

At the beginning I had the idea of making something procedural and particularly ugly, but after drawing the first few things on screen I fell in love with the idea of creating some cool-looking solar systems. The result is Ultranova.Zero, you can download it here and you can read more about how I made it after the gif.

ultranova0d

Inception

As it often happens , I decided to use GameMaker: Studio to develop this piece of software. I am super comfortable working with this tool; if I’m not mistaken I first used it back in 2009, so you can imagine I’d rather use this than any other thing. This is also the only software I used since every bit of the screensaver is drawn via code, so zero extra assets.

My initial idea was to create one of those shapes that bounces when hitting the borders of the screen. The first thing I did was to create an empty object and draw a huge circle on the left side of the screen; then it just hit me that that circle HAD to be a planet. My next step was to make a sort of aura around the planet to make it look like an atmosphere of some sort. Easy stuff, I drew other two circles with a slightly bigger radius but with a lower transparency value (one of the same color, the other white).

The code in the draw event looked pretty much like this:

draw_set_circle_precision(precision);          //remember that the precision value needs to be a multiple of 4!              
draw_circle_colour(x,y,radius,color,color,0);
draw_set_alpha(.5);
draw_circle_colour(x,y,radiusPulse,color,color,0);
draw_set_alpha(.1);
draw_circle_colour(x,y,radius+120,c_white,c_white,0);
draw_set_alpha(1);                             //don't forget to reset the alpha value to 1!

I also took this chance to rename the object oObject to oPlanet – and here I’ll make a tiny digression on naming convention is GM:S.

// START DIGRESSION

One of the things I learned by diving into Spelunky’s source code was how to name objects to make my life easier. While GM:S standard naming convention would suggest obj_Planet, using single letters before names makes your life much easier!

Type Standard convention Shortened convention
Sprite spr_Name sName
Object obj_Name oName
Sound snd_Name aName
Script scr_Name cName
Font fnt_name fName
Timeline tml_Name tName
Path pth_Name pName
Shader shd_Name gName
Room rm_Name rName

It is true that the shortened convention is less descriptive, but once you get used to it there’s no way you’re gonna forget!

// END DIGRESSION

To the stars

Back to the screensaver, I decided that a nice visual effect could have been to have stars orbiting around the planet, so I created oStar and made use of this code and adapted it in the following way:

Create Event

radius = irandom_range(2,10); //variable star radius
orbit_angle = irandom(360); //random angle around the planet
orbit_radius = irandom_range(320,640); //random distance
orbit_speed = random_range(.05,.5); //random speed

Step Event

orbit_angle += orbit_speed;
//rotation code
x = oPlanet.x + orbit_radius * cos(orbit_angle * pi / 180);
y = oPlanet.y - orbit_radius * sin(orbit_angle * pi / 180);

Draw Event

draw_set_circle_precision(global.starPrec); //squares if precision is 4
draw_set_alpha(choose(.2,.3,.4));
draw_circle_color(x,y,radius,c_white,c_white,0);
draw_set_alpha(1);

You can see the result in the tweet below:

After the stars I decided it could have been pretty awesome (in order to make the screensaver more visually appealing) to have a satellite orbiting around the planet. The satellite is a hybrid between the oPlanet code and the oStar code; it’s pretty much like a smaller planet that has the orbiting code in it. I also created a control object oScreensaver which I use for creating all the different elements in the room and deciding some parameters of the procedural generation of the system (see last paragraph).

Surfing the sin

I used the sine function A LOT in order to make some smooth looking movements on screen. The following paragraph is super basic stuff, but perhaps if you just started making digital stuff this might be useful for you. In order to use the sin you will need to track the time of the room so, in this case, I just created global.time = 0; in the creation code of oScreensaver and added global.time++; in the Step event. Since I set the room speed at 60 frames per second, this means that after 1 second, the time will be 60, after 2 seconds it will be 120, and so on.

After you have track of the time, you can use the sin function to create some schwifty effects. The sin function always returns a value between -1 and 1. This means that if every frame I do radiusPulse += sin(time/pulseSpeed), the pulsing radius will first grow (when sin goes from -1 to 1), then shrink (from 1 to -1), then grow again and so on. I used the same principle with the stars, to give them a wave-y kind of movement.

screensaverjam3

The first visually-decent version of Ultranova.Zero

Final touches and procedural logics

There are few things that I still didn’t mention. For the background I just used draw_rectangle_colour(0,0,room_width,room_height,col,col,col,col,0);, where col is generated from a set of pre-defined ones with the make_colour_rgb(r,g,b); function. In this way I made sure that, rather than giving fully random colors, I picked a set of colors that I liked. I also spawn the game title from time to time, and I make sure that if the background is dark, the font will be white, or black in case the background is bright. A simple way to do this is to add up the rgb values of the background colors and devide by 3. A low value will indicate a dark background.

I also added star trails (subconscious inspiration by Panoramical), which is possibly the most un-optimal thing in the entire screensaver (rather than using the particle system, I spawn tiny objects that rapidly die – whoops!). I also tried to give some extra visual variation so that, from time to time, instead of using the stars as stars, I use them as vertices for planet spikes (you can see this in the first gif of the article, all the way up). It’s much simpler than it sounds, really. Finally, I added the possibility to have an extra satellite, I made sure that the satellite is always visible when the new solar system is generated, that the star trails get the colors of the planet and satellites to keep the color palette consistent, and I eventually put the planet at the center of the room to give a pleasant sense of symmetry. I created an exe file, making sure that every 5 seconds a new solar system is generated and randomized correctly.

This is a table of the procedural logic for each element of the screensaver:

Background

  • Color randomly picked from a defined set of palettes
  • 25% chance that the bkg gets darker over time

Planet

  • Variable radius
  • 2% chance of being a square
  • Variable pulsing speed
  • Random color
  • 50% chance to get a satellite

Satellite

  • Variable radius
  • 12.5% chance of being a square
  • Variable orbit speed
  • Variable pulsing speed
  • Random color
  • 33% chance of having a twin satellite

Stars

  • Random amount from a predefined set of possibilities
  • 25% chance of being circular
  • Variable orbit distance
  • Variable orbit speed
  • 20% chance of being used as vertices for planet spikes
  • 25% chance of having wave-y movement
  • 75% chance of having star trails
  • 25% chance of having star trail if used as vertices for planet spikes
  • Variable spike width if used as vertices for planet spikes

Star Trails

  • 50% chance of being drawn like outline or filled
  • Random interval between trail particles (longer if drawn as outline)
  • Same shape as stars
  • Color picked from planet and satellites (if present)

Title

  • 8% chance of appearing
  • Color depends on background brightness

It’s nothing super-complex but it gives enough variation to create very unique (and sometimes unexpected) results. You can download Ultranova.Zero here.