We are about to switch to a new forum software. Until then we have removed the registration on this forum.
That's a question I see coming up regularly, people attempting to display a message, but saying it only flashes briefly on screen.
To show how to do it properly, we need a simple sketch to build upon. Say we use the good old bouncing ball:
// Ball handling
float posX, posY; // Position
float speedX, speedY; // Movement (linear)
int radius; // Radius of the ball
color ballColor; // And its color
void setup()
{
size(600, 400);
smooth();
// Initialize the ball's data
posX = 120;
posY = 50;
speedX = -2;
speedY = 3;
radius = 24;
ballColor = #002277;
}
void draw()
{
// Erase the sketch area
background(#AAFFEE);
// Compute the new ball position
moveBall();
// And display it
displayBall();
}
void moveBall()
{
// Move by the amount determined by the speed
posX += speedX;
// Check the horizontal position against the bounds of the sketch
if (posX < radius || posX > width - radius)
{
// We went out of the area, we invert the h. speed (moving in the opposite direction)
// and put back the ball inside the area
speedX = -speedX;
posX += speedX;
}
// Idem for the vertical speed/position
posY += speedY;
if (posY < radius || posY > height - radius)
{
speedY = -speedY;
posY += speedY;
}
}
void displayBall()
{
// Simple filled circle
noStroke();
fill(ballColor);
ellipse(posX, posY, radius * 2, radius * 2);
}
Now, say that if the user clicks on the moving ball, we display a message, then we continue. We need to display a text, so we set up the font in setup():
PFont f = createFont("Arial", 48);
textFont(f);
It can be replaced by a simple textSize(48)
in recent versions of Processing, now using a default font if none is provided. Note also I use createFont()
because it is convenient (no need to create stuff in the data folder) but to make a more portable sketch across system (that might not have this font), you better use loadFont()
.
We can now add the management of the mouse click:
void mousePressed()
{
// If mouse position is close of the center of the ball (less than the radius in distance)
if (dist(mouseX, mouseY, posX, posY) < radius)
{
// We have a hit, and indicate it
fill(#FFAA88);
text("You got it!", 150, height / 2);
}
}
When running it, you can see where this code is faulty: at best, we see the text flashing briefly. That's because the click event happens somewhere in the middle of a frame, and updates it. But the next frame, some milliseconds later, just erases the text.
There are several ways to address the issue, some of them are flawed...
New users often try or attempt wrong approaches, we will briefly examine them here.
The common attempt is to use delay()
, which stops the sketch for the given duration. This is so bad that the function almost disappeared from Processing 2.0 (but it was kept because it is useful with the Serial library).
delay()
literally stops the sketch: no secondary animation is possible, counters are halted, the user cannot click or hit a key, etc. This "stop the world" approach is to avoid in general.
Users often ask, in the forum, if we can reset the millis()
value, ie. set it to zero. It is just not possible (or wanted), as it counts the number of milliseconds spent since the start of the sketch. But it is a step in the right direction, as we will see below.
One of the most flexible and extensible to do it properly is to use states, aka. a state machine.
Here, it can be reduced to its simplest form, using a simple boolean, bDisplayMessage
, that we declare at global level. We also need to track time to be sure the message is displayed long enough.
boolean bDisplayMessage; // False by default
int startTime; // The (last) time when the mouse have been clicked
final int DISPLAY_DURATION = 1000; // in milliseconds = 1s
We move the drawing of the message to draw()
:
void draw()
{
background(#AAFFEE);
if (bDisplayMessage)
{
fill(#FFAA88);
text("You got it!", 150, height / 2);
}
else
{
moveBall();
displayBall();
}
}
and then the mouse handler just sets the boolean:
void mousePressed()
{
// Better than if (cond) b = true; else b = false; !
bDisplayMessage = dist(mouseX, mouseY, posX, posY) < radius;
}
Now we just have to stop displaying the message and to resume the game after a while. We do the check in draw()
, see it in the complete code (stripped of most comments for brevity):
// Ball handling
float posX, posY;
float speedX, speedY;
int radius;
color ballColor;
boolean bDisplayMessage;
int startTime;
final int DISPLAY_DURATION = 1000; // 1s
void setup()
{
size(600, 400);
smooth();
PFont f = createFont("Arial", 48);
textFont(f);
posX = 120;
posY = 50;
speedX = -2;
speedY = 3;
radius = 24;
ballColor = #002277;
}
void draw()
{
background(#AAFFEE);
if (bDisplayMessage)
{
fill(#FFAA88);
text("You got it!", 150, height / 2);
// If the spent time is above the defined duration
if (millis() - startTime > DISPLAY_DURATION)
{
// Stop displaying the message, thus resume the ball moving
bDisplayMessage = false;
}
}
else
{
moveBall();
displayBall();
}
}
void mousePressed()
{
bDisplayMessage = dist(mouseX, mouseY, posX, posY) < radius;
// Record the time of the event
startTime = millis();
}
// Same moveBall/displayBall code, see above
In the next articles, we will see how to how to handle more states, and use classes to make this sketch scalable (using more balls).