This blog is dedicated to getting you up and running doing real programming using the language called processing. It is meant for anyone with some normal computer experience, but maybe someone that does not have the patience to pick up a long and detailed book about programming, It is more a learn by doing approach used here. Source code examples for simple classic games and cool graphics effects will be posted and discussed.
I grew up first without computers at all, but at age ~11 or so, I got a Commodore 20 (VIC-20). Such a (simple) computer came with an instruction book and power supply. I did not initially get an games or any other software. So the "only" fun one had was to turn it on and try to enter some BASIC programs and learn to program.
Today, one is presented with a very different initial exposure to computers. Once I had kids myself, I wondered how they could be introduced to the fun of programming, without having a large initial step to take. Scratch of course is an easy and quite fun way to go, but when trying this with my kids and doing some programs myself, I found it sometimes hard and cumbersome to express programs graphically.
My older son got introduced in school recently to a new attempt to tech programming. The course/program is called You++. In the background it is using processing as the language. When I looked at what he was doing, I tried it myself and then I downloaded the stand-alone processing package. Now, that seemed both fun and easy to start with.
This blog is an attempt to show you how to start with processing, from scratch, and focus on the "fun" of it, with not a huge focus on knowing exactly on every detail of the language and functions it has and I use. Hope it is helpful to someone.
The video below is going to be an example that will be explained later. But it will be later. A few things needs to be explained first. Enjoy (hopefully)!
Then navigate to the download page (donate money if you like, or later if you end up liking it, no pressure) and select your operating system. If you do not know if you have 32-bit or 64-bit windows, then you can always select the 32-bit version (or you can right click on "Computer" and select properties, like shown below to find out if you have 32-bit or 64-bit):
Download and you will get a ".zip" file in your download directory. Extract the files, to the desktop for instance, and you are done.
Now, go into the new directory (on the desktop) and you can start processing by double-clicking on the "processing" icon. You should then soon see:
Now we are ready to test our very first program. Enter this into the window, or copy and paste it:
Ok, so you tried it and got to draw circles in the window. But how did that happen? In processing one can insert comments in the code by entering "//". All text on the line after the two forward slashes are ignored by processing. The first program can be explained with inline comments like this:
It might still seem a little magical that this short code of just two routines (called procedures) does what you have seen it does. Lets try an addition. Colors are made up of one number, between 0-255 that represents 0=black, 128=grey and 255=white, and all the grey shades in-between. Lets change the code to:
// setup() is called once when the program starts executionvoidsetup () {
// Open a window. First 400 is the width in pixels, second 400 the heightsize(400, 400);
}
// draw() is called once every frame update (default 30 per second)voiddraw () {
// Set the fill color to a random grey scalefill(random(0,255));
// Draw an ellipse at position X=mouseX, Y=mouseY. The mouseX/Y are built in // variables that are updated with the mouse pointer// First 20 is the width and second 20 is the height, in pixelsellipse(mouseX, mouseY, 20, 20);
}
If you enter this (or just add the "fill(random(0,255));" line, and run, you should get this (after moving the mouse over the window):
Now, that might look a little nicer. The "random(0,255)" will simply pick a random number between 0 (black) and 255 (white). What if you do not want to have that long "tail" of circles. Then you could add another command in your program;
// setup() is called once when the program starts executionvoidsetup () {
// Open a window. First 400 is the width in pixels, second 400 the heightsize(400, 400);
}
// draw() is called once every frame update (default 30 per second)voiddraw () {
// Clear the screen setting the color to 255 (white)background(255);
// Set the fill color to a random grey scalefill(random(0, 255));
// Draw an ellipse at position X=mouseX, Y=mouseY. The mouseX/Y are built in// variables that are updated with the mouse pointer// First 20 is the width and second 20 is the height, in pixelsellipse(mouseX, mouseY, 20, 20);
}
This, when executed will make the screen look like this:
I think I prefer the "tail". But what about real color? Easy, instead of just one value (for grey) we use a set of three values in the "fill()" call. These three value are RED, GREEN, BLUE, or simply known as RGB. For instance (255,0,0)=RED. Check out this link to find colors and their values. Now add this (and remove the "background()" call;
// setup() is called once when the program starts executionvoidsetup () {
// Open a window. First 400 is the width in pixels, second 400 the heightsize(400, 400);
}
// draw() is called once every frame update (default 30 per second)voiddraw () {
// Set the fill color to a random colorfill(random(0, 255), random(0, 255), random(0, 255));
// Draw an ellipse at position X=mouseX, Y=mouseY. The mouseX/Y are built in// variables that are updated with the mouse pointer// First 20 is the width and second 20 is the height, in pixelsellipse(mouseX, mouseY, 20, 20);
}
Let's take that last program with nice colors and add so that we can start and stop the drawing of circles by pressing the left mouse button, and maybe drawing rectangles when pressing the right button.
// setup() is called once when the program starts executionvoidsetup () {
// Open a window. First 400 is the width in pixels, second 400 the heightsize(400, 400);
}
// draw() is called once every frame update (default 30 per second)voiddraw () {
// Set the fill color to a random colorfill(random(0, 255), random(0, 255), random(0, 255));
// Check if mouse button is pressed and if it is left buttonif (mousePressed && (mouseButton == LEFT)) {
// Draw an ellipse at position X=mouseX, Y=mouseY. The mouseX/Y are built in// variables that are updated with the mouse pointer// First 20 is the width and second 20 is the height, in pixelsellipse(mouseX, mouseY, 20, 20);
} elseif (mousePressed && (mouseButton == RIGHT)) {
// Draw an square at position X=mouseX, Y=mouseY. The mouseX/Y are built in// variables that are updated with the mouse pointer// First 20 is the width and second 20 is the height, in pixelsrect(mouseX, mouseY, 20, 20);
}
}
The output looks like this:
The "if" statement is looking at the condition in the paranthesis and if true, executes that code, otherwise not. When comparing values it is important to use "==" for equal and not just "=". The "&&" is for "and". I.e. both the "mousePressed" needs to be true as well as the "mouseButton == LEFT".
To get help on what commands do and example on how to use them, click on Help->Reference in the menu on the window:
You will then see this:
And here are all the built in commands listed. Each one has a small example to it. Try to find the help for "iF" or "rect".
Some classic programming example involves a ball that is bouncing back and forth when reaching the edge of the screen. It is part of the action in some classic games starting with breakout . So how is that done? Well, take a look at this basic program:
// Run once at startvoidsetup() {
// 500 pixels wide, 400 pixel height// This call will set system variables (width, height)size(500, 400);
}
// Variables to keep track of position and speed of ballint x = 0;
int y = 0;
int x_speed = 5;
int y_speed = 5;
// Called every re-draw, defaul 30 times per secondvoiddraw() {
// Clear screen to blackbackground(0);
// Set fill color to whitefill(255);
// Draw a circle at position x,y, 10 pixels largeellipse(x, y, 10, 10);
// Update position by adding speed
x = x + x_speed;
y = y + y_speed;
}
If you enter it into the processing editor, or simple copy it, and then run you will see this:
But... that is not quite right is it? The ball starts off but when it reaches the bottom then it disappears... Not what we want. Solution? Add this lines before the last curly bracket "}":
if (y>height)
y_speed = -y_speed;
Now re-run... All good? Well yes, it bounces when it reaches the bottom, but then it is gone again. We need to detect when the x is also out of range. Add:
if (x>width)
x_speed = -x_speed;
Now, re-run. When the ball reached the bottom or the right side, we reverse the speed and it looks like it is bouncing. But we still have a problem. We also need to detect the upper side and the left side. There are two ways to write this. Compare the x and y in these two:
if (y>height)
y_speed = -y_speed;
elseif (y<0)
y_speed = -y_speed;
if (x>width || x<0)
x_speed = -x_speed;
They both do the job. The "||" sign mean logical OR. I.e. if (x>width) OR (x<0) then do the next statement. Since the same is happening for both cases, this is more compact and easier to write and read. Job done. Whole program is now:
// Run once at startvoidsetup() {
// 500 pixels wide, 400 pixel height// This call will set system variables (width, height)size(500, 400);
}
// Variables to keep track of position and speed of ballint x = 0;
int y = 0;
int x_speed = 5;
int y_speed = 5;
// Called every re-draw, defaul 30 times per secondvoiddraw() {
// Clear screen to blackbackground(0);
// Set fill color to whitefill(255);
// Draw a circle at position x,y, 10 pixels largeellipse(x, y, 10, 10);
// Update position by adding speed
x = x + x_speed;
y = y + y_speed;
if (y>height)
y_speed = -y_speed;
elseif (y<0)
y_speed = -y_speed;
if (x>width || x<0)
x_speed = -x_speed;
}
And that keeps the ball bouncing until the end of time...
Remember that link I gave you to the breakout game? Well, now we have a bouncing ball. What we need is a paddle that we can move to the left and right on the screen. This is simply added by using the "rect()" command. This command, like the ellipse() takes four inputs:
rect(x position, y position, width, height)
But what we have not pointed out is what the x,y position is referencing to. For a ellipse the default is to the middle/center of the ellipse, but for a rectangle it is the upper left corner. One can change this reference for these types of objects with a command:
rectMode(CENTER);
This will now make the x,y reference the center of the rectangle. It makes it a little easier to deal with the position of the obejects, I think.
to detect key presses, we can use some built in variables in the draw() routing. They are
keyPressed - set to 1 (TRUE) when any key is pressed
keyCode - Set to special keys that is not a sign, like arrow left (LEFT)
key - Set to the key pressed, like 'A' or 'a' (note upper/lower case is not same)
This can now be put together to this program:
// Run once at start
voidsetup() {
// 500 pixels wide, 400 pixel height// This call will set system variables (width, height)size(500, 400);
rectMode(CENTER);
}
// Variables to keep track of paddleint x_paddle = 250, y_paddle = 370;
int paddle_width_half = 40;
// Called every re-draw, defaul 30 times per secondvoiddraw() {
// Check if keys are pressedif (keyPressed) {
if (keyCode == RIGHT || key == 'd') {
// Move paddle right
x_paddle = x_paddle + 8;
} elseif (keyCode == LEFT || key == 'a') {
// Move paddle left
x_paddle = x_paddle - 8;
}
}
// Clear screen to blackbackground(0);
// Set fill color to whitefill(255);
// draw paddlerect(x_paddle, y_paddle, paddle_width_half*2+1, 11);
}
Enter it, or copy paste, and run:
Try moving it with the left/right arrow. Personally I think we need to add so that one cannot go outside the screen. Adding this after the checking of keys will do that.
if (x_paddle>width) x_paddle = width;
if (x_paddle<0) x_paddle = 0;
If you take what is in the 5th and 6th post and put it all in the same program you will get a bouncing ball and a moving paddle. Let's do that. The code is (with some clean up):
// Run once at startvoidsetup() {
// 500 pixels wide, 400 pixel height// This call will set system variables (width, height)size(500, 400);
rectMode(CENTER);
}
// Variables to keep track of position and speed of ballint x = 0;
int y = 100;
int x_speed = 5;
int y_speed = 5;
// Variables to keep track of paddleint x_paddle = 250, y_paddle = 370;
int paddle_width_half = 40;
// Called every re-draw, defaul 30 times per secondvoiddraw() {
// Update position by adding speed
x = x + x_speed;
y = y + y_speed;
if (y>height)
y_speed = -y_speed;
elseif (y<0)
y_speed = -y_speed;
if (x>width || x<0)
x_speed = -x_speed;
// Check if keys are pressedif (keyPressed) {
if (keyCode == RIGHT || key == 'd') {
// Move paddle right
x_paddle = x_paddle + 8;
} elseif (keyCode == LEFT || key == 'a') {
// Move paddle left
x_paddle = x_paddle - 8;
}
}
// Check if paddle is at edge of screen if (x_paddle>width) x_paddle = width;
if (x_paddle<0) x_paddle = 0;
// Clear screen to blackbackground(0);
// Set fill color to whitefill(255);
// Draw a circle at position x,y, 10 pixels largeellipse(x, y, 10, 10);
// draw paddlerect(x_paddle, y_paddle, paddle_width_half*2+1, 11);
}
Try to copy it and run it. Almost a start of the breakout game, right? But that code does nothing when the ball hits the paddle and the ball does not disappear when it reaches the bottom of the screen. Let's try to fix that. Making it disappear at the bottom is simple. We just remove the check where we reverse the y_speed when it reaches the bottom. I.e. removing:
if (y>height)
y_speed = -y_speed;
Will make the ball disappear at the bottom. How about making the ball "bounce" of the paddle? It is not as simple as what we just removed, as the paddle is smaller, and can move. We need to check if the ball is hitting where the paddle is at the moment. We need to check that 4 conditions are met at the same time. The ball needs to be in x-position where the paddle is (checked in the first two lines below) and then we also need to check that the paddle is in y-position where we have the paddle. We need to have a range here as the ball is travelling with some speed and will not move just one pixel at the time. Place this code just before the real drawing begins (i.e. at the background()):
// Check if ball collides with paddleif ((x_paddle-paddle_width_half)<x && (x_paddle+paddle_width_half)>x &&
(y_paddle-10)<y && (y_paddle)>y) {
// ball is hitting paddle rectangle, reverse y_speed
y_speed = -y_speed;
}
Now try it and run it. You should be able to keep the ball bouncing if you have skills and precision for a while. What if we tried to add a score keeping for each bounce of the paddle you manage? Declare a global variable (done outside the setup() and draw() routines) called "score" like:
// score keeping
int score = 0;
And add an increment to the score inside the "if" where we detected the hit of the paddle:
// Check if ball collides with paddleif ((x_paddle-paddle_width_half)<x && (x_paddle+paddle_width_half)>x &&
(y_paddle-10)<y && (y_paddle)>y) {
// ball is hitting paddle rectangle, reverse y_speed
y_speed = -y_speed;
score = score + 1;
}
But how would we know the score? We need to display the score. This is done by:
Remember to put this after the "background()" call, since that is erasing the whole screen. So far we have this code, if you have followed along:
// Run once at startvoidsetup() {
// 500 pixels wide, 400 pixel height// This call will set system variables (width, height)size(500, 400);
rectMode(CENTER);
}
// Variables to keep track of position and speed of ballint x = 0;
int y = 100;
int x_speed = 5;
int y_speed = 5;
// Variables to keep track of paddleint x_paddle = 250, y_paddle = 370;
int paddle_width_half = 40;
// score keepingint score = 0;
// Called every re-draw, default 30 times per secondvoiddraw() {
// Update position by adding speed
x = x + x_speed;
y = y + y_speed;
if (y<0)
y_speed = -y_speed;
if (x>width || x<0)
x_speed = -x_speed;
// Check if keys are pressedif (keyPressed) {
if (keyCode == RIGHT || key == 'd') {
// Move paddle right
x_paddle = x_paddle + 8;
} elseif (keyCode == LEFT || key == 'a') {
// Move paddle left
x_paddle = x_paddle - 8;
}
}
// Check if paddle is at edge of screen if (x_paddle>width) x_paddle = width;
if (x_paddle<0) x_paddle = 0;
// Check if ball collides with paddleif ((x_paddle-paddle_width_half)<x && (x_paddle+paddle_width_half)>x &&
(y_paddle-10)<y && (y_paddle)>y) {
// ball is hitting paddle rectangle, reverse y_speed
y_speed = -y_speed;
score = score + 1;
}
// Clear screen to blackbackground(0);
// Set fill color to whitefill(255);
// Draw a circle at position x,y, 10 pixels largeellipse(x, y, 10, 10);
// draw paddlerect(x_paddle, y_paddle, paddle_width_half*2+1, 11);
// Display scoretextSize(16);
textAlign(RIGHT);
text("Score", 80, 390);
textAlign(LEFT);
text(score, 90, 390);
}
If you haven't followed along, copy and paste the whole thing and run it. Nice right? Almost a breakout game. Certainly a game, although simple. Now, what is missing is that we do not detect when you miss and declare "Game Over" and have a way to restart. This is fixed by adding two parts. One print at the very end, before the last "}" in the draw():
And then also inside the "keyPressed" "if" statement, adding another check for hitting the spacebar:
// Check if keys are pressedif (keyPressed) {
if (keyCode == RIGHT || key == 'd') {
// Move paddle right
x_paddle = x_paddle + 8;
} elseif (keyCode == LEFT || key == 'a') {
// Move paddle left
x_paddle = x_paddle - 8;
} elseif (key == ' ') {
// Restart
x = 0;
y = 100;
x_paddle = 250;
score = 0;
}
}
This last check for ' ' will reset the ball position, paddle position, and score to what it started with. Add it and run it. Nice? That is indeed a real game. Not too fancy and one get bored quickly, but still, a game. This is how it looks like:
Full source code:
// Run once at startvoidsetup() {
// 500 pixels wide, 400 pixel height// This call will set system variables (width, height)size(500, 400);
rectMode(CENTER);
}
// Variables to keep track of position and speed of ballint x = 0;
int y = 100;
int x_speed = 5;
int y_speed = 5;
// Variables to keep track of paddleint x_paddle = 250, y_paddle = 370;
int paddle_width_half = 40;
// score keepingint score = 0;
// Called every re-draw, defaul 30 times per secondvoiddraw() {
// Update position by adding speed
x = x + x_speed;
y = y + y_speed;
if (y<0)
y_speed = -y_speed;
if (x>width || x<0)
x_speed = -x_speed;
// Check if keys are pressedif (keyPressed) {
if (keyCode == RIGHT || key == 'd') {
// Move paddle right
x_paddle = x_paddle + 8;
} elseif (keyCode == LEFT || key == 'a') {
// Move paddle left
x_paddle = x_paddle - 8;
} elseif (key == ' ') {
// Restart
x = 0;
y = 100;
x_paddle = 250;
score = 0;
}
}
// Check if paddle is at edge of screen if (x_paddle>width) x_paddle = width;
if (x_paddle<0) x_paddle = 0;
// Check if ball collides with paddleif ((x_paddle-paddle_width_half)<x && (x_paddle+paddle_width_half)>x &&
(y_paddle-10)<y && (y_paddle)>y) {
// ball is hitting paddle rectangle, reverse y_speed
y_speed = -y_speed;
score = score + 1;
}
// Clear screen to blackbackground(0);
// Set fill color to whitefill(255);
// Draw a circle at position x,y, 10 pixels largeellipse(x, y, 10, 10);
// draw paddlerect(x_paddle, y_paddle, paddle_width_half*2+1, 11);
// Display scoretextSize(16);
textAlign(RIGHT);
text("Score", 80, 390);
textAlign(LEFT);
text(score, 90, 390);
if (y>height) {
textSize(40);
textAlign(CENTER);
text("Game over", 250, 150);
}
}
We seem to be moving to a breakout game. In that we also need some blocks. We cannot just have a bouncing ball and moving paddle. To keep track of the blocks we could use an array. Each element in the array represents a block. If it is not hit, and exists, then the element could be '1', when it is hit, we can set the element to '0'. If we start with the basics, let's declare a global array of ints to keep track of the blocks, and lets initialize them in the setup() routine:
// We will have 20 blocks. blocks[i]==1 means it still existsint[] blocks = newint[20];
// Run once at startvoidsetup() {
int i;
// 500 pixels wide, 400 pixel height// This call will set system variables (width, height)size(500, 400);
rectMode(CENTER);
for (i=0; i<20; i++) {
blocks[i] = 1;
}
}
Now, that's done, we also need to draw them. There are several ways to do it. What I did was to loop through all blocks with using a local variable (declared inside setup()) 'i'. Then I calculate the x, y pixel position of the block we are looking at right now. The picture shows how the blocks[] array corresponds to the blocks.
And this is the code to draw them:
voiddraw() {
int i, x, y;
// Erase the screen, all blackbackground(0);
// Loop through all the potential blocksfor (i=0; i<20; i++) {
// Calculate the x,y position of upper right corner
x = i%5*100+10; // %5 is modulus - will result 0-4 always
y = 40*(i/5)+10;
// Check if we we have a block (blocks[x] is 1)if (blocks[i]==1) {
// Draw the blockrect(x+40, y+10, 80, 20);
}
}
}
Now it is a nice collection of blocks. But they are all there, always. How do we "remove" them. We can test by checking if the mouseX/Y is on one of the blocks and if it is, remove it. This can be done by this code:
// Check if mouse is over the block, if so, remove it (blocks[i]=0)if (mouseX>x && mouseX<(x+80) &&
mouseY>y && mouseY<(y+20))
blocks[i]=0;
Similar to how we checked if the ball hit the pad, this is done by checking if it is inside the coordinates for the blocks. The x,y is same as when we did the drawing of the blocks. The last thing we would want to add in this example, is to detect when all blocks are gone. We can do this by setting a variable to '1' before drawing anything and if we draw any number of blocks, setting it to '0'. If it is '1' after the loop, we did not draw anything. The complete code for this is
// We will have 20 blocks. blocks[i]==1 means it still existsint[] blocks = newint[20];
// Run once at startvoidsetup() {
int i;
// 500 pixels wide, 400 pixel height// This call will set system variables (width, height)size(500, 400);
rectMode(CENTER);
for (i=0; i<20; i++) {
blocks[i] = 1;
}
}
voiddraw() {
int i, x, y;
int blocks_gone;
// Erase the screen, all blackbackground(0);
blocks_gone = 1;
// Loop through all the potential blocksfor (i=0; i<20; i++) {
// Calculate the x,y position of upper right corner
x = i%5*100+10; // %5 is modulus - will result 0-4 always
y = 40*(i/5)+10;
// Check if we we have a block (blocks[x] is 1)if (blocks[i]==1) {
// Draw the blockrect(x+40, y+10, 80, 20);
// Since we drew a block, all are not gone
blocks_gone = 0;
}
// Check if mouse is over the block, if so, remove it (blocks[i]=0)if (mouseX>x && mouseX<(x+80) &&
mouseY>y && mouseY<(y+20))
blocks[i]=0;
}
if (blocks_gone==1)
text("You win", 200, 200);
}
To make a "complete" game, probably with bugs and quirks, we just need to put together the paddle, moving ball, and the disappearing blocks. Well, and add some final touched such as introducing a level (ball speeds up for each level) and to add detection if the ball hit the sides of the blocks for just bouncing in the horizontal direction. Adding it together and adding those touches produces this code:
// We will have 20 blocks. blocks[i]==1 means it still existsint[] blocks = newint[20];
// Variables to keep track of position and speed of ballint x = 250 + int(random(-80,80));
int y = 350;
int x_speed = 3;
int y_speed = -3;
// Variables to keep track of paddleint x_paddle = 250, y_paddle = 370;
int paddle_width_half = 40;
// keep score int score = 0;
int level = 1;
int wait = 0;
// Run once at startvoidsetup() {
int i;
// 500 pixels wide, 400 pixel height// This call will set system variables (width, height)size(500, 400);
rectMode(CENTER);
// All blocks existfor (i=0; i<20; i++) {
blocks[i] = 1;
}
}
// Called every re-draw, defaul 30 times per secondvoiddraw() {
int i, x_tmp, y_tmp;
int blocks_gone;
// Update ball position by adding speed
x = x + x_speed;
y = y + y_speed;
if (x>width || x<0)
x_speed = -x_speed;
if (y<0)
y_speed = -y_speed;
// Check if keys are pressedif (keyPressed) {
if (keyCode == RIGHT || key == 'd') {
// Move paddle right
x_paddle = x_paddle + 8;
} elseif (keyCode == LEFT || key == 'a') {
// Move paddle left
x_paddle = x_paddle - 8;
} elseif (key == ' ') {
// Restart
x = 250 + int(random(-80,80));
y = 350;
x_speed = 3;
y_speed = -3;
x_paddle = 250;
score = 0;
level = 1;
// All blocks existfor (i=0; i<20; i++) {
blocks[i] = 1;
}
}
}
// Check if ball collides with paddleif ((x_paddle-paddle_width_half)<x && (x_paddle+paddle_width_half)>x &&
(y_paddle-10)<y && (y_paddle)>y) {
// ball is hitting paddle rectangle, reverse y_speed
y_speed = -y_speed;
score = score + 1;
}
// Clear screen to blackbackground(0);
// Set fill color to whitefill(255);
// Display scoretextSize(16);
textAlign(RIGHT);
text("Score", 80, 390);
textAlign(LEFT);
text(score, 90, 390);
// Display leveltextAlign(RIGHT);
text("Level", 450, 390);
textAlign(LEFT);
text(level, 460, 390);
// Draw a circle at position x,y, 10 pixels largeellipse(x, y, 10, 10);
// draw paddlerect(x_paddle, y_paddle, paddle_width_half*2+1, 11);
if (y>height) {
textSize(40);
textAlign(CENTER);
text("Game over", 250, 250);
}
blocks_gone = 1;
// Loop through all the potential blocksfor (i=0; i<20; i++) {
// Calculate the x,y position of upper right corner
x_tmp = i%5*100+10; // %5 is modulus - will result 0-4 always
y_tmp = 40*(i/5)+10;
// Check if we we have a block (blocks[x] is 1)if (blocks[i]==1) {
// Draw the blockrect(x_tmp+40, y_tmp+10, 80, 20);
// Since we drew a block, all are not gone
blocks_gone = 0;
}
// Check if ball is over the block, if so, remove it (blocks[i]=0) // first check bounce on top/bottomif (x>(x_tmp+4) && x<(x_tmp+76) &&
y>y_tmp && y<(y_tmp+20) && blocks[i]==1) {
blocks[i]=0;
y_speed = -y_speed;
score = score + 5;
}
// first check bounce on sidesif (((x>(x_tmp-5) && x<x_tmp) || (x>(x_tmp+80) && x<(x_tmp+85))) &&
y>y_tmp && y<(y_tmp+20) && blocks[i]==1) {
blocks[i]=0;
x_speed = -x_speed;
score = score + 5;
}
// first check bounce on sidesif (((x>(x_tmp-1) && x<(x_tmp+5)) || (x>(x_tmp+75) && x<(x_tmp+81))) &&
y>y_tmp && y<(y_tmp+20) && blocks[i]==1) {
blocks[i]=0;
x_speed = -x_speed;
y_speed = -y_speed;
score = score + 5;
}
}
if (blocks_gone==1 && wait<200) {
textSize(40);
textAlign(CENTER);
text("Level cleared...", 250, 210);
text("Preparing next level", 250, 290);
x = 250;
y = 350;
x_speed = 0;
y_speed = 0;
wait++;
if (wait==200) {
wait = 0;
x = 250 + int(random(-80,80));;
y = 350;
x_speed = 3+level;
y_speed = -3-level;
x_paddle = 250;
level++;
// All blocks existfor (i=0; i<20; i++) {
blocks[i] = 1;
}
}
}
One way to write very short programs that can make up complex drawings is to use recursive calls to a function. A recursive function is a function that calls itself from inside the own code in the function. When this is done, it is extremely important to have a termination condition to stop doing that. Otherwise the function will end up locking up the program/computer and will not respond at all.
A very classic example of this is to draw a "tree". This is done by drawing a line, just a line in a function, but then in the call, calling itself again twice. The call to itself is then modified to have a slightly shorter line, and at an angle... Enough talk. This is about "learn by doing". Here is the code:
// Run once
voidsetup() {
frameRate(30);
// size(1000, 1000);
fullScreen();
}
// Run "frameRate" times per secont
voiddraw() {
// angle is the amount of tilt we do on the line.
// In radians : 2*PI radians = 360 degrees
float angle = (width/2-mouseX)*TWO_PI/width;
// len is the length of the first line
float len = (height-mouseY)/2;
background(0); // Black
smooth(4);
stroke(255); // White
// Print the text
text("Len = " + len + " Angle(radians) = " + angle, 15, 15);
// Call the tree function (once)
tree(width/2, height, len, -PI/2, angle);
}
// This is the recursive function
void tree(float x, float y, float len, float alpha, float gamma) {
line(int(x), int(y), int(x+len*cos(alpha)), int(y+len*sin(alpha)));
// We need to have some termination condition. In our case we stop
// Calling recursive once the length has decreased to 1
if (len>1) {
// We change the len and alpha for each call to make up the "tree"
tree(x+len*cos(alpha), y+len*sin(alpha), len*0.64, alpha-gamma, gamma);
tree(x+len*cos(alpha), y+len*sin(alpha), len*0.64, alpha+gamma, gamma);
}
}
Now, some math understanding is required to really see what is going on, but please just copy, paste and run. Move the mouse! Read the text! Have fun!
Just to try some other classic games, consider Astroids. In this you are a small ship that looks like a triangle and your objective is to shoot down astroids that are moving around. In the following example, a ship is drawn, pointing to the mouse, and you can shoot bullets. There are no astroids and nothing fancy. There are two new items used.
The first one is the ability in processing to shift the perspective and then shift back. Using this one can draw the same object (like the triangle in the example) the same way, but still make it appear as if it is moving. This is used in the example to both draw the ship and the bullets.
The second new thing, is the use of Objects and list of objects. Objects are used to group variables and code belonging together. It is then also easy to use it over and over without making the code messy. In the example, the bullet is an object. The ship could also have been an object, but it felt too simple.
Enter this and try!
// List of shots// Shots is an object we have defined that represents a single bulletArrayList<shot> shots = newArrayList<shot>();
// Run oncevoidsetup () {
frameRate(60);
size(500, 500);
// Whitestroke (255);
fill(255);
}
// Called 60 times per secondvoiddraw()
{
int i;
// Find the angle from x=250, y=250 to the mousefloat angle = atan2(mouseY - 250, mouseX - 250);
// Clear screen, blackbackground(0);
// "pushMatrix" saves current viewpointpushMatrix();
// Set 250,250 as the new 0,0 translate(250, 250);
// Rotate screen "angle" rotate(angle);
// Draw a triangle (the ship)triangle(20, 0, -20, -10, -20, 10);
// Bring back normal perspektivepopMatrix();
// Go through all shots (if any) and update their positionfor (i = 0; i<shots.size(); i++) {
shot s = shots.get(i);
if (s.update()) {
// Remove bullet, if outside screen
shots.remove(i);
}
}
}
// When left mouse button is pressed, create a new shotvoidmousePressed() {
if (mouseButton == LEFT) {
float angle = atan2(mouseY - 250, mouseX - 250);
shots.add(new shot(angle, 2));
}
}
// Class definition for the shotclass shot {
// A shot has x,y, and speed in x,y. All float for smooth movementfloat angle, speed;
float position;
// Constructor
shot(float _angle, float _speed) {
angle = _angle;
speed = _speed;
position = 0;
}
// Update position, return true when out of screenboolean update() {
position = position + speed;
pushMatrix();
// Set 250,250 as the new 0,0 translate(250, 250);
// Rotate screen "angle" rotate(angle);
// Draw bulletellipse (position + 20, 0, 5, 5);
// Bring back normal perspektivepopMatrix();
if (position>250) {
returntrue;
} else {
returnfalse;
}
}
}
So in previous post, we have a ship that can shoot bullets at... nothing. Not much fun as a game. If we are to try to create a simple astroids clone, then we need... astroids. In my version they will come from outside the screen at random angles and speeds, and traverse in a straight line across. For making them look better, instead of just a circle, I use an object called "PShape" (click to read about ir). This shape is created unique per astroid as I create the shape for each new astroid in the constructor for the object. (Tutorial to objects and explanation of constructor here). I decided to keep the astroid roughly circular to make collision detection easier later...
How does it look like? Watch this:
Try it your self and play with it using this code. Clicking right mouse button spawns new astroid.
ArrayList<astroid> astroids = newArrayList<astroid>();
// Settings - how many seconds between each new astroid (3 seconds = 3 * 60)int astroid_rate = 3 * 60;
int astroid_count = 0;
// Size in pixel of nominal astroidfloat ast_size = 10;
// Run oncevoidsetup () {
frameRate(60);
size(500, 500);
}
// Called 60 times per secondvoiddraw()
{
int i;
// Find the angle from x=250, y=250 to the mousefloat angle = atan2(mouseY - 250, mouseX - 250);
// 1 new astroid every 5 seconds (60 fps * 4 sec)if (astroid_count--==0) {
astroids.add(new astroid(random(0, TWO_PI), random(0.1, 2.0), random(0.5, 4), random(-0.1, 0.1),
random(-150, 150), random(-150, 150)));
// Increase rate just a little
astroid_count = astroid_rate--;
}
// Clear screen, blackbackground(0);
// Go through all astroids (if any) and update their positionfor (i = 0; i<astroids.size(); i++) {
astroid a = astroids.get(i);
if (a.update()) {
astroids.remove(i);
}
}
// "pushMatrix" saves current viewpointpushMatrix();
// Set 250,250 as the new 0,0 translate(250, 250);
// Rotate screen "angle" rotate(angle);
fill(255);
// Draw a triangle (the ship)triangle(20, 0, -20, -10, -20, 10);
// Bring back normal perspektivepopMatrix();
}
// When right mouse button is pressed, create a new astroidvoidmousePressed() {
if (mouseButton == RIGHT) {
astroids.add(new astroid(random(0, TWO_PI), random(0.1, 2.0), random(0.5, 4), random(-0.1, 0.1),
random(-80, 80), random(-80, 80)));
}
}
// Class definition for the astroidclass astroid {
// An astroid angle, speed, size, rotationfloat angle, speed, size, rotSpeed;
float position;
float rotation;
float xoff, yoff;
float x, y;
PShape s; // The PShape object - Keeps the astroid shapefloat i;
// Constructor
astroid(float _angle, float _speed, float _size, float _rotSpeed, float _xoff, float _yoff) {
angle = _angle;
speed = _speed;
size = _size;
rotSpeed = _rotSpeed;
// This will be used when later breaking down astroidsif (xoff<1000) {
// Normal - start outside screen
x = 250+500*cos(angle)+_xoff;
y = 250+500*sin(angle)+_yoff;
} else {
// Start on specific x,y
x = _xoff-2000;
y = _yoff-2000;
}
rotation = 0;
// Generate the shape of the astroid - Some variations for all, roughly sircular
s = createShape();
s.beginShape();
s.fill(255, 255, 100);
s.noStroke();
for (i=0; i<TWO_PI; i=i+PI/(random(4, 11))) {
s.vertex(random(ast_size*0.8, ast_size*1.2)*cos(i), random(ast_size*0.8, ast_size*1.2)*sin(i));
}
s.endShape(CLOSE);
}
// Update position, return true when out of screenboolean update() {
x = x - cos(angle)*speed;
y = y - sin(angle)*speed;
rotation = rotation + rotSpeed;
pushMatrix();
// Set position as the new 0,0 translate(x, y);
// Rotate screen "angle" rotate(rotation);
// Draw astroidscale(size);
shape(s, 0, 0);
// Bring back normal perspektivepopMatrix();
if (x<-300 || x>800 || y<-300 || y>800) {
returntrue;
} else {
returnfalse;
}
}
}