Your browser does not support the canvas tag.

previous        Show / Hide Source        Download        next
///////////////////////////
//    Jumping Square     //
//  Object Oriented toy  //
// By: Nicolas Olivieri  //
///////////////////////////

// No objective, just jump around the screen
// and enjoy the pretty lights that run away from you!
//
// W = Jump
// A = Left
// D = Right

Floor floor;
Background gradBG;
Player player;
SetPlatforms platforms;
Particle p;
ParticleSystem ps;
Keyboard keyboard = new Keyboard();

void setup() {
    // Initialize screen
    size(800, 600, P2D);

    // Create the background
    gradBG = new Background();

    // Create the floor
    floor = new Floor();

    // Create the platforms
    platforms = new SetPlatforms();

    // Create the player
    player = new Player();

    // Create the particle system
    ps = new ParticleSystem();

    // Generate the spikes
    floor.generateBigSpikes();
    floor.generateSmallSpikes();

    // Generate the platforms
    platforms.generatePlatforms();

    // Generate the particles
    ps.generateParticles(player.location);
}

void draw() {
    frameRate(60);

    // Make sure particles don't go off the screen
    ps.constrainParticle(player.location);
    MPAFP();
    // Move the player
    player.runInput();
    player.move();
    player.constrainPlayer();

    gradBG.drawBG();
    ps.drawParticles();
    floor.drawFloor();
    platforms.drawPlatforms();
    player.drawPlayer();
}

// Move particles away from player
void MPAFP() {
    float playerCenterX = player.location.x + player.size / 2;
    float playerCenterY = player.location.y + player.size / 2;

    for (int i = 0; i < ps.psRandom.length; i++) {
        float distX = abs(playerCenterX - ps.psRandom[i].location.x);
        float distY = abs(playerCenterY - ps.psRandom[i].location.y);
  
        if (distX < 50 && distY < 50) {
            ps.psRandom[i].velocity.x = -player.velocity.x * 1.5;
            ps.psRandom[i].velocity.y = player.velocity.y * 1.5;
        }           
    }
}

// Needed to keep track of multiple keys being pressed at the same time
void keyPressed() {
    keyboard.onPressed(keyCode);
}

void keyReleased() {
    keyboard.onReleased(keyCode);
}
class Background {

  public void drawBG() {
    // Background
    pushMatrix();
    beginShape();
    // Top left
    fill(50);
    vertex(0, 0);
    // Top right
    fill(50);
    vertex(800, 0);
    // Bot right 
    fill(30);
    vertex(800, 600);
    // Bot left
    fill(30);
    vertex(0, 600);
    endShape();
    popMatrix();
    
    fill(0, 40);
    triangle(-100, height, 100, 150, 280, height);
    triangle(100, height, 300, 60, 450, height);
    triangle(300, height, 450, 300, 600, height);
    
  }
}
public class Floor {

  // Big Spikes
  private Spike[] bigSpikes = new Spike[20];

  // Small Spikes
  private Spike[] smallSpikes = new Spike[100];

  public void generateBigSpikes() {
    // Generates the big spikes in the array
    for (int i = 0; i < bigSpikes.length; i++) 
      bigSpikes[i] = new Spike();
    
    // Generates the big spike conditions for every spike in the array
    for (int i = 0; i < bigSpikes.length; i++) {
      bigSpikes[i].spikeX = random(0, width);
      bigSpikes[i].gradStart = color(255, 0, 0);
      bigSpikes[i].gradEnd = color(60, 0, 0);
      bigSpikes[i].spikeY = 600;
      bigSpikes[i].spikeSize = random(35, 60);
      bigSpikes[i].spikeHalfSize = bigSpikes[i].spikeSize / 2;
    }
  }

  public void generateSmallSpikes() {
    // Generates the small spikes in the array
    for (int i = 0; i < smallSpikes.length; i++) 
      smallSpikes[i] = new Spike();

    // Generates the small spike conditions for every spike in the array
    for (int i = 0; i < smallSpikes.length; i++) {
      smallSpikes[i].spikeX = random(0, width);
      smallSpikes[i].gradStart = color(20);
      smallSpikes[i].gradEnd = color(5);
      smallSpikes[i].spikeY = 600;
      smallSpikes[i].spikeSize = random(10, 25);
      smallSpikes[i].spikeHalfSize = smallSpikes[i].spikeSize / 1.5;
    }
  }

