chromsan-clock

Prevent time from passing by knocking away the incoming seconds. The more you prevent time from passing, the faster the seconds will come. If one of the seconds makes it past you, time is reset and can even go into the future.

(use side to side arrow keys to control the paddle)

I liked the idea of making some sort of a game for this project, as time is usually quite a large part of games. I also wanted to make a clock that was not quite accurate, so that you could have some control over the clock's speed. I took inspiration from Pong, so that the player can prevent time from being added to the clock. To give a bit of a challenge, I made time speed up as the player is more successful. This way, if they miss, time is both reset and a couple seconds may be added to the clock. The main issue with using p5.js for this project is that the animation stops drawing when you leave a tab, so the time that is drawn will be different from the real time, and the scoring system gets messed up. I'd like to find a way around this.

(update on 9/25- I've found a kinda hacky way around this to fix the score and time, embedded sketch has been updated)

The green theme denotes AM and red denotes PM.

Original ideas:

Code also on GitHub.

// real time (not based on game)
let realMS, realS, realM, realH; 
// game time (based on game performance)
let gameS, gameM, gameH; 
// previous logged times 
let p_realS, p_realM, p_realH; 
// how often the times appear [0-999]
const timeFreqInit = 999;
let timeFreq = timeFreqInit, timeFreqInc = 20;
// assorted global vars 
const deflectorSpeed = 15, timeSpeed = -10;
const dispTimeY = 80;
 
let deflector, time = 0;
let times = [], score = 1, dispScore = 0, highScore = 0;
let font, fSize = 60, fillCol, backCol;
let millisRolloverTime = 0; 
 
function preload() {
  font = loadFont('digital-7.ttf');
}
 
function setup() {
  createCanvas(600, 750, P2D);
  textFont(font);
  textSize(fSize);
  updateRealTime();
  // to start, game time is the same as real time
  gameS = realS;
  gameM = realM;
  gameH = realH;
  // if it's AM, color green, otherwise red 
  setAmPmCol();
  // paddle 
  deflector = new Deflector(10, 100, width/2, height/4);
}
 
function draw(){
  setAmPmCol();
  fill(fillCol);
  textSize(fSize);
  background(backCol);
  updateRealTime();
  drawGameTime();
  // every time theres a new second...
  if (p_realS != realS){
    millisRolloverTime = millis();
    p_realS = realS;
  }
  realMS = floor(millis() - millisRolloverTime);
  // add a new time based off freq
  if (realMS % timeFreq == 0) {
    time++;
    let expanded = secondsToMin(time);
    let temp = {s: expanded.s, m: expanded.m, h: realH, disp: expanded.s};
    times.push(new Time(temp, random(30, width - 30), height));
  }
 
  for (let i in times) {
    if (times[i].offscreen()){
      // remove offscreen times
      times.splice(i, 1);
    }
    if (times[i].intersects(deflector)){
      // remove times after hitting paddle
      times[i].bounceMove(random(-10,10), random(6, 12));
      if (score > highScore) { highScore = score }
    } 
    if (times[i].escaped() && !times[i].t.used){
 
      // if the time made it past the paddle
      times[i].t.used = true;
 
      if (-score <= 0){ let res = addToTime(gameS, gameM, gameH, times[i].t.s, times[i].t.m); gameS = res.s; gameM = res.m; gameH = res.h; } // measure the diff between real and game time to get score let d1 = new Date(2018, 9, 20, gameH, gameM, gameS); let d2 = new Date(2018, 9, 20, realH, realM, realS); let d = d2.getTime() - d1.getTime() if (realS - gameS > 0) {score = d /1000;}
      else {score = realS - gameS}
      time = 0;
      timeFreq = timeFreqInit;
    }
    if (times[i].goingUp) {
      // move the times up by speed
      times[i].move(timeSpeed);
    }
    times[i].update();
    times[i].draw();
  }
  deflector.update();
  deflector.draw();
  drawScore();
}
 
function keyReleased() { 
  if (keyIsDown(RIGHT_ARROW)) { deflector.move(deflectorSpeed) }
  else if (keyIsDown(LEFT_ARROW)) { deflector.move(-deflectorSpeed) }
  else { deflector.move(0) }
}
 
function keyPressed() {
  if (keyCode == LEFT_ARROW){ deflector.move(-deflectorSpeed)} 
  if (keyCode == RIGHT_ARROW){ deflector.move(deflectorSpeed)} 
}
 
function updateRealTime() {
  realS = second();
  realM = minute();
  realH = hour() % 12;
}
 
function drawGameTime() {
  textAlign(CENTER);
  text(gameH, width/4, dispTimeY);
  text(":", width/8 * 3, dispTimeY)
  text(gameM, width/4 * 2, dispTimeY);
  text(":", width/8 * 5, dispTimeY)
  text(gameS, width/4 * 3, dispTimeY);
}
 
function drawScore() {
  textSize(25);
  textAlign(CENTER);
  let res = secondsToMin(score);
  let disp, pos = "";
  if (score < 0) {pos = "+"} // check if we need to display minutes if (res.m > 0) {disp = pos + (-1 * res.m) + ":" + res.s}
  else {disp = pos + (-1 * res.s)}
  text(disp, width - 80, 80);
  textSize(12);
  //text("s", width - 150, height- 20);
  textSize(25);
  text("Max:  " + highScore, width - 75, height - 20);
  textSize(12);
  text("s", width - 30, height- 20);
}
 
function secondsToMin(sec) {
  let secRl = sec % 60;
  let minRl = Math.floor(sec / 60);
  return {s: secRl, m: minRl};
}
 
function addToTime(pS, pM, pH, s, m) {\
  // add seconds and minutes new seconds and minutes
  let fixedS = pS + s, fixedM = pM + m, fixedH = pH;
  // case check for if any go over 60 
  if (fixedS >= 60) { 
    let res = secondsToMin(fixedS);
    fixedS = res.s;
    fixedM += res.m;
  }
  if (fixedM >= 60){
    // we can use the same function for mins + hrs
    let res = secondsToMin(fixedM);
    fixedM = res.s;
    fixedH += res.m;
  }
  if (fixedH > 12){
    // kinda hacky but really unlikely to happen
    fixedH = hour() % 12;
  }
  return {h: fixedH, m: fixedM, s: fixedS}
}
 
function setAmPmCol() {
  if (hour() > 12) {fillCol = color(205, 0, 0); backCol = color(20, 0, 0)}
  else { fillCol = color(0, 205, 0); backCol = color(0, 20, 0) }
}
 
// the 'paddle' that used to hit the numbers
class Deflector {
  constructor(h_, w_, x_, y_){
    this.height = h_;
    this.width = w_;
    this.x = x_;
    this.y = y_;
    this.deltaX = 0;
  }
  update() {
    this.x += this.deltaX;
    this.x = constrain(this.x, this.width/2, width - this.width/2);
  }
  move(steps) { this.deltaX = steps }
 
  draw() {
    textAlign(CENTER);
    textSize(70);
    text("___", this.x, this.y);
    textSize(fSize); 
  }
}
 
// the numbers that appear
class Time {
  constructor(t_, x_, y_) {
    this.t = t_;
    this.x = x_;
    this.y = y_;
    this.deltaY = 0;
    this.deltaX = 0;
    this.goingUp = true;
    this.col = color(fillCol.levels[0], fillCol.levels[1], fillCol.levels[2]);
    this.used = false;
  }
  update() {
    this.y += this.deltaY;
    this.x += this.deltaX;
  }
  move(steps) { 
     this.deltaY = steps;
  }
  bounceMove(stepX, stepY){
    this.deltaX = stepX;
    this.deltaY = stepY;
  }
  escaped(){
    if (this.y < height/4) { return true }
    else {return false}
  }
 
  intersects(deflector) { 
    // from https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
    if (deflector.x < this.x + 50 && deflector.x + deflector.width > this.x + 40 &&
        deflector.y < this.y + 10 && deflector.y + deflector.height > this.y - 40) {
      if (this.goingUp) {
        // if the time hits the deflector...
        score++; 
        if (score > 0 && timeFreq >= 750){
          timeFreq -= timeFreqInc;
        }
 
      }
      this.goingUp = false;
      this.t.disp = '#'
      return true;
    }
    return false;
  }
 
  offscreen() {
    if (this.y < -40 || this.y > height + 40) return true;
    else return false;
  }
 
  draw() {
    textAlign(CENTER);
    if (this.escaped()){
      let a = this.col.levels[3];
      this.col.setAlpha(a - 15);
      fill(this.col);
      text(this.t.disp, this.x, this.y);
    } else {
      fill(fillCol);
      text(this.t.disp, this.x, this.y);
 
    }
 
  }
}