First little game, feedback welcome

Hi,
Here is a first small game I coded, just to start learning processing. I’m actually looking for feedback now on the code and on how to improve my coding style. It’s now been done with a ‘let’s-see-if-we-can-get-it-working-attitude’.

I’m learning to work with functions now and I’m wondering where and when to use them exactly.

int lives = 3;
int points = 0;
int highscore = 0;

//service
int service = 0;

boolean bService = true;

//racket
float rectX;
int rectY;
int rectWidth;
int rectHeight;

color rectColor1 = color(0);

//racket movement
float moveHorizontal;

//ball
float ellipseX;
float ellipseY; 
int ellipseH;
int ellipseB;

color ellipseColor1 = color(234, 255, 10);

//ballposition
float ballStartPosX;
float ballStartPosY;
float ballStartPosXR;

//ballmovement
float ballMoveHorizontal = 0;
float ballMoveVertical = 0;

//ballspeed
float ballSpeedLow;
float ballSpeedHigh;
float ballSpeedMid;

//Airplane
float planeX;
float planeY;

boolean bAirplane = false;
int flyingDutchman = 20;

//Pause
boolean pauSe = false;

//Highscore
boolean highScore = false;

//Text
String gameOver = "Game Over!";
String score = "Score: ";

//game over
//https://forum.processing.org/two/discussion/8084/how-do-i-display-a-message-for-a-few-seconds
boolean bDisplayMessage1 = false;
boolean bDisplayMessage2 = false;

PFont SerifBengali;

//Images
PImage mcenroe;
PImage borg;

//Timers
int startTime;
final int DISPLAY_DURATION = 6000; 
int beginTimer;
int airPlaneTime;



void setup() {

  //size(1200, 700);
  noCursor();
  fullScreen();

  //Font  
  SerifBengali = createFont("lmsansdemicond10-regular", 32);
  textFont(SerifBengali);

  //Images
  mcenroe = loadImage("mcenroe.jpeg");
  borg = loadImage("borg.jpeg");

  //Ballposition
  ballStartPosX = width/4;
  ballStartPosXR = width/1.33;
  ballStartPosY = height/9; 
  ellipseX = ballStartPosX;
  ellipseY = ballStartPosY;

  //Racket  
  rectX = width/1.67;
  rectY = height - (height/40);
  rectWidth = width/6;
  rectHeight = rectWidth/4;

  moveHorizontal = height/100;

  //Ball
  ellipseH = height/12;
  ellipseB = height/12;

  //BallSpeed
  ballSpeedLow = height/82;
  ballSpeedHigh = height/55;
  ballSpeedMid = height/70; 

  //Plane
  planeX = width/32;
  planeY = height/18;

  airPlaneTime = (int) random(50, 60);
}