  // Draw the floor
  public void drawFloor() {
    // Loop through the array and draw the spikes randomly
    for (int i = 0; i < bigSpikes.length; i++) 
      bigSpikes[i].display();

    for (int i = 0; i < smallSpikes.length; i++) 
      smallSpikes[i].display();

    // Floor line
    pushMatrix();
    beginShape(); 
    fill(30);
    vertex(0, 535);

    fill(30);
    vertex(0, 536);

    fill(100);
    vertex(150, 536);

    fill(100);
    vertex(650, 536);

    fill(30);
    vertex(800, 536);

    fill(30);
    vertex(800, 535);

    fill(100);
    vertex(150, 535);

    fill(100);
    vertex(650, 535);  
    endShape();
    popMatrix();
  }
}
// Keyboard keyCode values (from processing wiki)
int KEY_A = 65;
int KEY_D = 68;
int KEY_W = 87;
int KEY_S = 83;

class Keyboard {
    private boolean states[] = new boolean[256];

    public Keyboard() {
        for (int i = 0; i < states.length; i++)
            states[i] = false;
    }

    public void onPressed(int _keyCode) {
        //assert(_keyCode > 0 && _keyCode < states.length);
        states[_keyCode] = true;
    }

    public boolean isKeyDown(int _keyCode) {
        //assert(_keyCode > 0 && _keyCode < states.length);
        return states[_keyCode];
    }

    public void onReleased(int _keyCode) {
        //assert(_keyCode > 0 && _keyCode < states.length);
        states[_keyCode] = false;
    }

    public boolean isKeyUp(int _keyCode) {
        //assert(_keyCode > 0 && _keyCode < states.length);
        return !states[_keyCode];
    }
}
class Particle {
    PVector location;
    PVector velocity;
    PVector acceleration;

    float particleSize;
    color particleStroke;
    color particleFill;
    float w;
    float h;

    float fadeStroke = 100;
    float fadeFill = 150;

    // Update location of the particle and fade the floor particles
    void updateParticles() {
        if (location.y <= height) {
            fadeStroke -= 10;
            fadeFill -= 5;
        }
        
        velocity.add(acceleration);
        location.add(velocity);
        velocity.limit(3.5);
    }

    public void display() {
        drawParticle(particleSize, particleStroke, particleFill, location.x, location.y, w, h);
    }

    // Draw the particle
    void drawParticle(float sW, color s, color f, float x, float y, float w, float h) {
        strokeWeight(sW);
        stroke(s);
        fill(f);
        ellipse(x, y, w, h);
    }
}
class ParticleSystem {

    // Particles that are randomly moving around in the background
    // of the scene
    Particle[] psRandom = new Particle[20];

    // Particles that are on the floor
    Particle[] psFloor = new Particle[40];

    // Particles that follow the player
    Particle[] psPlayer = new Particle[20];

    float offset;

    // Generates the particles conditions for every particle in the array
    void generateParticles(PVector playerPos) {
        // Initialize random particles
        for (int i = 0; i < psRandom.length; i++) {
            psRandom[i] = new Particle();
            psRandom[i].location = new PVector(random(100, 500), height);
            psRandom[i].particleFill = color(181, 14, 14, 50);
            psRandom[i].acceleration = new PVector(0, 0);
            psRandom[i].velocity = new PVector(0, 0);
        }

        // Initialize particles on the floor
        for (int i = 0; i < psFloor.length; i++) {
            psFloor[i] = new Particle();
            psFloor[i].location = new PVector(random(0, width), random(height, 700));
            psFloor[i].particleSize = 2;
            psFloor[i].w = 5;
            psFloor[i].h = 6;
            psFloor[i].acceleration = new PVector(0, random(-0.01, -0.02));
            psFloor[i].velocity = new PVector(0, -2);
        }

        // Initialize particles following player
        for (int i = 0; i < psPlayer.length; i++) {
            psPlayer[i] = new Particle();
            psPlayer[i].location = new PVector(random(0, 50), playerPos.y + random(0, 50));
            psPlayer[i].particleSize = 1;
            psPlayer[i].w = 5;
            psPlayer[i].h = 6;
            psPlayer[i].acceleration = new PVector(0, random(-0.1, -0.2));
            psPlayer[i].velocity = new PVector(0, -2);
        }
    }

    // Generates the particles conditions that need to
    // be in draw for every particle in the array
    void drawParticles() {
        for (int i = 0; i < psRandom.length; i++) {
            psRandom[i].acceleration = PVector.random2D();
            psRandom[i].particleSize = sin(frameCount * 0.7) * 5;
            psRandom[i].particleStroke = color(255, 0, 0, sin(frameCount * 0.2) * 40);
            psRandom[i].w = 8 + sin(frameCount * 0.06) * 3;
            psRandom[i].h = 8 + sin(frameCount * 0.06) * 3;
            psRandom[i].updateParticles();
            psRandom[i].display();
        }

        for (int i = 0; i < psFloor.length; i++) {
            psFloor[i].particleFill = color(10, psFloor[i].fadeFill);
            psFloor[i].particleStroke = color(255, 0, 0, psFloor[i].fadeStroke);
            psFloor[i].updateParticles();
            psFloor[i].display();
        }

        for (int i = 0; i < psPlayer.length; i++) {
            psPlayer[i].particleFill = color(10, psPlayer[i].fadeFill);
            psPlayer[i].particleStroke = color(180, 133, 0, psPlayer[i].fadeStroke);
            psPlayer[i].updateParticles();
            offset = psPlayer[i].location.x;
            psPlayer[i].location.x = player.location.x + offset;
            psPlayer[i].display();
            psPlayer[i].location.x = offset;
        }
    }

