INSTRUCTIONS: Blocks will begin to appear on the screen and approach you! When blocks turn yellow, type the letter displayed on the block to earn a point. You have 5 lives.
// Nawon Choi
// Section C
// nawonc@andrew.cmu.edu
// Final Project
var numOfStars = 700;
var starSize = 1.2;
var sx = [];
var sy = [];
var sz = [];
var newBlockChance = 100;
var blocks = [];
var blockSize = 50;
var speed = 5;
var lastBlk = 0;
var alphabet = ["a", "b", "c", "d", "e", "f", "g",
"h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u",
"v", "w", "x", "y", "z"];
var txt;
var ripeBlocks = [];
var level = 1;
var points = 0;
var showPoints;
var lives = 5;
var heart;
var gameOver;
var resetTxt;
var yourScore;
var lvl;
function preload() {
heart = loadImage("https://i.imgur.com/YvJC0vj.png");
}
function setup() {
createCanvas(480, 500, WEBGL);
// generate a bunch of randomly placed stars
for (var i = 0; i < numOfStars; i++) {
sx.push(random(-width, width));
sy.push(random(-height, height));
sz.push(random(-height, height));
}
// used for drawing letter on block
txt = createGraphics(200, 200);
txt.textSize(75);
// show points/level at the corner
showPoints = createGraphics(width, 100);
showPoints.textSize(60);
showPoints.fill(255);
showPoints.textStyle(BOLD);
lvl = createGraphics(width, 100);
lvl.textSize(50);
lvl.fill(255);
// game over screen text
gameOver = createGraphics(width + 150, 100);
gameOver.textSize(100);
gameOver.fill(255);
gameOver.textStyle(BOLD);
resetTxt = createGraphics(width + 150, 100);
resetTxt.textSize(40);
resetTxt.fill(255);
yourScore = createGraphics(width + 150, 100);
yourScore.textSize(40);
yourScore.fill(255);
}
function draw() {
background("#1c2736");
drawStars();
if (lives > 0) {
// draw hearts to represent lives
for (var i = 0; i < lives; i++) {
push();
translate(width / 2 - (30 * (i + 1)), -height / 2 + 25);
texture(heart);
plane(30, 30);
pop();
}
// show points/level at the top corner
push();
showPoints.background("#1c2736");
showPoints.text("SCORE = " + points, 10, 60);
texture(showPoints);
translate(-width / 2 + 85, -height / 2 + 25);
plane(150, 30);
lvl.background("#1c2736");
lvl.text("LEVEL = " + level, 10, 60);
texture(lvl);
translate(0, 25);
plane(150, 30);
pop();
// long road
push();
fill("#104670");
translate(0, 30, -600);
rotateX(blockSize);
box(width, 10, 8000);
pop();
// highlight zone
push();
fill("#497291");
rotateX(blockSize);
translate(0, 150);
box(400, 10, 330);
pop();
// increase level based on points
var determineLvl = floor(points / 10) + 1;
if (determineLvl > 3) {
// only up to 3 levels
level = 3;
} else {
level = determineLvl;
}
if (level < 3) {
// make sure blocks don't overlap
if (frameCount > (lastBlk + 30)) {
var newBlock = floor(random(newBlockChance));
if (newBlock == 1) {
lastBlk = frameCount;
blocks.push(makeNewBlock());
}
}
} else {
// blocks can overlap
var newBlock = floor(random(newBlockChance));
if (newBlock == 1) {
lastBlk = frameCount;
blocks.push(makeNewBlock());
}
}
// draw blocks
for (var i = 0; i < blocks.length; i++) {
if (blocks[i].z < 330) {
blocks[i].draw();
if (blocks[i].ripe()) {
ripeBlocks.push(blocks[i]);
}
} else {
// remove blocks that have gone off the page
if (blocks[i].scored == 0) {
lives--;
}
blocks.shift();
}
}
} else {
// game over screen
push();
translate(0, -25);
gameOver.text("GAME OVER", 0, 80);
texture(gameOver);
plane(300, 50);
resetTxt.text("Press 'R' to restart", 0, 100);
translate(70, 40);
texture(resetTxt);
plane(300, 50);
yourScore.text("Your score = " + points, 0, 100);
translate(0, 50);
texture(yourScore);
plane(300, 50);
pop();
}
}
function resetGame() {
lives = 5;
points = 0;
level = 1;
blocks = [];
newBlockChance = 100;
}
function levelUp () {
// increase difficulty
newBlockChance -= 5;
}
function blockIsScored() {
this.scored = 1;
ripeBlocks.shift();
}
function blockIsRipe() {
// if block is on the highlighted zone
if (this.z == 70) {
return true;
}
return false;
}
function drawBlock() {
push();
// block changes color based on nearness/if scored
if (this.scored == 0) {
if (this.z >= 70) {
txt.background("yellow");
} else {
txt.background("red");
}
} else {
txt.background("green");
}
// displays letter on the block
var upper = this.letter.toUpperCase();
txt.text(upper, 80, 120);
stroke("#104670");
texture(txt);
rotateX(blockSize);
translate(this.x, this.y, this.z);
box(blockSize);
pop();
// move forward
this.z += speed;
}
function makeNewBlock() {
var block = {
x: floor(random(-100, 100)),
y: floor(random(-150, 25)),
z: -2000,
letter: alphabet[floor(random(26))],
ripe: blockIsRipe,
score: blockIsScored,
scored: 0,
draw: drawBlock
}
return block;
}
function drawStars() {
fill(200);
noStroke();
for (var i = 0; i < numOfStars; i++) {
push();
translate(sx[i], sy[i], sz[i]);
sphere(starSize);
pop();
}
}
function keyTyped() {
if (lives == 0) {
// reset game
if (key == "r") {
resetGame();
}
} else {
// check if a block was pressed correctly/timely
for (var i = 0; i < ripeBlocks.length; i++) {
if (key == ripeBlocks[i].letter) {
points++;
ripeBlocks[i].score();
if (points % 10 == 0) {
levelUp();
}
}
}
}
}
For my final project I created a 3D typing game. I tried to recreate Blade Saber, a similar VR game where players slash through the blocks using a VR “saber”. For a long time, I tried to have the player score points by dragging their mouse through the block, but due to complexities of 3D in p5js (translations, rotations, etc) it was way out-of-scope for this project. I decided to simplify this to be a keyboard-based game instead. Enjoy!