A floating golden 3

3 tips for Processing I wish I had known sooner

| Processing   Programming   Tips  

I've been working with Processing for some time now. Especially in the beginning I would have liked to know these 3 (or maybe 4?) tricks that make every sketch much more fun.

1. adjust pixel density

If you've always wondered why your sketches look so pixelated on your ultra-high resolution monitor (or on a Mac with Retina display), let me introduce you to pixelDensity(). In combination with displayDensity() Processing detects your screen resolution automatically and adjusts the output of the single pixels to the pixel density of your monitor. This way edges, curves and especially texts are rendered in full sharpness and not (anymore) pixelated. If you specify the command once in your setup function as follows, Processing will do the rest from then on:

pixelDensity(displayDensity());

The result is quite respectable:

The 2D-Rocket is very pixelated without the pixel density being adjusted.
Before
The 2D rocket is way smoother with pixel density being accounted for.
After

By the way, you can optionally hardcode a number (usually "2" for screens with double pixel density) instead of displayDensity() to avoid any compromises.

I must confess to my shame that I only recently discovered this command. On my MacBook, pixelated fonts in particular drove me crazy before. In fact, some of my paid work has also been rolled out in the past without taking pixel density into account. In retrospect, however, all of these programs were used on devices that had a pixel density of 1x (convention touchscreens and infotainment monitors, for example). Got Lucky, I guess... 😅 But I'm not going to count on that in the future!

Good to know: pixel-based commands like get(), set(), blend() etc. are also affected by pixelDensity(). So from now on, any filters and image operations can shine in glorious clarity!

2. place shapes exactly around texts

There are moments when you just want to draw a small box around a text. Then you try around until text and box are arranged halfway respectably on the stage and then... a text change comes in. For exactly such cases there is textWidth(). This command calculates the width of a string or character and makes it available. In other words:

    size(200,200);
  pixelDensity(displayDensity());
  int textHeight = 32;
  textSize(textHeight);
  String helloWorld = "Hello World";
  fill(255);
  rect(10, height/2, textWidth(helloWorld), textHeight);
  fill(0);
  text(helloWorld, 10, height/2 + textHeight);

The box adapts to the text. Combined with a grid system, this makes it easy to build a user interface. When I think about how much time I spent - especially when I started using Processing - fitting text with backgrounds, it gives me the creeps.

3. threads and parallel flows for splash screens, loading animations and menus.

If you have ever used fonts in Processing, you might know the problem: Before a text is displayed, the font has to be loaded and processed by Processing. This leads either to lag in the program or, if you called createFont()already in Setup, to an extremely long loading process where only a gray window is shown until the font is finally ready for use. Granted: Providing a .vlw font via Tools->Make Font can speed up the process, but depending on the font it can still take a few seconds. Especially with games this is very annoying, since apparently not even a splash screen can be displayed while the program is loading - or so I thought, until I discovered the thread() function.

thread(), as the name suggests, opens a new parallel thread running off the main loop (the void draw()). Now, to set up a splash screen and load the fonts and other assets alongside, the following code can be used:

PFont mainFont;
boolean contentLoaded = false;
boolean contentLoading = false; 

void setup(){
  size(500,500);
}
void draw(){
  if(contentLoaded){
    //Whatever happens after the splashscreen
  }else if(contentLoading){
    //Your splashscreen
  }else{
   thread("loadContent");
   contentLoading = true;
  }
}

void loadContent(){
  mainFont = createFont("Audiowide-Regular.ttf", 32);
  textFont(mainFont);
    contentLoading = false;
  contentLoaded = true;

As you can see, thread() takes the name of any function in form of a string as argument to execute it - detached from the main loop. A little hint on the side: Especially when loading and creating fonts asynchronously, make sure that you call the loading function only once (ensured above by contentLoading). Otherwise the computer will start a new thread with each frame and create the font over and over again, which will almost certainly lead to performance problems.

4. bonus tip: use FrameCount and Modulo together to do a collision check every few frames

I've had the pleasure of working on some arcade-style games that were to be created in Processing. That means classic Jump'n'Runs and especially Auto-Runners. Besides the movement functions, the collision detection is a regular annoyance. And anyone who has ever implemented collision detection for more than 100 objects at the same time in Processing knows that this can demand a lot from the computer. Often, however, querying the collision only a few times per second and not with every frame is enough to alleviate the computational load considerably without the game players noticing that there are sometimes minimal delays in the collisions. What I have seen more often in some of my tutorials in this context is that an extraVariable is used for this, which is incremented with each frame and reset to 0 after n repetitions, when the desired value is reached. For example, to increment and output a number by 5 every 5 frames, this would be programmed here:

int counter = 0;
int collector = 0;
void draw(){
  counter++;
  if(counter == 5){
      collector += counter;
    println(collector);
    counter = 0;
      // At this point, you could have your advertisement.
      // Or a collision query.
  }
}

However, this can also be done much more compactly, because Processing already has the incrementing variable built in by default. The magic word is frameCount and specifies the number of the current frame. We can take advantage of this by applying the % operator (modulo) to it. If you have never worked with modulo before, here is the short form:

The modulo outputs the "remainder" of a division, as you often learn in elementary school. For example, if 5/3 = 1 remainder 2, then the % operator will only output the remainder as a result: 5%3 = 2

If we now think this concept further, every nth frame is given exactly when frameCount % n == 0, (frameCount thus "smoothly" divisible). Using the example n = 3:

  • 0 % 3 = 0
  • 1 % 3 = 1
  • 2 % 3 = 2
  • 3 % 3 = 0
  • 4 % 3 = 1
  • 5 % 3 = 2
  • 6 % 3 = 0
  • etc...

Our example from above can be shortened in this way as follows:

void draw(){
  if(frameCount % 5 == 0){
    println(frameCount);
  }
}

Of course, the example with the println() command is not very impressive. But suppose the collision query were in the if query: the resources saved would be enormous!

I hope these tips have helped you. Feel free to leave a comment if you have any questions or feedback. I would also be very happy if you subscribe to my newsletter. I don't write often, but when I do, it's worth it - I promise!

Comments

Add a comment

No coments written yet 😢