// Yoo Jin Shin
// yoojins@andrew.cmu.edu
// Section D
// Final Project
// All scenes global variables
var yaxis = 1;
var xaxis = 2;
var frames = [];
var frameCounts = 0;
var textSpeed = 3;
var slider;
var sliderValue;
// Yeosu global variables
var c1, c2, c3, c4;
var terrainSpeed1 = 0.0001;
var terrainDetail1 = 0.003;
var terrainSpeed2 = 0.00012;
var terrainDetail2 = 0.0045;
var terrainSpeed3 = 0.00007;
var terrainDetail3 = 0.0006;
var clouds = [];
var trees = [];
var textX = 1;
var sunMouse;
var birdLoad;
var bird1;
var birds = [];
// Grand Canyon global variables
var terrainSpeed4 = 0.00003;
var terrainDetail4 = 0.0005;
var terrainSpeed5 = 0.00012;
var terrainDetail5 = 0.007;
var terrainSpeed6 = 0.00007;
var terrainDetail6 = 0.0065;
var textX3 = 1;
var c9;
// Las Vegas global variables
var c5, c6, c7, c8;
var buildings = [];
var snowflakes = [];
var drops = [];
var textX2 = 1;
var buildingMouse;
var carLoad;
var car1;
var cars = [];
function preload(){
// Loading sprite sheet for girl character via direct links from imgur
// Creds to https://www.gameart2d.com/freebies.html
var filenames = [];
filenames[0] = "https://i.imgur.com/oEJGbCm.png";
filenames[1] = "https://i.imgur.com/QsZoWMJ.png";
filenames[2] = "https://i.imgur.com/kyo4lhi.png";
filenames[3] = "https://i.imgur.com/XmPYK4a.png";
filenames[4] = "https://i.imgur.com/w80hQ6A.png";
filenames[5] = "https://i.imgur.com/0BiNaBy.png";
filenames[6] = "https://i.imgur.com/WL7kjhR.png";
filenames[7] = "https://i.imgur.com/z6agkKq.png";
filenames[8] = "https://i.imgur.com/Vxi5Eu6.png";
filenames[9] = "https://i.imgur.com/mrjcctG.png";
filenames[10] = "https://i.imgur.com/oDP8zsr.png";
filenames[11] = "https://i.imgur.com/DsNFucK.png";
filenames[12] = "https://i.imgur.com/8Y3e8BW.png";
filenames[13] = "https://i.imgur.com/aQUjC1v.png";
filenames[14] = "https://i.imgur.com/6yQMDLO.png";
filenames[15] = "https://i.imgur.com/fJZPSul.png";
filenames[16] = "https://i.imgur.com/vghvvNP.png";
filenames[17] = "https://i.imgur.com/oh2WsXz.png";
filenames[18] = "https://i.imgur.com/yPh4Upu.png";
filenames[19] = "https://i.imgur.com/k0wd9HK.png";
for (var i = 0; i < filenames.length; i++) {
frames.push(loadImage(filenames[i]));
}
// Loading bird and car images created using illustrator
birdLoad1 = "https://i.imgur.com/RQSUNmm.png";
bird1 = loadImage(birdLoad1);
carLoad1 = "https://i.imgur.com/yKCei98.png";
car1 = loadImage(carLoad1);
}
function setup() {
createCanvas(480, 480);
drawSlider();
// Yeosu: Define gradient colors
c1 = color(225, 217, 199); // sky top color - grey
c2 = color(254, 180, 144); // sky bottom color - orange
c3 = color(50, 50, 2); // ground left - lighter green
c4 = color(27, 20, 2); // ground right - dark green
// Yeosu: Initialize clouds
for (var i = 0; i < 3; i++) {
var cloudX = random(width);
clouds[i] = drawClouds(cloudX);
}
// Yeosu: Initialize trees
for (var i = 0; i < 7; i++) {
var treeX = random(width);
var t = drawTrees(treeX);
trees[i] = t;
}
// Yeosu: Initialize birds
for (var i = 0; i < 7; i++) {
var birdX = random(width);
birds[i] = drawBird(birdX);
}
// Las Vegas: Define gradient colors
c5 = color(0, 0, 0); // sky top color - black
c6 = color(61, 48, 52); // sky bottom color - purpley
// Las Vegas: Initialize buildings
for (var i = 0; i < 6; i++) {
var rx = random(width);
buildings[i] = drawBuilding(rx);
}
// Las Vegas: Initialize cars
for (var i = 0; i < 10; i++) {
var carX = random(width);
cars[i] = drawCar(carX);
}
imageMode(CENTER);
}
function drawSlider() {
slider = createSlider(5, 60, 30);
slider.style(200, '100px');
slider.position(20, 450);
}
function draw() {
// Set slider to adjust frameRate
var frameRateSpeed = slider.value();
frameRate(frameRateSpeed);
// Change scene every 160 frames, then loop
// Loop: Yeosu - GrandCanyon1 - LasVegas - GrandCanyon2
if (frameCounts <= 160) { // 160
drawYeosu();
}
else if (frameCounts >= 160 & frameCounts < 320) {
drawGrandCanyon(); // transition: light to dark sky
}
else if (frameCounts >= 320 & frameCounts < 480) {
drawLasVegas();
}
else if (frameCounts >= 480 & frameCounts < 640) {
drawGrandCanyon2(); // transition: dark to light sky
}
else if (frameCounts === 640) {
frameCounts = 0;
}
frameCounts++;
}
// =====================================================================
// ==============================YEOSU==================================
// =====================================================================
function drawYeosu() {
setGradient(0, 0, width, height / 2 + 70, c1, c2, yaxis); // sky
drawSun();
drawMountain1();
drawMountain2();
drawOcean();
drawSunReflection();
updateClouds();
removeClouds();
addClouds();
setGradient(0, 390, width, 480, c3, c4, xaxis); // ground
updateTrees();
removeTrees();
addTrees();
drawMe();
drawFog();
updateBird();
removeBird();
addBird();
writeText();
}
function setGradient(x, y, w, h, c1, c2, axis) {
// Top to bottom gradient
if (axis == yaxis) {
for (var i = y; i <= y + h; i++) {
var inter = map(i, y, y + h, 0, 1);
var c = lerpColor(c1, c2, inter);
stroke(c);
line(x, i, x + w, i);
}
}
// Left to right gradient
else if (axis == xaxis) {
for (var i = x; i <= x + w; i++) {
var inter = map(i, x, x + w, 0, 1);
var c = lerpColor(c1, c2, inter);
stroke(c);
line(i, y, i, y + h);
}
}
}
function drawMountain1(){
noStroke();
fill(204, 157, 135);
beginShape();
for (var x = 0; x < width; x++) {
var t = (x * terrainDetail1) + (millis() * terrainSpeed1);
var y = map(noise(t), 0, 1.8, height / 4 - 10, height);
vertex(x, y);
}
vertex(width, height);
vertex(0, height);
endShape();
}
function drawFog() {
for (var y = 200; y < height + 5; y++) {
var f = map(y, height / 2 - 40, 550, 0, 100);
stroke(255, f);
line(0, y, width, y);
}
}
function drawMountain2(){
fill(147, 126, 115);
noStroke();
beginShape();
for (var x = 0; x < width; x++) {
var t = (x * terrainDetail2) + (millis() * terrainSpeed2);
var y = map(noise(t), 0, 2, height / 4 + 60, height);
vertex(x, y);
}
vertex(width,height);
vertex(0,height);
endShape();
}
function drawOcean() {
fill(170, 146, 121);
noStroke();
beginShape();
for (var x = 0; x < width; x++) {
var t = (x * terrainDetail3) + (millis() * terrainSpeed3);
var y = map(noise(t), 0, 2, height - 230, height);
vertex(x, y);
}
vertex(width, height);
vertex(0, height);
endShape();
}
function drawSun() {
// All sun parts based on mouseY
sunMouse = constrain(mouseY, 0, height / 3 + 30);
noStroke();
// Sunglow
// Color less intense and size smaller as sun goes farther up
fill(253, 254 - sunMouse, 221 - sunMouse, 20 - sunMouse*0.02);
ellipse(width / 3, sunMouse + 40, sunMouse - 10, sunMouse - 10);
fill(253, 254 - sunMouse, 221 - sunMouse, 20 - sunMouse*0.03);
ellipse(width / 3, sunMouse + 40, sunMouse*1.1, sunMouse*1.1);
fill(253, 254 - sunMouse, 221 - sunMouse, 20 - sunMouse*0.05);
ellipse(width / 3, sunMouse + 40, sunMouse*1.2 + 10, sunMouse*1.2 + 10);
// Sunrays
fill(253, 254, 221, 30);
triangle(width / 3, sunMouse + 20, width / 3 - 40, height / 3 + 90, width / 3 - 10, height /3 + 80); // left big
triangle(width / 3, sunMouse + 20, width / 3 + 40, height / 3 + 80, width / 3 + 10, height /3 + 70); // right big
triangle(width / 3, sunMouse + 20, width / 3 - 50, height / 3 + 90, width / 3 - 30, height /3 + 80); // left small
triangle(width / 3, sunMouse + 20, width / 3 + 50, height / 3 + 80, width / 3 + 30, height /3 + 70); // right small
triangle(width / 3, sunMouse + 20, width / 3 - 70, height / 3 + 70, width / 3 - 80, height /3 + 70); // left small2
triangle(width / 3, sunMouse + 20, width / 3 + 70, height / 3 + 90, width / 3 + 80, height /3 + 80); // right small2
// Sun
fill(253, 254, 221, sunMouse + 30);
ellipse(width / 3, sunMouse + 40, sunMouse - 20, sunMouse - 20);
}
function drawSunReflection() {
// Size of reflection on ocean corresponds to sun size/distance away
var x = 10;
var y = 47;
var w = sunMouse;
var h = 10;
noStroke();
// Outer darker glow
fill(248, 172, 137, 100);
push();
rectMode(CENTER);
rect(width / 3, 2 * height / 3 + y, w, h, 10);
rect(width / 3, 2 * height / 3 + y - 13, w * 0.8, h, 10);
rect(width / 3, 2 * height / 3 + y - 26, w * 0.6, h, 10);
// Inner lighter glow
fill(253, 254, 221, 50);
rect(width / 3, 2 * height / 3 + y - 4, w * 0.9, h * 0.8, 10);
rect(width / 3, 2 * height / 3 + y - 17, w * 0.7, h * 0.8, 10);
rect(width / 3, 2 * height / 3 + y - 30, w * 0.5, h * 0.8, 10);
pop();
}
function updateClouds(){
for (var i = 0; i < clouds.length; i++) {
clouds[i].move();
clouds[i].display();
}
}
function removeClouds(){
var keepClouds = [];
for (var i = 0; i < clouds.length; i++) {
if (clouds[i].x - this.width / 2 < width) {
keepClouds.push(clouds[i]);
}
}
clouds = keepClouds;
}
function addClouds(){
var newCloud = 0.01;
if (random(0, 1) < newCloud) {
clouds.push(drawClouds(random(width)))
}
}
function moveClouds(){
this.x += this.speed;
}
function displayClouds(){
var b = random(165, 180);
fill(255, 240, 240, 30);
ellipse(this.x, this.y, this.width, this.height);
ellipse(this.x + 50, this.y + 10, this.width - 20, this.height - 70);
ellipse(this.x - 50, this.y - 5, this.width - 70, this.height - 10);
}
function drawClouds(newCloudX) {
var cloud = {
x: newCloudX,
y: random(30, height / 2),
width: random(150, 250),
height: random(20, 50),
speed: random(0.5, 1.5),
move: moveClouds,
display: displayClouds
}
return cloud;
}
function updateTrees() {
for (var i = 0; i < trees.length; i++) {
trees[i].move();
trees[i].display();
}
}
function removeTrees() {
var keepTrees = [];
for (var i = 0; i < trees.length; i++) {
if (trees[i].x < width) {
keepTrees.push(trees[i]);
}
}
trees = keepTrees;
}
function addTrees() {
var newTree = 0.02;
if (random(0, 1) < newTree) {
trees.push(drawTrees(random(width)));
}
}
function moveTrees() {
this.x += this.speed;
}
function displayTrees() {
// Tree height based on mouseY, mapped to a reasonable extent
var treeMouse = map(mouseY, 0, height, height / 3, 0);
var treeMouseCon = constrain(treeMouse, 0, height)
// Trunk
stroke(c4);
strokeWeight(4);
line(this.x, this.y + 20 - treeMouseCon, this.x, this.y + 40);
// Leaves
fill(c3, 20);
stroke(c3);
strokeWeight(7);
strokeJoin(ROUND);
triangle(this.x, this.y - treeMouseCon, this.x + 20, this.y + 25, this.x - 20, this.y + 25);
// Add snow if on Grand Canyon scene
if (frameCounts >= 160 & frameCounts < 320 || frameCounts >= 480 && frameCounts < 640) {
fill(250);
This project was inspired by my love of taking photos, especially of nature and cityscapes. I tried recreating the feel and weather of three shots below in a looping generative landscape. Follow me! You can speed up or slow my pace using the slider and control objects like the sun and buildings using the mouse.
Since this doesn’t load properly on WP, access zip file here!
]]>//Lan Wei
//lanw@andrew.cmu.edu
//Section // D
//Final Project-Musical Universe
var snd = []; //this array will hold the sounds
var amplitude = [];
var ampTotal; //Total amplitude to change background color
var mouseXArray = []; //the array of X values
var mouseYArray = []; //the array of y values
var balls = []; //the array of ball objects
var np = 26; //the number of background lines
var pArray = []; //the array of particles
//preload the sounds
function preload() {
var PREFIX = "https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/";
for (var i = 1; i < 10; i++){
var sound = loadSound(PREFIX + "sound" + i + ".wav"); //load sounds
sound.setVolume(0.5);
snd.push(sound); //push the sounds in snd. var sound will never be used again
}
}
//make ball objects
function makeBalls(bx, by, br){
var ball = {x: bx,
y: by,
r: br, //radius of the ball
update: updateBall, //update the size of the balls
sound: whichSound, //which amplitude is the ball related to
draw: drawBall,
play: playSound};
return ball; //return the new object
}
function drawBall(){
stroke(255);
//ellipse(this.x, this.y, this.r, this.r);
var total = 20; //control the density of the points on the balls
for (var i = 0; i < total; i++){
var longtitude = map(i, 0, total, -PI, PI);
for (var j = 0; j < total; j++){
var latitude = map(j, 0, total, -HALF_PI, HALF_PI);
//fold the longtitude-latitude panel into a sphere
var ptX = this.r * sin(longtitude) * cos(latitude);
var ptY = this.r * sin(longtitude) * sin(latitude);
var ptZ = this.r * cos(longtitude);
push();
stroke(255);
translate(ptX, ptY, ptZ);
sphere(0.1);
pop();
}
}
}
function whichSound(){
if (zone(this.x, this.y) == 1){
return snd[0];
}
if (zone(this.x, this.y) == 2){
return snd[1];
}
if (zone(this.x, this.y) == 3){
return snd[2];
}
if (zone(this.x, this.y) == 4){
return snd[3];
}
if (zone(this.x, this.y) == 5){
return snd[4];
}
if (zone(this.x, this.y) == 6){
return snd[5];
}
if (zone(this.x, this.y) == 7){
return snd[6];
}
if (zone(this.x, this.y) == 8){
return snd[7];
}
if (zone(this.x, this.y) == 9){
return snd[8];
}
}
function playSound(){
var soundLocal = this.sound();
soundLocal.play();
}
function updateBall(){
var amp = amplitude[zone(this.x, this.y) - 1];
var level = amp.getLevel(); //get the level
this.r = 30 + 300 * level; //the size of balls
}
//particles to make background lines
function makeParticle(px, py, pdx, pdy){
p = {x: px,
y: py,
dx: pdx,
dy: pdy,
update: pUpdate,
draw: pDraw}
return p;
}
function pUpdate(){
this.x += this.dx;
this.y += this.dy;
//limit the lines in a certain area of 500 * 500 using 'bouncing'
if (this.x < -250){ //left boundary
this.x = -this.x - 250;
this.dx = -this.dx - 250;
}
else if (this.x > 250){ //right boundary
this.x = 300 - this.x;
this.dx = 300 - this.dx;
}
if (this.y < -250){ //downward boundary
this.y = -this.y - 250;
this.dy = -this.dy - 250;
}
else if (this.y > 250){ //upward boundary
this.y = 300 - this.y;
this.dy = 300 - this.dy;
}
}
function pDraw(){
ellipse(this.x, this.y, 5, 5);
}
/////////////////////////////////////////////////////////////////////////////////////
function setup() {
createCanvas(450, 450, WEBGL); // 3D mode. (0, 0)locates in the center of the canvas
background(0);
stroke(255);
perspective(); //perspective view
for (var i = 0; i < 9; i++){
amplitude[i] = new p5.Amplitude();
amplitude[i].setInput(snd[i]); //get independent amplitude
}
ampTotal = new p5.Amplitude(); //total amplitude
for (var i = 0; i < np; i++){
//the boundary of particles is a little bigger than the canvas
var p = makeParticle(random(-300, 300), random(-300, 300), random(-2, 2), random(-2, 2));
pArray.push(p);
}
}
function draw() {
var levelTotal = ampTotal.getLevel();
var col = map(levelTotal, 0, 1, 0, 100);//background color
background(col, 0, 2 * col);
//draw background lines
for (var i = 0; i < np; i++){
pArray[i].update();
pArray[i].draw();
//lines between particles
for (var j = 0; j < np/2; j++){
stroke(random(0, 150));
strokeWeight(0.2);
line(pArray[j].x, pArray[j].y, pArray[j + np/2].x, pArray[j + np/2].y);
}
}
//the canvas is divided by a 3*3 grid
strokeWeight(1);
stroke(random(20, 70));
line(-75, -225, -75, 225);
line(75, -225, 75, 225);
line(-225, -75, 225, -75);
line(-225, 75, 225, 75);
if (mouseXArray.length != 0){ //after the 1st mouse press
stroke(255);
fill(255);
// draw all the balls
for (i = 0; i < balls.length; i ++){
balls[i].update(); //update the radius of the balls
push();
translate(balls[i].x, balls[i].y, 0);
//rotate with randomness
rotateX(frameCount * 0.05 + i);
rotateY(frameCount * 0.05 + i * 5);
rotateZ(frameCount * 0.05 + i * 5);
balls[i].draw(); //draw the balls
pop();
}
}
}
//To identify which zone is the ball in
//translate the coordinate to normal mode
function zone(x, y){
if ((y > -225 )& (y < height/3 - 225)){
if ((x > -225) && (x < width/3 - 225)){ //position 1
return 1;
}
if ((x > width/3 - 225) & (x < 2 * width/3 - 225)){ //position 2
return 2;
}
if ((x > 2 * width/3 - 225) & (x < width - 225)){ //position 3
return 3;
}
}
if ((y > height/3 - 225) & (y < 2 * height/3 - 225)){
if ((x > -225) && (x < width/3 - 225)){ //position 4
return 4;
}
if ((x > width/3 - 225) & (x < 2 * width/3 - 225)){ //position 5
return 5;
}
if ((x > 2 * width/3 - 225) & (x < width - 225)){ //position 6
return 6;
}
}
if ((y > 2 * height/3 - 225) & (y < height - 225)){
if ((x > -225) && (x < width/3 - 225)){ //position 7
return 7;
}
if ((x > width/3 - 225) & (x < 2 * width/3 - 225)){ //position 8
return 8;
}
if ((x > 2 * width/3 - 225) & (x < width - 225)){ //position 9
return 9;
}
}
}
//when mouse is clicked, a sound will be played and a ball will be drawn
function mouseClicked(){
var clickOn; //to check whether click on an existing ball
var newBalls = [];
var newMouseXArray = [];
var newMouseYArray = [];
if (mouseXArray.length == 0){ //when there is no existing balls
clickOn = 0;
newBalls.push(balls[0]);
newMouseXArray.push(mouseXArray[0]);
newMouseYArray.push(mouseYArray[0]);
}
// a ball will be removed when clicked again
else{ //after the 1st click
for (i = 0; i < balls.length; i++){
var distance = dist(mouseX - 225, mouseY - 225, balls[i].x, balls[i].y);
//is clicked
if (distance <= 20){ //minimum distance
var soundBall = balls[i].sound();
soundBall.setVolume(0); //stop the sound of the ball that's clicked
// balls.splice(i, 1); //remove the ball
// mouseXArray.splice(i, 1);
// mouseYArray.splice(i, 1);
clickOn = 1;
}
//is not clicked
else{
clickOn = 0;
newBalls.push(balls[i]);
newMouseXArray.push(mouseXArray[i]);
newMouseYArray.push(mouseYArray[i]);
}
}
balls = newBalls;
mouseXArray = newMouseXArray;
mouseYArray = newMouseYArray;
}
if (clickOn == 0){
mouseXArray.push(mouseX - 225);//translate to WEBGL coordinates
mouseYArray.push(mouseY - 225);
// initial radius: 30
//make ball objects
var newBall = makeBalls(mouseX - 225, mouseY - 225, 30);
balls.push(newBall);
}
//play sound using the object
for (i = 0; i < balls.length; i++){
var soundLocal = balls[i].sound();
balls[i].play();
soundLocal.loop();
}
}
//YOU'VE GOT A MUSICAL UNIVERSE!!!
[How to play]: Click on different positions of the canvas to get different mixtures of ‘UNIVERSE MUSIC’,click the balls again to remove them as well as the sound (in the opposite order you created the balls). Play slowly! (Sometimes it doesn’t work well if played fast, maybe it’s my browser’s problem.)
My initial idea of the project is to create a simple instrument that enables users to mix several pieces of music. In the proposal stage, I have imagined many sound effects and interaction methods, but have only achieved part of them. The actual coding process is much harder than I thought, and the final result becomes more visual than auditory. But actually, I’m very happy about the project because it is really a reviewing as well as a learning process. I’m not very good at using objects and particles so I tried to include these contents in my project. And I also watched some online tutorials to create the 3D effect of the balls. The final result is very satisfying for me, especially the 3D ball effect!
Have Fun!
]]>
// Jessica Timczyk
// Section D
// jtimczyk@andrew.cmu.edu
// Final-Project
///////// GLOBAL VARIABLES ///////////
var terrainDetail = 0.005;
var intercols = [];
/////// snow globals //////
var snow = [];
var gravity;
var zOff = 0;
var mt = [];
///// elephant globals //////
var frames = []; // store images
var frameIndex = 0;
var system; // particle system for elephants water
var trunkUp = false;
var counter = 0;
var click = -1;
//////// sound globals ////////
var elephantSound;
var birdSound;
var tigerSound;
///////// water waves globals /////////////
var t = 0;
///////// bush globals //////////////
var bush;
var bush1y= 240;
var bush2y = 350;
function preload() {
////////////////// ELEPHANT IMAGE PRELOADS ////////////////
// urls for each elephant image of movement cycle
var filenames = [];
filenames[0] = "https://i.imgur.com/PSJnCjQ.png";
filenames[1] = "https://i.imgur.com/PbWyeNh.png";
filenames[2] = "https://i.imgur.com/s0o7oWG.png";
// stores each picture into an array
for (var i = 0; i < 3; i++) {
frames[i] = loadImage(filenames[i]);
}
////////////////// BUSH PRELOADS ////////////////////
bush = loadImage("https://i.imgur.com/jdxSCfo.png");
////////////////// SOUND PRELOADS //////////////////
elephantSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/139875__y89312__44-1.wav");
elephantSound.setVolume(0.5);
birdSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/238144__m4d3r0__small-bird-jungle-sounds-v3-2.wav");
birdSound.setVolume(0.5);
tigerSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/439280__schots__angry-tiger-4.wav")
tigerSound.setVolume(0.5);
}
function setup() {
createCanvas(600, 400);
background(219, 245, 250);
////////////// MOUNTAIN SET UP /////////////
for (var x = 0; x < width; x++) { // createse a random static mountain range using noise every time refreshed
var t = (x * terrainDetail)
var y = map(noise(t), 0,1, 0, height/ 1.5);
mt.push([x,y,height]); // pushes the array into the mt array
}
//////////// ELEPHANT WATER PARTICLE SYSTEM ///////////
system = new ParticleSystem(createVector(260, 230)); // creates a particle system for the elephants water
//////////// SNOWFLAKE SET UP ////////////
gravity = createVector(0, 0.2); // gravity vector
for (var i = 0; i < 200; i++){ // creates the 200 snowflakes and gives them random starting positions
var x = random(width);
var y = random(height);
snow.push(new Snowflake(x,y)); // pushes new snowflake into the array
}
}
function draw() {
background(25, 28, 84);
drawNorthernLights();
drawScenary();
drawTrees();
drawBush();
////////////////// ELEPHANT /////////////////////
drawElephant();
if (frameIndex === 2) { // water comes out of his trunk when its in the up position
elephantWater();
}
if (mouseIsPressed & trunkUp == false && mouseX > 150 && mouseX < 300 && mouseY > 190 && mouseY < 350 && (click%2 === 0)) { // if mouse pressed on elephant trunk moves up
counter = 0
trunkUp = true
}
if (trunkUp == true) {
moveUp()
}
if (mouseIsPressed && counter > 32 && (click%2 != 0) && mouseX > 150 && mouseX < 300 && mouseY > 190 && mouseY < 350) { // when the elephant is clicked a second time it puts and keeps trunk down
counter = 0;
frameIndex = 0;
trunkUp = false;
}
drawTrees2(); // second set of trees that are infront of the elephant
drawBush2(); // second set of bushes that are infront of elephant
if (mouseX > 400 & mouseX < 480 && mouseY > 220 && mouseY < 260) { // the bush hops when mouse is scrolled over it
bush1y += random(-5, 5);
}
if (mouseX > 30 & mouseX < 130 && mouseY > 345 && mouseY < 385) { // the bush hops when mouse is scrolled over it
bush2y += random(-5, 5);
}
////////////////// DRAWING SNOWFLAKE /////////////
zOff += 0.01; // z offset
for (flake of snow) { //the position, gravity and wind forces are unique to each snowflake
var xOff = flake.pos.x / width;
var yOff = flake.pos.y / height;
var wAngle = noise(xOff, yOff, zOff) * TWO_PI;
var wind = p5.Vector.fromAngle(wAngle);
wind.mult(0.2);
flake.applyForce(gravity);
flake.applyForce(wind);
flake.update();
flake.render();
}
mySounds(); // plays sounds
}
function drawScenary() {
/////////// MOUNTAINS /////////////
stroke(76, 86, 109); // Grey color of mountains
noFill();
beginShape();
for (var x = 0; x < width; x++) { // createse a random static mountain range using noise every time refreshed
line(mt[x][0], mt[x][1], mt[x][0], mt[x][2]);
}
endShape();
////////////// GROUND ///////////////
noStroke();
fill(196, 157, 112);
rect(0, height / 2 + 20, width, height / 2 - 20);
/////////// POND ///////////////
noStroke();
fill(69, 156, 223);
ellipse(width - 40, height - 60, 500, 150);
}
///////////////////// BUSHES ///////////////////////
function drawBush() { // draws bush images that sit behind the elephant
imageMode(CENTER);
image(bush, 440, bush1y, 150, 70); // variable so that it wiggles up and down
image(bush, 320, 260, 150, 70);
image(bush, 85, 240, 190, 70);
}
function drawBush2() { // draws bush images that sit infront of the elephant
imageMode(CENTER);
image(bush, 310, 380, 180, 70);
image(bush, 70, bush2y, 150, 70); // variable so that bush shakes when moused over
}
/////////////////// ELEPHANT /////////////////////
function drawElephant() { // elephant picture
imageMode(CENTER);
image(frames[frameIndex], 230, 270, 200, 200);
}
function moveUp() { // moves trunk all the way up when clicked once
if (click%2 == 0) { // when the elephant is clicked an odd time the trunk just goes down
counter += 1;
if (counter == 15) {
frameIndex = 1;
}
if (counter == 30) {
frameIndex = 2;
}
}
}
function mousePressed() { // increases click count when mouse is pressed once and mouse on elephant
if (mouseX > 150 & mouseX < 300 && mouseY > 190 && mouseY < 350){
click += 1;
}
}
function elephantWater() { // run particle system
system.addParticle();
system.run();
}
// simple particle class
var Particle = function(position) {
this.acceleration = createVector(0, 0.05);
this.velocity = createVector(random(-1, 1), random(-1, 0));
this.position = position.copy();
this.lifespan = 255;
}
Particle.prototype.run = function() {
this.update();
this.display();
}
// updating position
Particle.prototype.update = function() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.lifespan -= 2;
}
// display method for particles
Particle.prototype.display = function() {
stroke(190, 244, 250, this.lifespan);
strokeWeight(2);
fill(143, 239, 250, this.lifespan);
ellipse(this.position.x, this.position.y, 12, 12);
}
// check if particle is still useful
Particle.prototype.isDead = function() {
return this.lifespan < 0;
}
var ParticleSystem = function(position) { // position of particles
this.origin = position.copy();
this.particles = [];
}
ParticleSystem.prototype.addParticle = function() { // pushes a new particle into the system at the starting point
this.particles.push(new Particle(this.origin));
}
ParticleSystem.prototype.run = function() { // gets rid of particles that have expired
for (var i = this.particles.length-1; i >= 0; i--) {
var p = this.particles[i];
p.run();
if (p.isDead()) {
this.particles.splice(i, 1);
}
}
}
////////////////// TREES ////////////////////////
function drawTrees() { // trees that are positioned begind elephant
push();
translate(550, 250);
drawBranch(0, map(mouseY, 0, 400, 15, 20));
pop();
push();
translate(150, 250);
drawBranch(0, map(mouseY, 0, 400, 15, 20));
pop();
push();
translate(100, 290);
drawBranch(0, map(mouseY, 0, 400, 15, 20));
pop();
}
function drawTrees2() { // trees that are positioned in front of elephant also rotate opposite way
push();
translate(100, 350);
drawBranch2(0, map(mouseY, 0, 400, 15, 20));
pop();
push();
translate(500, 260);
drawBranch2(0, map(mouseY, 0, 400, 15, 20));
pop();
}
function drawBranch2(depth, len) { // same as drawtree but these trees are positioned infront of the elephant
strokeWeight(depth / 3 + 1);
stroke(random( 15, 180), random(100, 244), random(10, 100)); // continuously changing green color
line(0, 0, 0, -len);
push();
translate(0, -len);
drawTree2(depth + 1, len);
pop();
}
function drawTree2(depth, len) { // same as drawtree but these trees are positioned infront of the elephant
if (depth < 8) {
rotate(radians(map(mouseX, 400, 0, -5, 5))); // allows trees to rotate with mouse
drawBranch2(depth, len);
rotate(radians(30));
drawBranch2(depth, len);
}
}
function drawBranch(depth, len) { // idividual branch details
strokeWeight(depth / 3 + 1);
stroke(random( 15, 180), random(100, 244), random(10, 100)); // continuously changing green color
line(0, 0, 0, -len);
push();
translate(0, -len);
drawTree(depth + 1, len);
pop();
}
function drawTree(depth, len) { // draws the two sides of the tree branches
if (depth < 8) {
rotate(radians(map(mouseX, 0, 400, -30, -20))); // allows trees to rotate with mouse
drawBranch(depth, len);
rotate(radians(30));
drawBranch(depth, len);
}
}
////////////////// NORTHERN LIGHTS ///////////////////
function drawNorthernLights() {
noStroke();
fill(random( 15, 180), random(10, 100), random(100, 244), 190); // make colors change and transparent to look like northern lights
// make a x and y grid of circles
for (let x = 0; x <= width; x = x + 30) {
for (let y = 0; y <= height / 2 + 20; y = y + 30) {
// starting point of each circle depends on mouse position
let xAngle = map(mouseX, 0, width, -4 * PI, 4 * PI, true);
let yAngle = map(mouseY, 0, height, -4 * PI, 4 * PI, true);
// also varies based on the particle's location
let angle = xAngle * (x / width) + yAngle * (y / height);
// each particle moves in a circle
let myX = x + 20 * cos(2 * PI * t + angle);
let myY = y + 20 * sin(2 * PI * t + angle);
ellipse(myX, myY, 10); // draw particle
}
}
t = t + 0.01; // update time
}
////////////////// SNOWFLAKES ///////////////////
function getRandomSize() { // makes a randomly sized snowflake that is more likely to be smaller
var r = pow(random(0, 1), 5);
return constrain(r * 32, 2, 36);
}
class Snowflake {
constructor() { // snowflake object
var x = random(width);
var y = random(-100, -10);
this.pos = createVector(x, y);
this.vel = createVector(0, 0);
this.acc = createVector();
this.angle = random(TWO_PI);
this.dir = (random(1) > 0.5) ? 1 : -1;
this.xOff = 0;
this.r = getRandomSize(); // random sizr of snowflakes
}
applyForce(force) {
// parallax effect hack
var f = force.copy();
f.mult(this.r);
this.acc.add(f); // add acceleration force
}
randomize() { // gives flakes a random starting poisiton above the canvas
var x = random(width);
var y = random(-100, -10);
this.pos = createVector(x, y);
this.vel = createVector(0, 0);
this.acc = createVector();
this.r = getRandomSize();
}
update() { // updates positions, speeds, angles and etc of snow
this.xOff = sin(this.angle * 2) * 2 * this.r; // sine wave making snow move in addition to perlin noise
this.vel.add(this.acc); // velocity calculations
this.vel.limit(this.r * 0.2);
if (this.vel.mag() < 1) {
this.vel.normalize();
}
this.pos.add(this.vel);
this.acc.mult(0);
if (this.pos.y > height + this.r){ //gives them a new random position when they reach the bottom of the screen
this.randomize();
}
// flakes wrap left and right
if (this.pos.x < -this.r) {
this.pos.x = width + this.r;
}
this.angle += this.dir * this.vel.mag() / 200; // spin of flakes goes different directions, also dependent on their speed
}
render(){ // draw the snowflakes
stroke(255);
push();
strokeWeight(this.r);
translate(this.pos.x + this.xOff, this.pos.y);
point(0, 0);
pop();
}
}
function mySounds() {
// elephant sound
// plays sound when elephants clicked on and only when trunk us down
if (mouseIsPressed & mouseX > 150 && mouseX < 300 && mouseY > 190 && mouseY < 350 && click%2 == 0) {
elephantSound.play();
}
// bird sound
// plays sound when bush is clicked on
if (mouseIsPressed & mouseX > 30 && mouseX < 130 && mouseY > bush2y - 20 && mouseY < bush2y + 20) {
birdSound.play();
}
// tiger sound
// plays sound when bush is clicked on
if (mouseIsPressed & mouseX > 400 && mouseX < 480 && mouseY > bush1y - 20 && mouseY < bush1y + 20) {
tigerSound.play();
}
}
After following the instructions for uploading sounds onto WP, my program will run perfectly but the sounds for some reason are still not working. Therefore, I have additionally included a zip of my final project so that you can see the working sounds. All you need to do is unzip the file and open it using a local server. After troubleshooting many times, I am still unsure why the sound will not work over WordPress. Final Project
I really enjoyed doing this project, it forced me to use new techniques and functions that I never had before. I really wanted this to be an interactive picture, using many complex computations to make things move and etc.
]]>// John Legelis
// Section D
// FINAL
// Board dimensons
var b
var boardW = 200
var boardH = 350
var boardX = 10
var boardY = 10
// Oscillator dimensions/variables
var oscSpacing = boardH/4.1
var numberboxS = 20
// Dial dimensions/variables
var dR = 17
var dialStroke = 4
var dialSpacing = boardW/5
var theta = Math.PI/2
var overDial = false
var moving = false
var iy
var oldTheta
var thetaMin = -1/4*Math.PI
var thetaMax = 5/4*Math.PI
var pressed = false
var oscOn = false
var c = 2**(1/12) // Frequency diffrence between half steps
var cf = 2 **(1/100/12) // Frequency diffrence between cents
var volKnob = 0 // Index of volume knob
var pitchKnob = 1 // Index of pitch knob
var fineKnob = 2 // Index of fine knob
var wList = ["z", "x", "c", "v", "b", "n", "m", ",", ".", "/" ]
var bList = ["s", "d", false, "g", "h", "j", false, "l", ";"]
function setup() {
createCanvas(600, 385);
background(0);
noFill()
// Draw the board
b = new ThreeWaveBoard(boardX, boardY, boardW, boardH)
b.drawBoard()
}
function ThreeWaveBoard(X,Y,W,H) {
// Board Dimensions
this.bX = X
this.bY = Y
this.bW = W
this.bH = H
this.bStroke = 4
this.oscillators = []
// Draw board border ==> rectangle
this.drawBoard = function() {
stroke(255)
strokeWeight(this.bStroke)
rect(this.bX, this.bY, this.bW, this.bH)
strokeWeight(2)
rect(this.bW/2,this.bY + 50, 10,10)
strokeWeight(1)
textSize(15)
text("FAT", this.bW/2 + 15, this.bY+60)
textSize(29)
text("3 Wave Oscillator", this.bX+8, this.bY+40)
// Make 3 oscillators
for (osc=0; osc<3; osc++){
var oX = this.bX
var oY = this.bY + oscSpacing*(osc+1)
var oW = this.bW
var oNum = osc + 1
this.oscillators.push(new oscillator(oX, oY, oW, oNum))
this.oscillators[osc].drawoscillator()
}
}
// Update board
this.updateBoard = function() {
stroke(255)
strokeWeight(this.bStroke)
rect(this.bX, this.bY, this.bW, this.bH)
strokeWeight(1)
textSize(26)
text("3 Wave Oscillator", this.bX+8, this.bY+40)
// Update 3 oscillators
for (osc=0; osc<3; osc++){
this.oscillators[osc].updateOscillators()
}
}
}
function oscillator(X,Y,W,num) {
// Oscillator Dimensions
this.oX = X
this.oY = Y
this.oW = W
this.oStroke = 4
this.oTextStroke = 1
this.oTextSize = 15
this.number = num
this.dials = []
this.wave = new p5.Oscillator()
this.f = false
this.fine = 0
this.volume = 0
// Draw oscillator ==> line, number box, number
this.drawoscillator = function() {
//line and number box
stroke(255)
strokeWeight(this.oStroke)
line(this.oX, this.oY, this.oX+this.oW, this.oY)
rect(this.oX, this.oY, 20, 20)
// Number text
strokeWeight(this.oTextStroke)
textSize(this.oTextSize)
text(num, this.oX+5, this.oY+15)
// Make volume dial
var dX = this.oX + dR + this.oW/5 * (2)
var dY = this.oY + oscSpacing/2
this.dials.push(new Dial(dX, dY, dR, theta, -12, 12, "Volume"))
this.dials[0].drawDials()
// Make pitch dial
var dX = this.oX + dR + this.oW/5 * (3)
var dY = this.oY + oscSpacing/2
this.dials.push(new Dial(dX, dY, dR, theta, -24, 24, " Pitch"))
this.dials[1].drawDials()
// Make fine dial
var dX = this.oX + dR + this.oW/5 * (4)
var dY = this.oY + oscSpacing/2
this.dials.push(new Dial(dX, dY, dR, theta, -12, 12, " Fine"))
this.dials[2].drawDials()
}
this.updateOscillators = function() {
// Line and number box
stroke(255)
strokeWeight(this.oStroke)
line(this.oX, this.oY, this.oX+this.oW, this.oY)
rect(this.oX, this.oY, 20, 20)
// Number text
strokeWeight(this.oTextStroke)
textSize(this.oTextSize)
text(num, this.oX+5, this.oY+15)
for (i=0; i<this.dials.length; i++){
this.dials[i].drawDials()
}
}
}
function Dial(X, Y, R, theta, min, max, kind) {
// Dial dimensions
this.dialX = X
this.dialY = Y
this.dialR = R
this.stroke = 4
this.l1X = X
this.l1Y = Y
this.l2X = X + R*cos(theta)
this.l2Y = Y - R*sin(theta)
this.theta = theta
this.over = false
this.kind = kind
// Ranges of each dials
this.min = min
this.max = max
this.val = 0 // value of dial initialized to 0
// Turns a decimal radian angle into a number rounded to a specific fration of the circle related to the ticks of the dial
this.inc = function(dec) {
this.range = (this.max - this.min)/2
var increment = 3/4*Math.PI/(this.range)
var d = dec/increment
var r = round(d)
var num = r * increment
return num // Returns radian value along tick marker
}
// Draw dial shape and text
this.drawDials = function() {
this.l2X = X + R*cos(this.theta)
this.l2Y = Y - R*sin(this.theta)
strokeWeight(this.stroke)
stroke(255)
ellipse(this.dialX, this.dialY, this.dialR * 2, this.dialR * 2) // Dial circle
line(this.l1X, this.l1Y, this.l2X, this.l2Y) // Dial line
strokeWeight(1)
text(this.val, this.dialX - 4, this.dialY + 32)
text(this.kind, this.dialX - 25, this.dialY - 22)
stroke(0)
}
}
function draw() {
stroke(255)
noFill()
background(0)
b.updateBoard()
drawPiano(230,20)
// If a key is being pressed freq is its frequnncy
if (keyIsPressed & !oscOn && typeof whichKey() == "string" && whichKey() == key) {
oscOn = true
for(waves = 0; waves < b.oscillators.length; waves++) {
// Math for adjusting pitch and volume according to knobs
b.oscillators[waves].f = frequencyfunc(whichKey()) * (c ** (b.oscillators[waves].dials[pitchKnob].val)) * (cf ** (b.oscillators[waves].dials[fineKnob].val))
b.oscillators[waves].volume = 0.5 + map(b.oscillators[waves].dials[volKnob].val, -12, 12, -0.5 ,0.5)
// Update oscilator parameters
b.oscillators[waves].wave.amp(b.oscillators[waves].volume)
b.oscillators[waves].wave.freq(b.oscillators[waves].f)
b.oscillators[waves].wave.start()
}
}
if (oscOn){
// If another key is pressed while one is already down, priortize newest key
if (whichKey != key){
for(waves = 0; waves < b.oscillators.length; waves++) {
b.oscillators[waves].f = frequencyfunc(whichKey()) * (c ** (b.oscillators[waves].dials[pitchKnob].val)) * (cf ** (b.oscillators[waves].dials[fineKnob].val))
b.oscillators[waves].wave.freq(b.oscillators[waves].f)
b.oscillators[waves].volume = 0.5 + map(b.oscillators[waves].dials[volKnob].val, -12, 12, -0.5 ,0.5)
b.oscillators[waves].wave.amp(b.oscillators[waves].volume)
}
}
if (!keyIsPressed){
oscOn = false
}
}
// Turn sounds off if oscillator is off --> no key is pressed
if (!oscOn) {
for(waves = 0; waves < b.oscillators.length; waves++) {
b.oscillators[waves].wave.stop()
}
}
// Check each dial in each oscillator
for(osc=0; osc < b.oscillators.length; osc++){
for(d=0; d < b.oscillators[osc].dials.length; d++){
// If clicked / dragged on dial adjust the angle of specific dial respectively
if ((dist(mouseX, mouseY, b.oscillators[osc].dials[d].dialX, b.oscillators[osc].dials[d].dialY)) <= (dR) & (mouseIsPressed) && !moving) {
b.oscillators[osc].dials[d].over = true
moving = true
iy = mouseY
oldTheta = b.oscillators[osc].dials[d].theta
}
if (!mouseIsPressed) {
b.oscillators[osc].dials[d].over = false
moving = false
}
if (b.oscillators[osc].dials[d].over == true) {
var rawTheta = (oldTheta - map((iy - mouseY)*3, 0, height, 0, 2*Math.PI)) // Smooth theta from mouse
var cTheta = constrain(rawTheta, thetaMin, thetaMax) // Constrained theta based on min and max
var iTheta = b.oscillators[osc].dials[d].inc(cTheta) // Theta chopped to increment
b.oscillators[osc].dials[d].val = round((map(iTheta, thetaMin, thetaMax, b.oscillators[osc].dials[d].max, b.oscillators[osc].dials[d].min)))
b.oscillators[osc].dials[d].theta = iTheta
}
}
}
}
// Return string value of a keyboard key, otherwise false
function whichKey(){
if ( keyIsDown(90) || keyIsDown(83) || keyIsDown(88) || keyIsDown(68) || keyIsDown(67) || keyIsDown(86) || keyIsDown(71) || keyIsDown(66) || keyIsDown(72) || keyIsDown(78) || keyIsDown(74) || keyIsDown(77) || keyIsDown(188)|| keyIsDown(76) || keyIsDown(190)|| keyIsDown(186)|| keyIsDown(191) ) {
k = key
}
if ((keyIsPressed) && (key == k)) {
return str(k)
}
else{
return false
}
}
// Function that draws the piano shapes and text
function drawPiano(x,y){
var whiteW = 35
var whiteH = 200
var blackW = 20
var blackH = 115
// White Keys
for (i=0; i<10; i++){
push()
translate(x,y)
fill(255)
rect(i*whiteW, 0, whiteW, whiteH)
fill(0)
textSize(25)
text(wList[i],i*whiteW+ whiteW/2 - 6, whiteH - 30)
pop()
}
// Black Keys
for (j=0; j<9; j++){
if ((j != 2) & (j != 6)) { // Exclude black key between E and F, and B and C
push()
translate(x,y)
fill(0)
rect((j+1) * whiteW - blackW/2, 0, blackW, blackH)
fill(255)
textSize(22)
text(bList[j],(j+1) * whiteW - blackW/2 + 6 , blackH - 15)
pop()
}
}
fill(255)
stroke(255)
textSize(30)
text("Click and Drag Dials!", 250,290)
text("Play piano with keyboard!", 250, 340)
noFill()
strokeWeight(5)
stroke(255)
rect(240,250,340,110)
}
// Return the frequency of a keyboard key given string of key pressed
function frequencyfunc(str) {
if (str == "z") {
f = 523.25 // C5
} else if (str == "s") {
f = 554.37 // C#5
} else if (str == "x") {
f = 587.33 // D5
} else if (str == "d") {
f = 622.25 // Eb5
} else if (str == "c") {
f = 659.25 // E5
} else if (str == "v") {
f = 698.46 // F5
} else if (str == "g") {
f = 739.99 // F#5
} else if (str == "b") {
f = 783.99 // G5
} else if (str == "h") {
f = 830.61 // Ab5
} else if (str == "n") {
f = 880.00 // A5
} else if (str == "j") {
f = 932.33 // Bb5
} else if (str == "m") {
f = 987.77 // B5
} else if (str == ",") {
f = 1046.50 // C6
} else if (str == "l") {
f = 1108.73 // C#6
} else if (str == ".") {
f = 1174.66 // D6
} else if (str == ";") {
f = 1244.51 // Eb6
} else if (str == "/") {
f = 1318.51 // E6
}
else {
f = false
}
return f
}
Click within the project to start!
My final project took a lot of effort on the back end side especially on the dial interaction. In order to make them clickable and draggable and easy to interface with, a lot of thinking and debugging had to be done in order for them to behave properly. Then the p5.Oscillators were used as a basis of the sound of my Oscillator. That package took some troubleshooting but eventually I worked out how to create this monophonic synthesizer. I am very proud of this instrument as it is highly functional and can be used as a real live instrument. It was lots of fun to work on and see it come together.
]]>/*
Min Jun Kim & Han Yu
minjunki && hyu1
15104
Final Project
*/
//This Project is a spaceship fighting game.
var px = 0; //x location of ship
var py = 240; //starting y location of our ship
var psize = 50; //size of ourship
var keys = []; //for controlling sihp
var Selfprojectiles = []; //array of our projectiles
var projectileCounter = 100; //for controlling flow of projectiles
var SOE = []; //array of enemy ships
var shipSpeed = 3; //our speed for moving ship
var distanceOne = []; //distance between projectile and enemy
var distanceTwo = []; //distance between projectile and enemy
var SOEnumb = 2; //number of enemies
var timer = 0; //for controlling flow of game
var gameStage = 1; //starting stage of the game
var timerIncrement = 1; //another variable for controlling flow of game
var enemyProjectiles = []; //array of enemy bullets
var EPdistance = []; //distance between enemy bullets and our ship
var ourHealth = 10; //how much health we have left
var gameOver = 0; //activated when user dies
var clickStart = 0; //for controlling start of game
var clickCount = 0; //for making sure no double clicks
var stars = []; //array of stars in background
var imageOne; //image
var imageTwo; //image
var imageThree; //image
//making background stars
function makeStars(x, y, size) {
return {x: x, y: y, size: size, draw: starRender, update: starUpdate}
}
//draws stars as ellipse
function starRender() {
fill(255);
ellipse(this.x, this.y, this.size);
}
//scrolling speed of stars
function starUpdate() {
this.x -= 3;
}
//making our bullets
function makeOurProjectile(x, y, fast, powerful) {
return {x: x, y: y, speed: fast, power: powerful,
drawIt: OPrender, updateIt: OPupdate}
}
//making enemy bullets
function makeEnemyProjectile(x,y, speedo,status) {
return {x: x, y: y, speed: speedo, boss: status,
updir: random(-5,5), drawIt: EPrender, updateIt: EPupdate}
}
//draws bullets, depending on type of enemy
function EPrender() {
//if boss, have balls instead of lines
if (this.boss == true) {
fill(0);
stroke(250,0,0);
ellipse(this.x, this.y, 10, 10);
}
//standard line bullets from enemy
else {
stroke(250, 0, 0);
strokeWeight(3);
line(this.x, this.y, this.x-30, this.y);
}
}
//controls the movement of enemy bullets depends on enemy type
function EPupdate() {
//if boss, then make bullets bounce and have random speed
if (this.boss == true) {
this.y += this.updir;
this.x -= this.speed*random(1,2);
//bounces bullets
if (this.y > height || this.y < 0) {
this.updir = -this.updir;
}
}
//all other enemies have one dimensional attacks
else {
this.x -= this.speed;
}
}
//draws our bullets as green lines
function OPrender() {
stroke(0,200,0);
strokeWeight(this.power);
line(this.x, this.y, this.x+30, this.y);
}
//moves the bullet across map
function OPupdate() {
this.x += this.speed;
}
//the specs of enemy ships
function makeSOEnemy(x, y, fast, sizing) {
return {x: x, y: y, big: sizing, speed: fast,
drawIt: SOErender, update: SOEupdate, DMG: 0}
}
//controls the movement of enemy ships depending on size and location
function SOEupdate() {
//moves the enemy along the map
if (gameStage == 1) {
this.y += this.speed;
}
if (gameStage == 2) {
this.y += this.speed;
}
//if 2nd round 1st enemy, move back as game goes on
if (gameStage == 2 & this.x < 500) {
this.x += 0.1;
}
if (this.y > height) {
this.speed = -this.speed;
}
if (this.y < 0) {
this.speed = -this.speed;
}
//first round enemy with easy projectiles
if (gameStage == 1) {
if (random(1) > 0.98 & this.y < 600) {
enemyProjectiles.push(makeEnemyProjectile(this.x, this.y,5));
}
}
//2nd round enemy, with more frequent bullets
if (gameStage == 2 & this.big < 90) {
if (random(1) > 0.93 && this.y < 600) {
enemyProjectiles.push(makeEnemyProjectile(this.x, this.y, 5));
}
}
//pulls our ship closer and make projectiles more frequently
if (gameStage == 2 & this.big > 90) {
if (random(1) > 0.95 && this.y < 600) {
enemyProjectiles.push(makeEnemyProjectile(this.x, this.y,
random(4,6,1), true));
}
px+=0.3;
}
}
//draws the enemies depending on the size input.
function SOErender() {
fill(100)
//draw the boss and health bar
if (gameStage == 2 & this.big > 90) {
push();
fill(250,0,0);
rectMode(CORNER);
rect(5,5,width-this.DMG*6.4,20);
pop();
push();
translate(this.x-85, this.y-60);
scale(0.37, 0.37);
image(imageThree,0, 0);
pop();
}
//the standard enemy
else {
//health bar
push();
fill(220, 0, 0);
rect(this.x, this.y - this.big/1.5,
this.big - this.DMG*this.big/20, 10);
pop();
//standard enemy
push();
translate(this.x-this.big*1.4, this.y-this.big*0.5);
scale(0.0035*this.big, 0.0035*this.big);
image(imageTwo, 0, 0);
pop();
}
}
//==============================================================
//sets up the keys and the initial stars
function setup() {
createCanvas(640,480)
keys = [false, false, false, false, false]
for (i = 0; i < 200; i++) {
stars[i]=makeStars(random(width), random(height), random(5));
}
imageOne = loadImage("https://i.imgur.com/GHtg2vU.png");
imageTwo = loadImage("https://i.imgur.com/svK8OkI.png");
imageThree = loadImage("https://i.ibb.co/n0G0yGC/eae.png");
}
function draw() {
//basic initial specs
background(17,21,61);
rectMode(CENTER);
noStroke();
fill(100);
//projectile counter increases by 1
projectileCounter += 1;
//increase timer by timer increment
timer += timerIncrement;
//makes first round of enemies when timer is 1
if (timer == 1 & gameStage == 1) {
for (i = 0; i < SOEnumb; i ++) {
SOE[i] = makeSOEnemy(500,random(height), random(5), random(40,60));
}
timer += 1;
}
//states the first round
if (timer > 1 & timer < 40) {
textAlign(CENTER);
push();
stroke(255);
fill(255);
textSize(20);
text("Round One!", width/2, height/2-100);
pop();
}
//pauses the increase in timer
if (timer > 41) {
timerIncrement = 0;
}
//resumes timer once 1st round is finished
if (gameStage == 2) {
timerIncrement = 1;
}
//once timer has started moving again, write text stating the stage
if (timer > 50 & timer < 150) {
push();
noStroke();
fill(255);
textSize(20);
text("Final Stage!", width/2, height/2-100);
pop();
}
//makes 2 more enemies once timer reaches 60
if (timer == 60 & gameStage == 2) {
SOE[0] = makeSOEnemy(400,random(height), 6.5, 30);
SOE[1] = makeSOEnemy(550, height/2, 3, 100);
timer += 1;
}
//move right if rightarrow is pressed
if (keys[0] == true) {
px += shipSpeed;
}
//move left if leftarrow is pressed
if (keys[1] == true) {
px -= shipSpeed;
}
//move up if uparrow is pressed
if (keys[2] == true) {
py -= shipSpeed;
}
//move down if downarrow is pressed
if (keys[3] == true) {
py += shipSpeed;
}
//add projectiles once shift is pressed and reset counter
if (keys[4] == true & projectileCounter > 5) { //8
Selfprojectiles.push(makeOurProjectile(px+50, py, 10, 3))
projectileCounter = 0;
}
//for teleporting up and down once limit is reached
if (py > height) {
py = 0;
}
if (py < 0) {
py = height;
}
//prevents user from going behind map by pushing back
if (px < -5) {
px += 3;
}
//prevents user from moving too forward by pushing back
if (px > width/2) {
px -= 3.3;
}
//makes random star objects, and initializes the functions
noStroke()
for (i = 0; i < 1; i++) {
stars.push(makeStars(width, random(height), random(5)));
}
for (i = 0; i < stars.length; i++) {
stars[i].draw();
stars[i].update();
//if stars if too far behind map, shift the array
if (stars[i].x < -500) {
stars.shift();
}
}
//initializes the enemoies
for (i = 0; i < SOEnumb; i ++) {
SOE[i].drawIt();
SOE[i].update();
}
//initializes the enemy projectiles
for (i = 0; i < enemyProjectiles.length; i++) {
enemyProjectiles[i].drawIt();
enemyProjectiles[i].updateIt();
}
//initializes our projectiles
for (i = 0; i < Selfprojectiles.length; i++) {
Selfprojectiles[i].drawIt();
Selfprojectiles[i].updateIt();
//calculates distance if the array is nonzero and projectiles
//are still on map
if (Selfprojectiles.length > 0 & Selfprojectiles[i].x < 700) {
//calculate the distance between enemy 1 and projectile
distanceOne[i] = dist(Selfprojectiles[i].x,
Selfprojectiles[i].y, SOE[0].x, SOE[0].y);
//calculate distance between enemy 2 and projectiles
distanceTwo[i] = dist(Selfprojectiles[i].x,
Selfprojectiles[i].y, SOE[1].x, SOE[1].y);
//if off map shift distances and projectiles
if (Selfprojectiles[i].x > width) {
Selfprojectiles.shift();
distanceOne.shift();
distanceTwo.shift();
}
//if distance is smaller than the size of enemy 1 shift
if (distanceOne[i] < SOE[0].big) {
Selfprojectiles.shift();
distanceOne.shift();
distanceTwo.shift();
SOE[0].DMG ++;
}
//if distance is smaller than the size of enemy 2 shift
if (distanceTwo[i] < SOE[1].big) {
Selfprojectiles.shift();
distanceOne.shift();
distanceTwo.shift();
SOE[1].DMG ++;
}
//if enemy 1 reach damage cap, move off map prevent shooting
if (SOE[0].DMG > 20) {
SOE[0].y = 700;
SOE[0].x = 700;
SOE[0].speed = 0;
}
//if enemy 2 reach damage cap, move off map prevent shooting
if (SOE[1].DMG > 20 & gameStage == 1) {
SOE[1].y = 700;
SOE[1].x = 700;
SOE[1].speed = 0;
}
//if boss reach damage cap move off stage
if (SOE[1].DMG > 100 & gameStage == 2) {
SOE[1].y = 700;
SOE[1].x = 700;
SOE[1].speed = 0;
}
//if both enemies are killed during 2nd stage, end game
if (SOE[0].y == 700 & SOE[1].y == 700 && SOE[1].DMG > 99) {
gameStage = 3;
}
//if both enemnies killed during 1st stage, move up stage
if (SOE[0].y == 700 & SOE[1].y == 700 && gameStage == 1) {
gameStage =2;
}
stroke(255);
}
}
for (z = 0; z < enemyProjectiles.length; z++) {
//if enemy projectile array is nonzero, calculate distance
if (enemyProjectiles.length > 0) {
EPdistance[z] = dist(enemyProjectiles[z].x, enemyProjectiles[z].y,
px, py);
//if bullet goes off map, shift distance and bullet
if (enemyProjectiles[z].x < 0) {
enemyProjectiles.shift();
EPdistance.shift();
}
//if projectile too close to ourship, shift arrays and do damage
if (EPdistance[z] < 20 & timer != 0) {
enemyProjectiles.shift();
EPdistance.shift();
ourHealth -= 1;
}
//if health is zero, end game
if (ourHealth === 0) {
gameOver = 1;
}
}
}
//draw the health bar
push();
rectMode(CENTER);
noStroke();
fill(0,200,0);
rect(px+psize/2, py-psize/2, ourHealth*5,5);
pop();
//draw our ship
push();
noStroke();
fill(100);
translate(px-37,py-25);
scale(0.15,0.15);
image(imageOne, 0, 0);
pop();
//initial stage specs. Gives instructions
if (clickStart == 0) {
timer = 0;
timerIncrement = 0;
//black background
push();
fill(0);
rect(width/2, height/2, 1000, 1000);
pop();
//design and title
push();
fill(255, 253, 83, 140);
noStroke();
triangle(150, 100, 280, 600, 550, 500);
triangle(110, 100, 20, 500, 130, 500);
triangle(170, 105, 700, 50, 700, 300);
textFont("futura");
textStyle(ITALIC);
fill(0);
textSize(50);
text("Supreme", 500-85, 130-3);
text("Invasion", 541-85, 180-3);
pop();
//decoration
push();
translate(-150,90);
scale(0.6, 0.6);
rotate(-0.5);
image(imageOne, 0, 100);
pop();
//instructions
push();
noStroke();
fill(0,0,200);
fill(255);
textSize(30);
text("click to begin!", width/2+25, 440);
fill(0);
textSize(20);
text("Arrow Keys to move", width/2+5, 390);
text("Or WASD to move", width/2+5, 410);
text("Shift to shoot", width/2-35, 370);
pop();
}
//once clicked, the opening screen is changed
if (mouseIsPressed & clickCount == 0) {
clickCount += 1;
timerIncrement = 1;
clickStart = 1;
}
//if game over is active, then cover screen with message
if (gameOver > 0) {
fill(0);
rect(0,0, width*2, height*2);
fill(255);
noStroke();
textSize(50);
text("Game Over!", width/2, height/2);
textSize(30);
text("Press 'r' to restart", width/2, height/2+50);
push();
translate(350, 10);
scale(0.5, 0.5);
image(imageTwo, 0, 0);
pop();
}
//if end game is activated then cover screen and say end message
if (gameStage == 3) {
fill(0);
rect(width/2, height/2, 1000,1000);
push();
translate(460, 200);
rotate(0.3);
scale(0.8, 0.8);
image(imageThree, 0, 0);
pop();
push();
noStroke();
fill(255);
textSize(50);
text("You Win! :)", width/2, height/2);
textSize(20);
text("Press 'r' to play again", width/2, height/2+50);
pop();
}
}
//Controls======================================================
//==============================================================
//if key is pressed changes the array for controls
function keyPressed() {
if (key == "ArrowRight" || key == "d" || key == "D") {
keys[0] = true;
}
if (key == "ArrowLeft" || key == "a" || key == "A") {
keys[1] = true;
}
if (key == "ArrowUp" || key == "w" || key == "W") {
keys[2] = true;
}
if (key == "ArrowDown" || key == "s" || key == "S") {
keys[3] = true;
}
if (key == "Shift") {
keys[4] = true;
}
//resets game if press r
if (key == "r" || key == "R") {
gameOver = 0;
gameStage = 1;
timer = 0;
timerIncrement = 1;
clickCount = 0;
clickStart = 0;
ourHealth = 10;
px = 0;
py = 240;
}
}
//releases the keys
function keyReleased() {
if (key == "ArrowRight" || key == "d" || key == "D") {
keys[0] = false;
}
if (key == "ArrowLeft" || key == "a" || key == "A") {
keys[1] = false;
}
if (key == "ArrowUp" || key == "w" || key == "W") {
keys[2] = false;
}
if (key == "ArrowDown" || key == "s" || key == "S") {
keys[3] = false;
}
if (key == "Shift") {
keys[4] = false;
}
}
Instructions:
To play the game, click the initial screen to start the game. You can use either the arrow keys or “w” to move up, “a” to move left, “s” to move down, and “d” to move right (WASD). To shoot projectiles press SHIFT. The point of the game is to defeat all enemies. If you hit the enemy with your projectiles their health will go down, and once the enemy’s health hits zero, the enemy is considered defeated. On the second stage, the boss will pull the user towards the right side of the map, so be mindful. Lastly, the user cannot go horizontally beyond certain limits, and once the user reaches the top of the map the user will be teleported to the bottom of the map.
Statements:
For this final project, I focused a lot on the structure and gameplay (the backbone of the game), while my partner focused on the design aspect and some functionality of the game. My partner personally drew all the characters, which made the game look more unique. The code spans around 600 lines, and in my perspective, it was certainly difficult because of the number of objects (4) and their interaction with each other. I made sure to limit the number in arrays to make the gameplay run more smoothly and cleanly.
The gameplay is slightly different from the initial proposal, but for good reason. We initially planned to have three stages in the game, however that either made the game extremely long or difficult. We also didn’t implement a sound source for this game, because we wanted the users to be able to play the game directly in the browser. Despite these minute changes, the main concept and paradigms are true to the original.
All in all, this project was extremely fun to program. It certainly did take hours and hours to get it right, but in the end it all paid off because of the aesthetics and fun gameplay.
]]>For the final project of this class, I’m going to collaborate with Min Jun Kim. We are thinking about doing a simple video game which draws inspiration from Gradius. We are planning to make three different stages of the game: first two are competing against small enemies and the last stage will be against the boss, a black hole. We use be using objects and interactive background and sound. The game will be set in a carefully designed galaxy theme that varies with each stage. There will be multiple different characters to choose from and about 10 kinds of enemy ships.
Inspiration 1: Lighting Changes with SoundThe Company by Nocte at Bring to Light Festival NYC
Nocte is a London-based team of expert that creates and designs light installations for different kinds of situations. They have been commissioned by multiple famous bands and exhibitions to create interactive visual connections that amplify the overall effects of the event. The Company is made up of 76 tungsten lamps the the brightness and on/off switch of each light bulb is interactive with the background music. The overall effect of this project is magnificent and I’d love to see it in person one day.
I think Nocte’s interactive lighting installation will be a good inspiration for my project because it will help me to build a more atmospheric background for my game-based project. I want to make the lighting and graphics of my project change along with the progression the game.
Inspiration 2: City Icon by Marcin Ignac at the Sustainable City Initiative at London in 2012.
Marcin Ignac is a Polish Artist and Programmer based in London. His work often involves visualizing data, creating artificial life forms and combining technology with art. City Icon is a series of six simulations representing different representation of London. This project is very relevant to my project because it can be incorporated into making themes and backgrounds of the game I’m making which is supposedly galaxy based.
]]>The first project is an interactive resume by Robby Leonardi. It’s colorful, fun, and highly interactive. The user scrolls up or down to move the figure through Robby’s resume. It really shows off the designer’s personality. One thing is that there is no link to a portfolio of his actual works/projects.
The second project is another online resume by Charles Richard. Similarly, the user scrolls up or down to navigate through Charles’s resume. However, this project uses more muted colors. One thing I noticed is that he uses circles throughout his project so it’s difficult to tell at a glance which ones are buttons and which ones are simply for aesthetics. Overall, I think both of these projects are fun, interactive ways to display one’s resume.
]]>
For the final project, I would like to create some sort of interactive display or animation for the home page of my personal website. I saw one in which the home screen included an interactive color wheel in which each color block led to a different project once clicked. Another included an animation that told a short story of the portfolio owner’s background/works. For the first idea, a lot would depend on the mouse positions and including images while the second idea would be some sort of generative landscape with mostly nonrandom elements.
What I want to do for the final project is something about music but also has visual effects. I want the project to be interactive, meaning that people can create their own music (probably unconsciously). The detailed effect that I’ve imagined is that in the canvas of ‘universe’, people can create planets every time they click, and each zone of the canvas is related to a related piece of rhythm. By clicking in different areas of the canvas different sound effects are created. The visual effect of the planets needs some planning. I’m thinking that when a point is clicked, some repulsion is generated from this point and shapes are pushed away from the point, and thus a planet is generated. It would be nice if the planets can rotate from its position in a 3D mode and also oscillate with the volume of the rhythm. Other effects might be added to make the project more interactive and playful. I’m really looking forward to it.