    void constrainParticle(PVector playerPos) {
        // Locks random prticles within the screen
        for (int i = 0; i < psRandom.length; i++) {
            if (psRandom[i].location.x >= width)
                psRandom[i].location.x = -8;

            if (psRandom[i].location.x <= -8)
                psRandom[i].location.x = width;

            if (psRandom[i].location.y >= height)
                psRandom[i].location.y = -8;

            if (psRandom[i].location.y <= -8)
                psRandom[i].location.y = height;
        }

        // Reset the floor particles when they hit the ground
        for (int i = 0; i < psFloor.length; i++) {
            if (psFloor[i].location.y <= 533 + psFloor[i].h) {
                psFloor[i].location.y = height;
                psFloor[i].fadeStroke = 100;
                psFloor[i].fadeFill = 150;
                psFloor[i].velocity.y = -2;
            }
        } 
        
        // Reset the player particles depending on their distance away from the player
        for (int i = 0; i < psPlayer.length; i++) {
            float distY = abs(playerPos.y - psPlayer[i].location.y);

            if (distY >= 100) {
                psPlayer[i].location.y = playerPos.y + random(0, 50);
                psPlayer[i].fadeStroke = 100;
                psPlayer[i].fadeFill = 150;
                psPlayer[i].velocity.y = -2;
            }
        }
    }
}
class Platform {

    PVector location,
            velocity,
            acceleration;

    float endPos; // End position of platform

    Platform() {
        velocity = new PVector(2, 2);
        acceleration = new PVector(0.06, 0.06);

    }

    void drawPlatform() {
        // Platform
        noStroke();
        pushMatrix();
        beginShape();
        // Top left
        fill(0);
        vertex(location.x, location.y); 
        // Top right
        fill(0);
        vertex(location.x + 70, location.y);
        // Bot right
        fill(180, 18, 93, 70);
        vertex(location.x + 70, location.y + 25);
        // Bot left
        fill(180, 18, 93, 70);
        vertex(location.x, location.y + 25);
        endShape();
        popMatrix();

        // Gives a stroke to the platform
        strokeWeight(1);
        stroke(255);
        noFill();
        rect(location.x - 1, location.y - 1, 72, 27);

        // Handle
        stroke(5);
        strokeWeight(3);
        line(location.x + 35, location.y + 28, location.x + 35, location.y + 45);

        // Blinking light
        fill(180, 18, 93, 70);
        stroke(30);
        strokeWeight(1);
        ellipseMode(CENTER);
        ellipse(location.x + 35, location.y + 45, sin(frameCount * 0.06) * 20, sin(frameCount * 0.06) * 20);
    }

    // Move the platform up on start up
    void movePlatform() {
        location.y -= velocity.y;
        velocity.y += acceleration.y;

        if (location.y <= endPos) {
            velocity.y = 0;
            acceleration.y = 0;
        }
    }

    // Is the player colliding with a platform
    public boolean collides() {
        if (player.location.y < location.y + 25 && player.size + player.location.y > location.y &&
            player.location.x < location.x + 70 && player.location.x + player.size > location.x) {
            return true;
        }

        return false;
    }
}
class Player {

    PVector location,
            velocity,
            acceleration; 

    float ground;

    float gravity,
          size;

    float playerSpeed,
          jumpSpeed,
          jumpGravityReduction;

    boolean jumping;

    Player() {
        size = 50;
        ground = 533;
        gravity = 4;

        playerSpeed = 2.5;
        jumpSpeed = 19;
        jumpGravityReduction = 0.25;

        jumping = false;

        location = new PVector(width / 2, ground - size);
        velocity = new PVector(0, 0);
        acceleration = new PVector(0, 0);
    }

    void runInput() {
        // Recalculate input velocity
        acceleration.x = 0;
        acceleration.y = 0;

        if (keyboard.isKeyDown(KEY_A)) {
            acceleration.x -= 8;
        } else if (keyboard.isKeyDown(KEY_D)) {
            acceleration.x += 8;
        }

        if (keyboard.isKeyDown(KEY_W) && !jumping) {
            jumping = true;
            velocity.y = -jumpSpeed;
        } 
    }

