xxxxxxxxxx
777
/////////////SCENE CONTROL
let scene = 0;
/////////////DIALOG BOX VARIABLES
let rectYoffset = 27;
/////////////SOUNDS
let textSound;
let kissSound;
/////////////FILTER VARIABLES
let filters = [];
let filterLabels = [
"poly",
"queer",
"with schedule overlap",
"emotionally available",
"creative",
"silly",
"morbid",
"kind",
"funny",
"smart"
];
let filtersPicked = [];
let wiggleX = 0;
let wiggleY = 0;
let filterColor;
let scene1text;
///////////////TEXTING VARIABLES
let asemicFont;
let txtFrames = [];
let frame = 0;
let health = 250/2;
let maxHealth = 250;
let rectW;
let rectH;
let rectX;
let rectY;
let rect2H;
let rect2Y;
let rect2Xoffset;
let rect2Yoffset;
let healthFill;
let warning;
let keyPresses = 1;
let txts = [];
let txtW;
let txtContents = [];
let txtColor;
let otherTxtCount = 1;
let otherTxts = [];
let otherTxtW;
let otherTxtContents = [];
let otherTxtColor;
let timer = 500;
let nextChange = timer; //syncs the timer and change rate
let successPts = 0;
let success = 225;
//////////////KISSY KISSY VARIABLES
let facemesh;
let video;
let predictions = [];
let xpos, ypos; // Starting position of shape
let xspeed = 7; // Speed of the shape
let yspeed = 7; // Speed of the shape
let xdirection = 1; // Left or Right
let ydirection = 1; // Top to Bottom
let otherMouth;
////////////////////////////PRELOAD & SETUP
function preload() { //runs once
asemicFont = loadFont('Orchard-Linear.otf');
textSound = loadSound('sounds/hannahj40-sendtext.mp3');
kissSound = loadSound('sounds/stereostereo-kiss.mp3');
for (i = 0; i < 4; i++) {
txtFrames[i] = loadImage("frames/texting"+str(i)+".gif");
}
}
function setup() {
createCanvas(600, 400);
//frameRate(20);
//create filters based on how many labels there are
filterColor = color(255, 0, 255, 50);
for (i = 0; i < filterLabels.length; i++) {
let r = random(100, 250);
filters[i] = new Filter(r,random(r / 3, width - r / 3),random(r / 3, height - r / 3),filterLabels[i],wiggleX,wiggleY,filterColor);
}
//set text bubble color
txtColor = color(0, 200, 255);
otherTxtColor = color(200);
//set up kissy kissy
kissySetup();
}
//////////////////////////DRAW
function draw() {
switch (scene) {
case 0:
scene0();
break;
case 1:
scene1();
break;
case 2:
scene2();
break;
case 3:
scene3();
break;
case 4:
scene4();
break;
case 5:
scene5();
break;
case 6:
scene6();
break;
case 7:
scene7();
break;
}
}
//////////////////////////STAGE 0: INTRO
function scene0() {
blendMode(BLEND);
background(0);
fill(0, 255, 0);
textAlign(CENTER,CENTER);
text("let's find someone\nto go on a cute date with",width/2,height/2);
rectMode(CENTER);
stroke(0,255,0);
noFill();
rect(width/2, height/2 + rectYoffset, 25, 15);
noStroke();
fill(0,255,0);
text("OK", width/2, height/2 + rectYoffset);
if (mouseX > width/2 - 25/2 && mouseX < width/2 + 25/2 && mouseY > height/2+rectYoffset - 15/2 && mouseY < height/2+rectYoffset + 15/2) {
rectMode(CENTER);
fill(0,255,0);
noStroke();
rect(width/2, height/2 + rectYoffset, 25, 15);
fill(0);
text("OK", width/2, height/2 + rectYoffset);
if(scene == 0 && mouseIsPressed) {
scene = 1;
}
}
//instructions
fill(255,255,0);
noStroke();
textAlign(LEFT);
text("D A T I N G S I M",5,10);
text("by gabriel lee",525,10);
text('may take a moment to load',5,height-10);
text("thanks for your patience",467,height-10);
}
//////////////////////////STAGE 1: FILTERS
function scene1() {
background(220);
//for every category, make a filter
for (i = 0; i < filterLabels.length; i++) {
filters[i].make();
//then check it against every other filter except itself, then check those too
for (let j = 0; j < filters.length; j++) {
for (k = 0; k < filters.length; k++) {
//if they overlap and the mouse clicks on the overlap
if (i != j && i != k && j != k && filters[i].overlaps(filters[j], filters[k]) && mouseIsPressed) {
//pass the filter labels to the global variable for use in next scene
scene1text=str("cool, someone "+filters[i].label+" & "+filters[j].label+" & "+filters[k].label+"! \nlet's text a bit");
//and go to the next scene
if (scene == 1 && millis() > 10000) {
scene = 2;
}
}
}
}
}
//instructions
fill(0);
textAlign(LEFT);
text('click on three overlapping filters',10,height-10);
}
//////////////////////////STAGE 2: INTRO
function scene2() {
blendMode(BLEND);
background(0);
//write the text
fill(0, 255, 0);
textAlign(CENTER,CENTER);
text(scene1text,width/2,height/2);
//create the OK button
rectMode(CENTER);
stroke(0,255,0);
noFill();
rect(width/2, height/2 + rectYoffset, 25, 15);
noStroke();
fill(0,255,0);
text("OK", width/2, height/2 + rectYoffset);
//if hovering button, invert color
if (mouseX > width/2 - (25/2) && mouseX < width/2 + 25/2 && mouseY > height/2+rectYoffset-15/2 && mouseY < height/2+rectYoffset + 15/2) {
rectMode(CENTER);
fill(0,255,0);
noStroke();
rect(width/2, height/2 + rectYoffset, 25, 15);
fill(0);
text("OK", width/2, height/2 + rectYoffset);
//if button pressed, new scene
if(scene == 2 && mouseIsPressed) {
scene++;
}
}
}
//////////////////////////STAGE 3: TEXTING
function scene3() {
frameRate(14);
background(240);
if(keyIsPressed){
tint(255,150);
image(txtFrames[frame],150,-300);
if(frame==3){
frame=0;
} else {
frame++;
}
}
//set up the first text
txts[0] = new Txt(width/2,200,random(50,75),25,"randomtext",txtColor);
//every time you press the key, add a text to the array, if enough time has passed
if(keyIsPressed && millis() > nextChange) {
txtW = random(50,75);
//for 1/6th of the text bubble width, generate random characters
for(i=0;i<txtW/6;i++) {
txtContents += char(int(random(65,65+24)));
//if the other txt is below 13 characters and you're in the safe zone, generate the other txt message
if(otherTxtContents.length <= 13 && health > rectW/3 && health < 2*rectW/3) {
otherTxtW = random(50,75);
otherTxtContents += char(int(random(65,65+24)));
//otherwise, ellipse
} else if (health <= rectW/3 || health >= 2*rectW/3) {
otherTxtW = 30;
otherTxtContents = ". . .";
}
}
//if health is outside safe zone, color bubble red; otherwise blue
if ((health >= 0 && health < rectW/3) || (health > 2*rectW/3 && health <= rectW)) {
txtColor = color(255,10,10);
} else if (health >= rectW/3 && health <= 2*rectW/3 ) {
txtColor = color(0, 200, 255);
}
//create a new txt and other txt
txts[keyPresses] = new Txt(width/2,200,random(50,75),25,txtContents,txtColor);
otherTxts[keyPresses] = new otherTxt(width/2,200,otherTxtW,25,otherTxtContents);
keyPresses++;
setInterval(textSound.play(),1000);
nextChange = millis() + timer;
}
//for each time the key is pressed, make a new Txt and clear the previous text message content
for(j=1; j<keyPresses; j++) {
txts[j].make();
txtContents=[];
// if(txts[j].y>=-25) {
txts[j].move(); //}
// else if (txts[j].y<-25) {
// txts.splice(j,1);
// }
otherTxts[j].make();
otherTxtContents=[];
otherTxts[j].move();
}
healthBar();
//instructions
fill(0);
noStroke();
textAlign(LEFT);
text('text them by pressing any key',10,height-10);
//create win condition
if (successPts >= success) {
scene = 5;
}
if (health == 0 || health == rectW) {
scene = 4;
}
}
//////////////////////////STAGE 4: TEXTING FAILURE
function scene4() {
blendMode(BLEND);
//reset filters and health
scene1text="";
for(i=1;i<txts.length;i++) {
txts[i].y=-100;
otherTxts[i].y=-100;
}
health = 250/2;
background(0);
fill(0, 255, 0);
textAlign(CENTER,CENTER);
text("god why are you so weird? \n try again",width/2,height/2);
rectMode(CENTER);
stroke(0,255,0);
strokeJoin(MITER);
strokeWeight(1);
noFill();
rect(width/2, height/2 + rectYoffset, 25, 15);
noStroke();
fill(0,255,0);
text("):", width/2, height/2 + rectYoffset);
if (mouseX > width/2 - 25/2 && mouseX < width/2 + 25/2 && mouseY > height/2+rectYoffset-15/2 && mouseY < height/2+rectYoffset + 15/2) {
rectMode(CENTER);
fill(0,255,0);
noStroke();
rect(width/2, height/2 + rectYoffset, 25, 15);
fill(0);
text("):", width/2, height/2 + rectYoffset);
if(scene == 4 && mouseIsPressed) {
scene = 1;
}
}
}
//////////////////////////STAGE 5: INTRO
function scene5() {
blendMode(BLEND);
background(0);
fill(0, 255, 0);
textAlign(CENTER,CENTER);
text("you're pretty into each other \n time to go on a date!",width/2,height/2);
rectMode(CENTER);
stroke(0,255,0);
strokeJoin(MITER);
strokeWeight(1);
noFill();
rect(width/2, height/2 + rectYoffset, 25, 15);
noStroke();
fill(0,255,0);
text("OK", width/2, height/2 + rectYoffset);
if (mouseX > width/2 - 25/2 && mouseX < width/2 + 25/2 && mouseY > height/2+rectYoffset-15/2 && mouseY < height/2+rectYoffset + 15/2) {
rectMode(CENTER);
fill(0,255,0);
noStroke();
rect(width/2, height/2 + rectYoffset, 25, 15);
fill(0);
text("OK", width/2, height/2 + rectYoffset);
if(scene == 5 && mouseIsPressed) {
scene = 6;
}
}
}
//////////////////////////STAGE 6: KISSY KISSY
function scene6() {
background(30);
drawOtherMouth();
drawMouth();
//instructions
fill(255,255,0);
noStroke();
textAlign(LEFT);
text("wow, it's kinda dark in here. but date's going great, let's make out",10,height-10);
}
//////////////////////////STAGE 7: YOU WON
function scene7() {
background(30);
textAlign(CENTER,CENTER);
fill(0,255,0);
text("(⁄ ⁄•⁄ω⁄•⁄ ⁄) wow!\nwhat a great date",width/2,height/2);
rectMode(CENTER);
stroke(0,255,0);
noFill();
rect(width/2, height/2 + rectYoffset, 30, 15);
noStroke();
fill(0,255,0);
text("END", width/2, height/2 + rectYoffset);
if (mouseX > width/2 - 30/2 && mouseX < width/2 + 30/2 && mouseY > height/2+rectYoffset-15/2 && mouseY < height/2+rectYoffset + 15/2) {
rectMode(CENTER);
fill(0,255,0);
noStroke();
rect(width/2, height/2 + rectYoffset, 30, 15);
fill(0);
text("END", width/2, height/2 + rectYoffset);
if(scene == 7 && mouseIsPressed) {
scene = 0;
}
}
}
class Filter {
constructor(r, x, y, label, wiggleX, wiggleY, filterColor) {
this.r = r;
this.x = x;
this.y = y;
this.label = label;
this.wiggleX = wiggleX;
this.wiggleY = wiggleY;
this.filterColor = filterColor;
this.overlaps = function (other1, other2) {
// if the mouse is less than half the radius from all three filters' center, return true
return (
dist(mouseX, mouseY, this.x, this.y) < this.r / 2 &&
dist(mouseX, mouseY, other1.x, other1.y) < other1.r / 2 &&
dist(mouseX, mouseY, other2.x, other2.y) < other2.r / 2
);
};
}
make() {
push();
//wiggle if you get within 2*radius, otherwise stop wiggling
if (dist(mouseX, mouseY, this.x, this.y) < this.r * 2 && mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
this.wiggleX = map(dist(mouseX, mouseY, this.x, this.y),0,width - this.x,0,90,true);
this.wiggleY = map(dist(mouseX, mouseY, this.x, this.y),0,height - this.y,90,0,true);
} else if (dist(mouseX, mouseY, this.x, this.y) > this.r * 2) {
this.wiggleX = 0;
this.wiggleY = 0;
}
translate(this.x + noise(frameCount) * this.wiggleX,this.y + noise(frameCount) * this.wiggleY);
blendMode(MULTIPLY);
fill(this.filterColor);
stroke(0,100);
circle(0, 0, this.r);
blendMode(MULTIPLY);
fill(0, 150);
noStroke();
textAlign(CENTER, CENTER);
textSize(12);
text(this.label, 0, 0);
pop();
}
}
class Txt {
constructor(x,y,w,h,txt,txtColor) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.txt = txt;
this.txtColor = txtColor;
}
make() {
push();
translate(this.x+50, this.y+20);
fill(txtColor);
stroke(240);
strokeWeight(2);
rectMode(RADIUS);
rect(0,0,this.w,this.h,50,50);
noStroke();
triangle(this.w*0.5,this.h/2,this.w*0.8,this.h/2,this.w*0.8,this.h/2+20);
textAlign(CENTER,CENTER);
textFont(asemicFont);
fill(0);
stroke(0);
strokeWeight(1);
text(this.txt,0,-5);
pop();
}
move() {
this.y-=5;
}
}
class otherTxt {
constructor(x,y,w,h,otherTxt) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.otherTxt = otherTxt;
}
make() {
push();
translate(this.x-50, this.y+50);
fill(200);
stroke(240);
strokeWeight(2);
rectMode(RADIUS);
rect(0,0,this.w,this.h,50,50);
noStroke();
triangle(this.w*-0.7,this.h/2,this.w*-0.4,this.h/2,this.w*-0.7,this.h/2+20);
textAlign(CENTER,CENTER);
textFont(asemicFont);
fill(0);
stroke(0);
strokeWeight(1);
text(this.otherTxt,0,-5);
pop();
}
move() {
this.y-=5;
}
}
function healthBar() {
//set bg bar & health bar parameters
rectMode(CORNER);
rectW = 250;
rectH = 35;
rectX = (width-rectW)/2;
rectY = 320;
//health bar height and y based on bg bar
rect2H = rectH - 15;
rect2Y = rectY + ((rectH-rect2H)/2);
//if the health is more than 0, decrease it
if(health > 0) {
health = min(maxHealth, --health);
}
//map the color scale to left and right ends of health bar
lerpScaleLeft = map(health, 0.25*rectW, 100, 0, 1, true);
lerpScaleRight = map(health, 150, 0.75*rectW, 0, 1, true);
//add or remove success points
//if health is in left third, shade to red and add wiggle and warning
if (health >= 0 && health < maxHealth/3) {
healthFill = lerpColor(color(255,10,10),color(0,200,255),lerpScaleLeft);
warning = "too slow to respond! you're bringing up their old ghosts";
warningSigns();
if(successPts>0) {
successPts--;
}
//if health is in right third, shade to red and add wiggle
} else if (health > 2*maxHealth/3 && health <= maxHealth) {
healthFill = lerpColor(color(0,200,255),color(255,10,10),lerpScaleRight);
warning = "wow you're being Too Much and freaking them out";
warningSigns();
if(successPts>0) {
successPts--;
}
//if health is in safe zone, fill health bar and text color blue
} else if (health >= maxHealth/3 && health <= 2*maxHealth/3 ) {
healthFill = color(0, 200, 255);
txtColor = color(0, 200, 255);
successPts++;
}
//draw bg bar
fill(0);
noStroke();
rect(rectX+rect2Xoffset,rectY+rect2Yoffset,rectW,rectH);
//draw safe zone indicator
// noFill();
// stroke(0,200,255);
// rect(rectX+rectW/3,rectY+rect2Yoffset - 5,rectW/3,rectH + 10)
//box labels
noStroke();
fill(0,200,255);
textAlign(CENTER);
text("stay in the safe zone", width/2+rect2Xoffset,rectY+rect2Yoffset-12);
//draw the health bar
fill(healthFill);
rect(rectX+rect2Xoffset,rect2Y+rect2Yoffset,map(health, 0, maxHealth, 0, rectW, true),rect2H);
if(keyIsPressed) {
health+=3;
}
//map successpts to percentage to display in bottom right
percent = int(map(successPts, 0, success, 0, 100));
//success meter
fill(0);
noStroke();
textAlign(LEFT);
text(percent+'%',width-35,height-10);
}
function warningSigns() {
//map the wiggle multiplier to the health bar's relative distance from center
healthMult = map(health,0,rectW,-15,15);
rect2Xoffset = noise(millis())*healthMult;
rect2Yoffset = noise(millis())*healthMult;
// noStroke();
fill(176, 0, 0);
strokeJoin(ROUND);
stroke(240);
strokeWeight(7);
textAlign(CENTER);
text(warning, width/2,40);
//make text bubble color red
//txtColor = color(255,0,0);
}
function kissySetup() {
video = createCapture(VIDEO);
video.size(width, height);
facemesh = ml5.facemesh(video, modelReady);
// This sets up an event that fills the global variable "predictions"
// with an array every time new predictions are made
facemesh.on("predict", results => {
predictions = results;
});
// Hide the video element, and just show the canvas
video.hide();
xpos = width / 2;
ypos = height / 2;
otherMouth = loadImage('mouth.png');
}
function modelReady() {
console.log("Model ready!");
}
function drawMouth() {
for (let i = 0; i < predictions.length; i += 1) {
// get the points from annotations having to do with the mouth:
let lipsUpperOuter = predictions[i].annotations.lipsUpperOuter;
let lipsLowerOuter = predictions[i].annotations.lipsLowerOuter;
let lipsUpperInner = predictions[i].annotations.lipsUpperInner;
let lipsLowerInner = predictions[i].annotations.lipsLowerInner;
fill(193, 110, 110);
noStroke();
beginShape();
// draw the outer shape of the mouth/lips.
// outer shape must be clockwise. start with upper outer lip, then
// do lower outer lip (backwards through array)
for (let i = 0; i < lipsUpperOuter.length; i++) {
const [x, y] = lipsUpperOuter[i];
vertex(x, y);
}
for (let i = lipsLowerOuter.length - 1; i >= 0; i--) {
const [x, y] = lipsLowerOuter[i];
vertex(x, y);
}
// cut out a hole for the inner shape of the mouth.
// contour takes a shape out of the main shape (creates a hole).
// contour must window counterclockwise. start with lower,
// then do outer lip (backwards through array)
beginContour();
for (let i = 0; i < lipsLowerInner.length; i++) {
const [x, y] = lipsLowerInner[i];
vertex(x, y);
if(dist(x,y,xpos,ypos)<15) {
scene = 7;
}
}
for (let i = lipsUpperInner.length - 1; i >= 0; i--) {
const [x, y] = lipsUpperInner[i];
vertex(x, y);
}
endContour();
endShape();
}
}
function drawOtherMouth() {
// Update the position of the shape
xpos = xpos + xspeed * xdirection;
ypos = ypos + yspeed * ydirection;
// Test to see if the shape exceeds the boundaries of the screen
// If it does, reverse its direction by multiplying by -1
if (xpos > width - 47 || xpos < 0) {
xdirection *= -1;
kissSound.play();
}
if (ypos > height-33 || ypos < 0) {
ydirection *= -1;
kissSound.play();
}
mouth(xpos, ypos);
}
function mouth(xpos, ypos) {
push();
translate(xpos,ypos);
image(otherMouth, 0, 0);
pop();
}
//////////////////////////CREDITS
//asemic font is Orchard Linear by Rory King at https://rorykingetc.com/dasickfonts
//health bar from manno: https://editor.p5js.org/manno/sketches/iX61TJgRv
//intersection from kjhollen: https://editor.p5js.org/kjhollen/sketches/tCR9KLNBE
//event timer from http://learn.digitalharbor.org/courses/creative-programming/lessons/using-timers-in-p5-js/
//facemesh lips from kjhollen at https://editor.p5js.org/kjhollen/sketches/97PAbjXhS
//lips bouncing from https://p5js.org/examples/motion-bounce.html
//text sound from hannahj40 on freesound.org at https://freesound.org/people/hannahj40/sounds/271543/
//kiss sound from stereostereo on freesound.org at https://freesound.org/people/stereostereo/sounds/117340/