Skip to content

Rhythm 1

An experimental game where time flows in rhythmic bursts. Click to shoot the purple circles; don't shoot the red health pickups.

Can you get past wave 40 60?

A tiny triangle surrounded by ever-larger numbers of big circles

function setup() {
  createCanvas(400, 400);
  cursor(CROSS)
  background(255)
}

let shots = []
let enemies = []
let cooldown = 0
let shotSpeed = 10
let enemySpeed = 4
let enemySpawnTimer = 0
let enemySpawnWave = 0
let lives = 100
let rhythm = 0
let beat = 3

function draw() {
  background(20, 20, 20, 140)
  
  if (lives < 10) {
    fill(255)
    noStroke()
    textSize(50)
    textAlign(CENTER)
    text("GG\nWave: " + enemySpawnWave, 200, 200 - 50 * 0.75)
    if (cooldown < 0) {
      cursor(HAND)
      if (mouseIsPressed) {
        shots = []
        enemies = []
        enemySpawnTimer = 0
        enemySpawnWave = 0
        lives = 100
      }
    }
    if (!mouseIsPressed) {
      cooldown = -1
    }
    return
  }
    
  rhythm ++
  if (beat == 3) {
    if (rhythm > 35*2) {
      beat = 1
      rhythm = 0
      cooldown = 0
    }
  } else {
    if (rhythm > 35) {
      beat ++
      rhythm = 0
      cooldown = 0
    }
  }
  let rhythmFactor = max(map(rhythm, 0, 20, 1, 0.5), 0.4)
  if (rhythm < 5 || (beat == 3 && rhythm < 6)) {
    noFill()
    for (let i = 0; i < 40; i += 4) {
      stroke(70 - i, 70 - i, 70 - i, (beat == 3 ? 6 : 5) - rhythm)
      strokeWeight(i)
      rect(0, 0, width, height)
    }
  }
  
  if (lives < 100) {
    lives = lives + 0.01
  }
  
  fill(0, 255, 100)
  noStroke()
  translate(200, 200)
  let angle = atan2(mouseY - 200, mouseX - 200)
  scale(lives / 100)
  rotate(angle)
  triangle(12, 0, -8-rhythmFactor, 10-rhythmFactor, -8-rhythmFactor, -10+rhythmFactor)
  resetMatrix()
  
  cooldown --
  if (mouseIsPressed && cooldown < 0) {
    cooldown = 6
    shots.push({x: 200, y: 200, a: angle+random(-0.08, 0.08)})
    //shots.push({x: 200, y: 200, a: angle+PI*0.1})
    //shots.push({x: 200, y: 200, a: angle-PI*0.1})
  }
  
  enemySpawnTimer --
  if (enemies.length == 0 || enemySpawnTimer < 0) {
    enemySpawnTimer = 300
    enemySpawnWave = enemySpawnWave + 1
    for (let i = 0; i < sqrt(enemySpawnWave * 5); i ++) {
      let x = 0
      let y = 0
      let r = random(10, 50)
      if (random() < 0.1) {
        r = -r
      }
      if (random() < 0.5) {
        x = random([-200, 600])
        y = random(-100, 500)
      } else {
        x = random(-100, 500)
        y = random([-200, 600])
      }
      enemies.push({
        x: x,
        y: y,
        r: r,
      })
    }
  }
  
  
  for (let i = 0; i < shots.length; i ++) {
    let shot = shots[i]
    if (shot.x > 600 || shot.y > 600 || shot.x < -200 || shot.y < -200) {
      shots.splice(i, 1)
      i --
      continue;
    }
    shot.x += shotSpeed * cos(shot.a) * rhythmFactor
    shot.y += shotSpeed * sin(shot.a) * rhythmFactor
    //shot.a = -atan2(shot.x - 200, shot.y - 200)
    
    
    stroke(140)
    strokeWeight(4)
    strokeCap(SQUARE)
    translate(shot.x, shot.y)
    rotate(shot.a)
    line(0, 0, -shotSpeed*rhythmFactor, 0)
    resetMatrix()
  }
  
  enemiesLoop:for (let i = 0; i < enemies.length; i ++) {
    let enemy = enemies[i]
    for (let j = 0; j < shots.length; j ++) {
      let shot = shots[j]
      let dx = (shot.x - enemy.x) 
      let dy = (shot.y - enemy.y) 
      if (dx * dx + dy * dy < enemy.r * enemy.r) {
        shots.splice(j, 1)
        j --
        
        enemy.r -= 10
        if (enemy.r < 6) {
          enemies.splice(i, 1)
          i --
          continue enemiesLoop
        }
      }
    }
    let dy = enemy.y - 200
    let dx = enemy.x - 200
    if (dx * dx + dy * dy < enemy.r * enemy.r) {
      lives -= enemy.r
      if(enemy.r > 0) {
        rhythm = random(35)
      }
      enemies.splice(i, 1)
      i --
      continue enemiesLoop
      
    }
    let a = atan2(dy, dx)
    enemy.x += -enemySpeed * cos(a) * 10 / abs(enemy.r) * rhythmFactor
    enemy.y += -enemySpeed * sin(a) * 10 / abs(enemy.r) * rhythmFactor
  
    if (enemy.r > 0) {
      fill(100, 10, 255)
      noStroke()
    } else {
      fill(255, 100, 0)
      noStroke()
      textSize(25)
      textAlign(CENTER)
      text("+", enemy.x, enemy.y + 9)
      stroke(255, 100, 0)
      strokeWeight(4)
      noFill()
    }
    circle(enemy.x, enemy.y, enemy.r * 2 - rhythmFactor * 10)
  }
}

(Originally seen at https://editor.p5js.org/bojidar-bg/sketches/-oMPJ0AH5)

Experiments tagged p5 (5/85)

|← Experiments tagged game (1/11)

Experiments on this site (5/85)