    void move() {
        // Apply gravity
        if (velocity.y < gravity) {
            float actualGravity = gravity;

            if (jumping)
                actualGravity *= jumpGravityReduction;

            velocity.y += actualGravity;

            // Clamp gravity
            if (velocity.y > gravity) 
                velocity.y = gravity;
        }

        // Check if the movement is making us collide with something
        boolean currentlyColliding = false;

        for (int i = 0; i < platforms.platforms.length; i++) {
            Platform p = platforms.platforms[i];
            boolean doesCollide = p.collides();

            if (doesCollide) {
                currentlyColliding = true;
                break;
            }
        }

        // Does moving down make us hit something
        float velY = velocity.y + acceleration.y;
        location.y += velY;
        boolean collidingBottom = false;

        for (int i = 0; i < platforms.platforms.length; i++) {
            Platform p = platforms.platforms[i];
            boolean doesCollide = p.collides();

            if (!currentlyColliding && doesCollide) {
                // Make sure the player doesn't go through the object
                collidingBottom = velY < 0;

                if (!collidingBottom) {
                    location.y = p.location.y - size - 1;
                    jumping = false; // Reset jumping because we're hitting a platform
                }
                break;
            }
        }

        float velX = velocity.x + acceleration.x;
        location.x += velX;

        for (int i = 0; i < platforms.platforms.length; i++) {
            Platform p = platforms.platforms[i];
            boolean doesCollide = p.collides();

            if (!currentlyColliding && doesCollide && !collidingBottom) {
                boolean collidingLeft = velX > 0;

                if (collidingLeft) {
                    location.x = p.location.x - size - 1;
                } else  {
                    location.x = p.location.x + 70 + 1;
                }
                break;
            }
        }
    }

    void drawPlayer() {
        stroke(255, 132, 0);
        strokeWeight(1);
        fill(50);
        rect(location.x, location.y, size, size);
        noStroke();
        fill(255, 132, 0, 100);
        rect(location.x + 10, location.y + 10, size - 20, size - 20);
        fill(50);
        rect(location.x + 15, location.y + 15, size - 30, size - 30);
    }

    // Constrain the player to the screen
    void constrainPlayer() {
        if (location.x >= width) {
            location.x = 0 - size + 1;
        }

        if (location.x <= 0 - size) {
            location.x = width;
        }

        if (location.y + (velocity.y + acceleration.y) > ground - size) {
            location.y = ground - size;
            jumping = false;
        }
    }
}
class SetPlatforms {

  Platform[] platforms = new Platform[7];

  public void generatePlatforms() {    
    // Sets random positions for the platforms
    for (int i = 0; i < platforms.length; i++) {
      platforms[i] = new Platform();
      platforms[i].endPos = random(60, 450);
      platforms[i].location = new PVector(random(10, width - 80), height + 1);
    }

    // Ensure that each platform is spaced out from all other platforms
    for (int i = 0; i < platforms.length; i++) {
      boolean finished = false;
      int attempts = 0; // safety to make sure the loop doesn't run infinitely

      while (!finished) {
        attempts++;
        finished = true;

        for (int j = 0; j < platforms.length; j++) {
          // don't try and space the platform out from itself
          if (j == i) 
            continue;

          float distX = abs(platforms[j].location.x - platforms[i].location.x);
          float distY = abs(platforms[j].location.y - platforms[i].location.y);

          if (distY < 45 && distX < 75) {
            // platform is too close; re-generate, try again.
            finished = false;
            platforms[i].location = new PVector(random(10, width - 80), height + 1);

            // break out of the for loop and start checking over again
            break;
          }
        }
        // Perhaps there's a better way of doing this?
        // Preventing an infinite loop
        if (attempts > 500000) {
          //println("Quite unlucky!");
          break;
        }
      }
    }
  }

  void drawPlatforms() {
    // Draw and update the platforms in the array
    for (int i = 0; i < platforms.length; i++) {
      platforms[i].movePlatform();
      platforms[i].drawPlatform();
    }
  }
}
public class Spike {
  public color gradStart, 
    gradEnd;

  public float spikeX, 
    spikeY, 
    spikeSize, 
    spikeHalfSize;

  public void display() {
    drawGradientSpike(spikeX, spikeY, spikeX + (spikeHalfSize / 2), spikeY - spikeSize, 
      spikeX + spikeHalfSize, spikeY, gradStart, gradEnd);
  }

  // Draws the gradient spike
  void drawGradientSpike(float x1, float y1, float x2, float y2, float x3, float y3, color color1, color color2) {
    noStroke();
    pushMatrix();
    beginShape();
    // top of triangle
    fill(color1);
    vertex(x2, y2);
    // bottom left
    fill(color2);
    vertex(x1, y1);
    // bottom right
    vertex(x3, y3);
    endShape();
    popMatrix();
  }
}