void draw() {
  background(55, 200, 5);

  //FIELD LINES

  //blendMode(ADD);
  stroke(255);
  strokeWeight(14);
  line(width/2, 0, width/2, height); 
  //rect(width/2, 0, width/100, height); //white lines
  fill(0);
  stroke(0);
  strokeWeight(2);
  for (int j = 5; j <= 15; j += 5) {
    line(0, height/3 + j, width, height/3 + j); //net
  }

  //BALL
  
  fill(#EFF007);
  strokeWeight(1);
  ellipse(ellipseX, ellipseY, ellipseH, ellipseB); //draw the ball

  //RACKET

  fill(rectColor1);
  //strokeWeight(12);
  strokeJoin(ROUND);
  rect(rectX, rectY, rectWidth, rectHeight); //draw rectangle to catch ball


  //START GAME

  if (keyPressed == true) { //press space to (re)start the game
    if ((key == ' ')  && (ballMoveVertical == 0) && (lives > 0 )) {
      loop();
      pauSe = false;
      //ballMoveVertical = random(ballSpeedLow, ballSpeedMid);
      if (lives % 2 != 0) { //opslagveld wisselen
        ballMoveHori(ballSpeedMid, ballSpeedMid );
        ballMoveVerti(ballSpeedLow, ballSpeedMid);
      } else {
        ballMoveHori(-ballSpeedMid, -ballSpeedMid );
        ballMoveVerti(ballSpeedLow, ballSpeedMid);
      }
    } else if ((keyCode == RIGHT) && (key == SHIFT)) { //sliding
      rectX += (moveHorizontal*2);
    } else if ((keyCode == LEFT) && (key == SHIFT)) {
      rectX -= (moveHorizontal*2);
    } else if (keyCode == RIGHT) { //move right
      rectX += moveHorizontal;
    } else if (keyCode == LEFT) { //move left
      rectX -= moveHorizontal;
    }
  }

  //BALL MOVES HORIZONTAL

  if ((ellipseX + (0.5*ellipseB)) > width ) {     //make ball bounce from sidewalls
    ballMoveHorizontal = random(-ballSpeedLow, -ballSpeedMid);
  } else if ((ellipseX - (0.5*ellipseB) < 0)) {
    ballMoveHorizontal = random(ballSpeedLow, ballSpeedMid);
  }
  ellipseX += ballMoveHorizontal;

  //BALL MOVES VERTICAL

  if (ellipseY < 0) {   //upper boundary
    ballMoveVertical = random(ballSpeedLow, ballSpeedHigh);
  }
  ellipseY += ballMoveVertical;


  //RACKET MOVES HORIZONTAL with BOUNDARIES

  if (rectX > (width-(0.5*rectWidth))) {   //add boundaries for rectangle
    rectX = (width-(0.5*rectWidth));
  } else if (rectX < -(0.5*rectWidth)) {
    rectX = -(0.5*rectWidth);
  }

  //DISTANCE CHECK

  float dist2 = dist(rectX + (0.7 * rectWidth), rectY+rectHeight, ellipseX, ellipseY);
  float dist1 = dist(rectX + (0.3 * rectWidth), rectY+rectHeight, ellipseX, ellipseY);
  if (dist2 < 0.4*rectWidth || dist1 <  0.4*rectWidth) {
    rectColor1 = color(255);  //change rect color to white
    ellipseY -=(0.6*ellipseH);
    ballMoveHorizontal = random(-ballSpeedHigh, ballSpeedHigh);
    ballMoveVertical = random(-ballSpeedLow, -ballSpeedHigh);
    points++;
    println("points: "); 
    println(points);
  } else {
    rectColor1 = color(0);  //let white color flash back to black
  }


  //BALL TOUCHES GROUND!

  if ((ellipseY + (0.5*ellipseH)) > height) { //ball touches ground!
    lives--;    //one life gone
    println(lives);
    if ((lives == 0) && (highScore == false)) { //if lives = 0 -> or highscore, or failure.
      bDisplayMessage1 = true;
      startTime = millis();
      println(lives);
    } else if ((lives == 0) && (highScore == true)) {  //highscore
      bDisplayMessage2 = true;
      startTime = millis();
      println(lives);
    } else {
      ellipseY = ballStartPosY;
      if (lives % 2 != 0) {
        ellipseX = ballStartPosX;
      } else {
        ellipseX = ballStartPosXR;
      }
      ballMoveVertical = 0;
      ballMoveHorizontal = 0;
    }
  }

  //HIGHSCORE CHECK

  blendMode(BLEND);
  stroke(0);
  strokeWeight(2);
  textSize(20);
  text(points, 80, 80);
  if (points > highscore) { //check for highscore
    highScore = true;
    highscore = points;
  }


  //DISPLAY GAME OVER WITHOUT HIGHSCORE

  if (bDisplayMessage1 == true) { 
    println(points);
    fill(0);
    textSize(32);
    text("Game over.", 150, height / 2);
    text("Not the next Björn Borg... ", 150, height / 2+60); 
    tint(110, 164, 32);
    //filter(THRESHOLD, 0.5);
    image(mcenroe, 150, height / 2+100, 400, 260);
    if (millis() - startTime > DISPLAY_DURATION) {
      bDisplayMessage1 = false;
      bDisplayMessage2 = false;
      ellipseY = ballStartPosY;
      ellipseX = ballStartPosX;
      ballMoveVertical = 0;
      ballMoveHorizontal = 0;
      points = 0;
      lives = 3;
      highScore = false;
    } else {
      bDisplayMessage1 = true;
    }
  }

  //DISPLAY GAME OVER WITH HIGHSCORE

  if (bDisplayMessage2 == true) {
    println(points);
    fill(0);
    textSize(32);
    text("Game Over...", 150, height / 2);
    text("The good news: new highscore!", 150, height / 2+60);
    text(highscore, 150, height/2 + 120); 
    tint(110, 164, 32);
    image(borg, 150, height / 2+150, 200, 260);
    if (millis() - startTime > DISPLAY_DURATION) {
      bDisplayMessage1 = false;
      bDisplayMessage2 = false;
      ellipseY = ballStartPosY;
      ellipseX = ballStartPosX;
      ballMoveVertical = 0;
      ballMoveHorizontal = 0;
      points  = 0;
      lives = 3;
      highScore = false;
    } else {
      bDisplayMessage2 = true;
    }
  }

  //RANDOM AIRPLANE DISTURB FUN

  beginTimer = second();

  if (beginTimer == airPlaneTime) {
    bAirplane = true;
    flyingDutchman = 1;
  }

  if (flyingDutchman > width-planeX) {
    bAirplane = false;
  }

  if ((bAirplane == true) &&  ((bDisplayMessage1 == false) && (bDisplayMessage2 == false))) {
    translate(width-(4*planeX) - flyingDutchman, 0+flyingDutchman);
    airPlane(planeX, planeY); //airplane
    flyingDutchman += 2;
  }
}


//function PAUSE

void keyPressed() {
  if (((key == 'p') && (ballMoveVertical != 0)) && ((bDisplayMessage1 == false) || (bDisplayMessage2 == false))) {
    if (pauSe == false) {
      pauSe = true;
      noLoop();
    } else {
      pauSe = false;
      loop();
    }
  }
}


//function draw AIRPLANE

void airPlane(float planeX, float planeY) {

  beginShape(QUADS);

  fill(255);
  //cclock, 1ste quad bottom
  vertex(planeX*2, planeY*2.6);
  vertex(planeX*4, planeY*3);
  vertex(planeX*4.6, planeY*2.6);
  vertex(planeX*4, planeY*1.8);
  //clockwise, 2nd quad top
  vertex(planeX*2, planeY*2.6);
  vertex(planeX*2.2, planeY*1.8);
  vertex(planeX*2.8, planeY*1.4);
  vertex(planeX*4, planeY*1.8);
  //println(planeY*1.8);

  //endShape();
  endShape();

  beginShape(TRIANGLES);
  //cclockwise, tail
  vertex(planeX*4, planeY*1.8);
  vertex(planeX*4, planeY);
  vertex(planeX*3.2, planeY*1.9);

  //middlepiece, cclockwise, start tail
  vertex(planeX*4, planeY*1.8);
  vertex(planeX*2.4, planeY*2);
  vertex(planeX*1.4, planeY*2.9);

  //lowest point nose, clockwise, start 1ste vertex 1ste quad
  vertex(planeX*2.06, planeY*2.64);
  vertex(planeX*2.44, planeY*2.68);
  vertex(planeX*1.4, planeY*2.9);

  //top small triangle, clockwise
  fill(#431639);
  vertex(planeX*2.2, planeY*1.8);
  vertex(planeX*2.7, planeY*1.34);
  vertex(planeX*2.8, planeY*1.4);

  //bottom airplane, cclockwise
  fill(#430E0E);
  vertex(planeX*4, planeY*3);
  vertex(planeX*4.6, planeY*2.6);
  vertex(planeX*4.58, planeY*2.5);

  endShape();
}

//function move ball

void ballMoveHori(float ballTempo, float ballTempoHigh) {

  ballMoveHorizontal = random(ballTempo, ballTempoHigh);
}

void ballMoveVerti(float ballTempoLow, float ballTempoHigh) {
  ballMoveVertical = random(ballTempoLow, ballTempoHigh);
}

In my opinion your code would really be improved by using object- oriented program. You can create separate classes to store that long list of variables in the beginning of your program. For example you can create a ‘Ball’ , ‘Racket’ and ‘Airplane’ class. Check out processing’s tutorial on OOP

3 Likes

Thx for the suggestion. I’ve started from scratch more or less with classes, see below.

Now I wonder, when do I write functions and when do I write a dedicated class for something? I’ve now written a class for ‘score’ for instance and I could write a class to display lines on the playingfield for instance, but do I write just a function or a class for it?

edit: update code

Ball ball0;
//Ball ball1;

Paddle paddle0;

//Score score0;

void setup() {
  size(1000, 800);
  noCursor();
  //fullScreen();

  ball0 = new Ball(color(100, 0, 100), width/2, height/10, 30, 0, 0);
  //ball1 = new Ball(color(random(255), random(255), random(255)), 10, 50, 20, random(1, 2), random(1, 2));
  paddle0 = new Paddle();
  //score0 = new Score(0);
}
//end setup

void draw() {
  background(0);
  //fill(0, 200);
  //rect(0, 0, width, height);
  field();
  ball0.move();
  ball0.display();
  paddle0.display();

  if (keyPressed) {
    if ((key == 'a') || (key == 'd')) {
      paddle0.move();
    } else if (key == ' ') {
      ball0.play();
    }
  }

  boolean collision = hitPaddle(paddle0, ball0);
  if (collision == true) {
    ball0.hit();
    ball0.points();
    //score0.points();
  }
}

//end draw
boolean hitPaddle(Paddle p, Ball b) {
  float  dist2 = dist(p.x + (0.7 * p.paddleW), p.y+p.paddleH, b.x, b.y);
  float dist1 = dist(p.x + (0.3 * p.paddleW), p.y+p.paddleH, b.x, b.y);
  if (dist2 < 0.3*p.paddleW || dist1 <  0.3*p.paddleW) {
    return true;
  } else {
    return false;
  }
}

void field() {
  stroke(255);
  //strokeWeight(4);
  line((width/2)-5, 0, (width/2)-10, height);
  line((width/2)+5, 0, (width/2)+10, height);
}

class Ball {
  float x, y, diameter, xspeed, yspeed;
  color ballColor;
  int points = 0;
  int lives = 3;
  float ballSpeedLow = height/82;
  float ballSpeedMid = height/55;
  float ballSpeedHigh = height/70;


  Ball(color tempBallcolor, float tempX, float tempY, float tempDiameter, float tempXspeed, float tempYspeed) {
    ballColor = tempBallcolor;
    x = tempX;
    y = tempY;
    diameter = tempDiameter;
    xspeed = tempXspeed;
    yspeed = tempYspeed;
  }

  void move() {
    y += yspeed;
    x += xspeed;

    //xboundary
    if (x > width) {
      xspeed = random(-ballSpeedLow, -ballSpeedHigh);
    }
    if (x <= 0) {
      xspeed = random(ballSpeedLow, ballSpeedHigh);
    }

    //yboundary
    if (y <= 0) {
      yspeed = random(ballSpeedLow, ballSpeedHigh);
    }

    if (y >= height) {
      y = height/10;
      x = (width/2);
      //yspeed = random(ballSpeedLow, ballSpeedHigh);
      yspeed = 0;
      xspeed = 0; 
      lives--;
      println("lives:");
      println(lives);
      if (lives == 0) {
        lives();
      }
    }
  }

  void lives() {
    println("gameover");
    println("points:");
    println(points);
    x = (width/2);
    xspeed = 0;
    yspeed = 0;
  }

  void points() {
    points++;
    println("points:");
    println(points);
  }


  void play() {
    //if ((keyPressed) && (key == ' ') && (xspeed == 0) && (yspeed ==0)) {
    if ((key == ' ') && (xspeed == 0) && (yspeed ==0)) {
      xspeed = random(-ballSpeedHigh, ballSpeedHigh);
      yspeed = random(ballSpeedLow, ballSpeedHigh);
    }
  }

  void hit() {
    y -= 5;
    yspeed = random(-ballSpeedLow, -ballSpeedHigh);
    xspeed = random(-ballSpeedHigh, ballSpeedHigh);
  }

  void display() {
    noStroke();
    fill(ballColor);
    ellipse(x, y, diameter, diameter);
  }
}

class Paddle {
  //float x, y, paddleW, paddleH;
  float  x = width/1.67;
  float  y = height - (height/40);
  float paddleW = width/8;
  float paddleH = paddleW/4;
  float moveHori = height/100;

  void move() {
    //if (keyPressed) {
    if (key == 'a') {
      x -= moveHori;
    }
    if (key == 'd') {
      x += moveHori;
    }
    if (x + (paddleW/2) <= 0) {
      x = -(paddleW/2);
    }
    if (x + (paddleW/2) >= width) {
      x = (width -  (paddleW/2));
    }
    //{
  }

  void display() {
    fill(255);
    rect(x, y, paddleW, paddleH);
  }
}

//class Score {
//  int points;

//  Score(int tempPoints) {
//    points = tempPoints;
//  }

//  void points() {
//    points++;
//    println(points);
//  }
//}

I think that a class is still just a data type. It’s useful to store complex objects that would need multiple primitive variables like the Ball class, but a little bulky for something like score which is just a number. You don’t really even have to write a function for score- you’ve simplified it to the point where you can just write score++

In general this is really about personal preference and style; However, I believe you see the simplification objects and functions provide to your code and the power they give to tackle complex problems

I’m able to get most of the code in classes. But what to do with functions which uses objects from multiple classes?

As you can see, most functions I’ve in the main (?) program, uses two different objects from different classes. Is there a way to make this code cleaner without all the functions below draw()?

Ball ball0;
//Ball ball1;

Paddle paddle0;
Text text;
Score score0;
Field field;

int endTime = 5000;
int startTime = 0;
int highScore = 0;

boolean clock = false;

//rain
ArrayList<Rain> rain; 
float rainNr;
float rainYspeed = random(6, 10);


void setup() {
  size(1000, 800, P2D);
  noCursor();
  //fullScreen();


  ball0 = new Ball(color(100, 0, 100), width/2, height/10, 40, 0, 0);
  //ball1 = new Ball(color(random(255), random(255), random(255)), 10, 50, 20, random(1, 2), random(1, 2));
  paddle0 = new Paddle();
  text = new Text();
  score0 = new Score();
  field = new Field();

  //rain
  rain = new ArrayList<Rain>();
}


void draw() {
  background(0);
  //fill(0, 200);
  //rect(0, 0, width, height);
  field.lines();

  ball0.move();
  ball0.display();
  ball0.lives();
  paddle0.display();
  rain();
  movePaddle();
  collision();
  gameover();
}



//functions

boolean hitPaddle(Paddle p, Ball b) {
  float  dist2 = dist(p.x + (0.7 * p.paddleW), p.y+p.paddleH, b.x, b.y);
  float dist1 = dist(p.x + (0.3 * p.paddleW), p.y+p.paddleH, b.x, b.y);
  if (dist2 < 0.28*p.paddleW || dist1 <  0.28*p.paddleW) {
    return true;
  } else {
    return false;
  }
}

void movePaddle() {
  if (keyPressed && clock == false) {
    if ((key == 'a') || (key == 'd')) {
      paddle0.move();
    } else if (key == ' ') {
      ball0.play();
    }
  }
}

void rain() {
  rainNr = int(random(100));
  if (rainNr < 100) {
    float rainX = random(width);
    for (int i = 0; i < 10; i++) {
      rain.add(new Rain(rainX, -30, rainX, 0, rainYspeed));
    }
  }
  //rain.add(new Rain(x, y1, x, y2, yspeed));


  for (int i = rain.size() -1; i >= 0; i--) {
    Rain rainy = rain.get(i);
    rainy.display();
    rainy.move();
    //rainy.boundary();
    if (rainy.boundary()) {
      rain.remove(i);
      //println(rain.get(i));
    }
  }
}


void collision() {
  boolean boom = hitPaddle(paddle0, ball0);
  if (boom) {
    ball0.hit();
    ball0.points();
    //score0.points();
  }
}

void gameover() {
  boolean dood = ball0.lives();
  if (dood && clock == false) {
    clock = true;
    startTime = millis();
  } else if (dood && clock == true) {
    if (millis() - startTime < endTime) {
      boolean hogescore = score0.highscore(ball0.score(), highScore);
      if (hogescore) {
        text.highscore(ball0.score());
        text.gameOver();
      } else {
        text.gameOver();
      }
    } else {
      score0.high_update(ball0.score());
      clock = false;
      ball0.start();
    }
  }
}


Ah paddle and ball games. Love them! :slight_smile:

What you could do is have what is called an orchestration class which holds references to all of your game’s actors and objects (ball, paddle, text, field etc.).

For example, Game myGame = new Game(...);

This class would then be utilised to invoke different actions in the game to abstract away from your “main” script.

So say you wanted to move the paddle during the draw method - You could call myGame.movePaddle() and inside of the Game class’ movePaddle method you could use your code with this.ball and this.paddle.

This would move the functions and global variables to the Game class, giving responsibility of your game to the Game class and abstracting away from the “main” script.

You would need at least one global variable though to store the Game instance (unless someone has some clever way to get around that).

1 Like