MLNI Week 4: Posenet Interactive Game – Alex Wang

Task:

Create a screen-based interactive game that utilizes “abstract” buttons and poseNet. The sketch needs to demonstrate proper use of object-oriented programming concept as well.

Inspiration:

I wanted to create a space shooter/space invader type game where the player controls the spaceship which shoots bullets to hit targets.

I plan on utilizing Posenet by using face coordinates to control the space ship.

Final Product:

Progress:

I started off by creating a spaceship, as soon as Posenet detects a face it will use the nose coordinates to create a ellipse that fires particles. which is defined as a class named particle()

Then I made it more game like by adding a new class named enemy(). By shooting the enemy with particles, they will be removed from the game and score will go up.

I then added the ability to fire at an angle by tilting your head. I achieved this through calculating the difference between the y value of the left and right eye, then I smooth out the difference value by dividing so that it is not sensitive. The player should be able to shoot straight upwards without keeping their head perfectly still.

The last change I made to the game is adding an “abstract button”, I decided to change the game so that particles would only be fired when the right hand is held upwards.

Reflection:

I am very surprised by how much interaction and immersion posenet can bring to a simple game by setting the controls to human movement as opposed to keyboard/mouse interaction. I ran into a lot of problems when creating this project, I wanted to add sound and sprites to the project to make it look more polished, but my computer/browser does not load things properly. The macbook’s camera also turns off from time to time making debugging difficult. The mechanic of firing with hand position is also very glitchy cause it either gets in the way of face detection, or it goes outside of the frame captured by the camera. Aside from the problems, I think just with what I have right now it is already a game that is fun to play. I believe by adding sound/sprites and more levels/power-ups/types of enemies, this can be a really fun game.

Final Code:

console.log("ml - ",ml5.version);
let particles = [];
let targets = [];
let cam;
let img;
let poses = [];
var timer = 0;
let score = 0;

function setup(){
createCanvas(700,600);

background(0);
cam = createCapture(VIDEO);

//img.resize(10, 10);
//imageMode(CENTER);
//cam.size(1000,800);
//cam.hide();
textSize(32);
poseNet = ml5.poseNet(cam,modelReady);
poseNet.on('pose',function(results){poses = results});

}

function modelReady(){
console.log("model loaded");
}

function draw(){

timer +=1;
if (timer >=100){
timer=0;
targets.push(new enemy());
}
background(0);
fill(255,255,255);
text('score: '+score, width/2-50,50 );
//image(img, 0, 0);
//image(cam,0,0);
//tint(0,0,0,100);
for (let i = 0; i<poses.length;i++ ){
//console.log(poses[i].pose.nose);

//particles.push(new Particle(poses[i].pose.nose.x,poses[i].pose.nose.y));
console.log(poses[i]);
//image(img,width - poses[i].pose.nose.x,height*3/4);
ellipse(width - poses[i].pose.nose.x,height*3/4,10,10);
fill(0,0,255);
//ellipse(width - poses[i].pose.rightEye.x,poses[i].pose.rightEye.y,10,10);
//ellipse(width - poses[i].pose.leftEye.x,poses[i].pose.leftEye.y,10,10);
//ellipse(width - poses[i].pose.rightWrist.x,poses[i].pose.rightWrist.y,10,10);
//ellipse(width - poses[i].pose.leftWrist.x,poses[i].pose.leftWrist.y,10,10);
var angle = poses[i].pose.rightEye.y - poses[i].pose.leftEye.y;

angle = int(angle/10);
console.log(angle);
if (poses[i].pose.rightWrist.y < 350){
particles.push(new Particle(width - poses[i].pose.nose.x,height*3/4,angle));
}

}

//console.log(particles.length);
//particles.push(new Particle(width/2,height/2));
for (let i = 0; i<particles.length;i++ ){
particles[i].move();
for (let i = 0; i<poses.length;i++ ){
//particles[i].checkinteraction(width-poses[i].pose.nose.x,height*3/4);
}

particles[i].display();
}

for (let i = particles.length-1; i>=0;i-- ){
particles[i].checkedge();
if (particles[i].isDone){
particles.splice(i,1);
}
}

//console.log(targets);
for (let i = targets.length-1; i>=0;i-- ){
targets[i].checkedge();
targets[i].move();
targets[i].display();
if (targets[i].isDone){
targets.splice(i,1);
}
}

//check collision
for (let i = targets.length-1; i>=0;i-- ){
for (let b = particles.length-1; b>=0;b-- ){
check(targets[i],particles[b])
}
}
while (particles.length >500){
particles.splice(0,1); //index,num of element
}

}

function check(target,bullet){
let distance = dist(target.x,target.y,bullet.x,bullet.y);
if (distance <= 17){
target.isDone = true;
bullet.isDone = true;
score += 100;
}
}

function mousePressed(){
//particles.push(new Particle(random(width),height-10));

}

class enemy{
constructor(){
this.x = random(10,width-10);
this.y = 5;
this.xspd = 0;
this.yspd = 2;
this.rad = 20;
this.isDone = false;
}
move(){
this.x += this.xspd;
this.y += this.yspd;
}
checkedge(){
if (this.x < 0 || this.x > width){
this.isDone = true;
}
if (this.y < 0 || this.y > height){
this.isDone = true;
}
}

display(){
push();
strokeWeight(0);
fill(0,0,255);
ellipse(this.x,this.y,this.rad*2,this.rad*2);
pop();
}
}

class Particle {
constructor(x,y,angle) {
this.x = x;
this.y = y;
this.xspd = angle;
this.yspd = -5;
this.rad = 2;
this.color = [random(200,255),random(50,200),random(10),random(100,300)];
this.isDone = false;
}

checkinteraction(x,y){
let distance = dist(this.x,this.y,x,y);
if (distance < this.rad + 25){
this.color = [0,0,255];

}
else{
this.color = [distance,(distance/4)+200,255];
}
}
move(){
this.x += this.xspd;
this.y += this.yspd;
}

checkedge(){
if (this.x < 0 || this.x > width){
this.isDone = true;
}
if (this.y < 0 || this.y > height){
this.isDone = true;
}
}

display(){
push();
strokeWeight(0);
fill(this.color[0],this.color[1],this.color[2]);
ellipse(this.x,this.y,this.rad*2,this.rad*2);
pop();
}
}

MLNI Week 4 HW Jessica

Summary

For the homework this week, I wanted to make a simple game where the user uses their nose to control the frog’s tongue and eat flies. The final product I have is below. When the frog eats a fly, a sound effect goes off and the fly turns red and disappears to indicate the user successfully ate the fly. 

Discussion

I came across a lot of difficulties while coding for this assignment. The first issue was getting the flies to react to the tongue touching them by using my nose. I was confused because the code would work fine when I used my mouse, however after talking to Professor Moon, we were able to figure out that I had issues with my camera code. Initially I set the camera size too large which Moon explained to me that this can cause the tongue to not only be laggier, but also skew the nose detection.

He also helped me make my flies appear less frequently and rapidly by showing me the if (random(1) < 0.03) which makes the info within the if statement appear only 3% of the time.

After resolving these issues, I spent a lot of time trying to make the flies look like flies however this was difficult for me because it was hard to create drawings when the shapes generate in places randomly and are different sizes.

Since I wanted it to be clear that these were flies, I wanted to insert sound effects such as fly buzzing and eating sound effects. I was able to make a Yoshi eating sound when the tongue touches the fly and also had the buzzing sound working but I decided to take it out because it got too annoying to listen to constantly. 

Conclusion

It’s been very cool being able to utilize my face rather than the keyboard of mouse to create interaction, however there is definitely a lot of more practice I need to become comfortable and familiar with the coding and getting it to work. Also, I believe I would like to practice drawing more so that I can make more immersive content in the future. 

Week 4: Buttons-N-PoseNet Eszter Vigh

This was so hard to load! So I ended up having issues loading the background music. Originally I was going to have the goat/dinosaur sound interrupt the nice music, but for whatever reason the larger sound file was not found. I always got the Error 404 message… I still included it in the zip file, but honestly, I settled for the sound playing with the sprites. 

I thought challenging myself with creating sprites would up the difficulty since you can no longer use radius as a measurement. 

The sounds get bugged at times, but it works most of the time. I also started playing around with the background as another way of interaction. So the background squares appear/fade based on your nose location. 

This is where my project is linked

This is my script.js:

console.log(“ml5 version”, ml5.version);
let cam;
let poseNet;
let poses = [];
let noseX, noseY;
let dinosaurs = [];
var dino;
let goats = [];
var goat;
function preload()
{
// load images
dino = loadImage(“dino.png”);
goat = loadImage(“goat.png”);
}
function setup() {
createCanvas(640,480);
background(“lavender”);

// loadImage();
// createImg();
cam = createCapture(VIDEO);
cam.size(640,480);
cam.hide();
soundFormats(‘mp3’, ‘m4a’);
song = loadSound(‘dino.mp3’)
moo = loadSound(‘goat.m4a’)
// soundtrack = loadSound(‘bendsound-buddy.mp3’);
//poseNet
poseNet = ml5.poseNet(cam,modelReady);
poseNet.on(‘pose’, function(results){
poses = results;
} );

}

function modelReady() {
console.log(“Model Loaded!!!!”);
}

function draw() {
// image(cam,0,0); //vidObj,x,y,(W),(h)
background(“lavender”);
// soundtrack.play();
let r1 = map(noseX, 0, width, 0, height);
let r2 = height – r1;
// class Square {
// constructor(x,y){
// this.x = x;
// this.y = y;
// this.width = random(20,100);
// this.height = random(10,150);
// this.r = random(255);
// this.g = random(255);
// this.b = random(255);
// }
// }

fill(66, 206, 244, r1);
rect(0 + r1 / 2, 0, r1, r1);

fill(244, 220, 65, r2);
rect(200 – r2 / 2, 0, r2, r2);

fill(44, 22, 65, r1);
rect(400 – r1 / 2, 300, r1, r1);

fill(44, 220, 165, r2);
rect(600 – r2 / 2, 100, r2, r2);

fill(144, 220, 265, r1);
rect(300 – r1 / 2, 100, r1, r1);

fill(144, 0, 65, r2);
rect(0 – r2 / 2, 300, r2, r2);

fill(144, 220, 65, r1);
rect(500 – r1 / 2, 0, r1, r1);

fill(244, 22, 0, r1);
rect(500 – r1 / 2, 400, r1, r1);

fill(244, 0, 65, r2);
rect(300 – r2 / 2, 0, r2, r2);

if (random(1) < 0.10) { // only 10%
dinosaurs.push( new Dinosaur(width/2, height) );
}

// update & display
for (let i=0; i<dinosaurs.length; i++) {
let p = dinosaurs[i];
p.move();
// p.fall();
p.updateLifespan();
p.checkEdges();
p.checkInteraction(noseX, noseY);
p.display();
}

// remove dinosaurs if done!
for (let i = dinosaurs.length-1; i >= 0; i–) {
let p = dinosaurs[i];
if ( p.isDone ) {
dinosaurs.splice(i, 1);
}
}

// limit the number of dinosaurs
while (dinosaurs.length > 7) {
dinosaurs.splice(0, 1); //(index, howMany)
}

// check the number of dinosaurs
fill(255);
text( dinosaurs.length, 10, 20 );
//Goats
if (random(1) < 0.10) { // only 10%
goats.push( new Goat(width/2, height) );
}

// update & display
for (let i=0; i<goats.length; i++) {
let w = goats[i];
w.move();
// p.fall();
w.updateLifespan();
w.checkEdges();
w.checkInteraction(noseX, noseY);
w.display();
}

//Goats
for (let i = goats.length-1; i >= 0; i–) {
let w = goats[i];
if ( w.isDone ) {
goats.splice(i, 1);
}
}

// limit the number of goats
while (goats.length > 5) {
goats.splice(0, 1); //(index, howMany)
}

// check the number of goats
fill(0);
text( goats.length, 10, 200 );

for (let i = 0; i < poses.length; i++) {
let pose = poses[i].pose;
// console.log(poses[i].pose.keypoints);
for (let k = 0; k < pose.keypoints.length; k++) {
// console.log(pose.keypoints[k]);
let p = pose.keypoints[k];
if(p.part == “nose”){
noseX = p.position.x;
noseY = p.position.y;
noStroke();
fill(0,0,255);
ellipse(noseX, noseY, 10, 10);
//console.log(p);
}

// text(p.part, x+15,y+0);
}
}
}

class Dinosaur {
constructor(x, y) {
this.x = x;
this.y = y;
this.xSpd = random(-2, 2);
this.ySpd = random(-5, -3);
this.rad = dino.width ;
// this.r = random(255);
// this.g = random(255);
// this.b = random(255);
this.isDone = false;
this.lifespan = 10.0; // 100%
this.lifeReduction = random(0.001, 0.010); // 0.1% to 1%
}
updateLifespan() {
if (this.lifespan < 0.0) {
this.lifespan = 0.0;
this.isDone = true;
} else {
this.lifespan -= this.lifeReduction;
}
}

// fall() {
// this.ySpd += 0.03;
// }
checkInteraction(x, y) {
let distance = dist(this.x, this.y, x, y); //(x1, y1, x2, y2)
if (distance < this.rad) {
// in the area
// this.r = 255;
// this.g = 255;
// this.b = 0;
song.play();
this.isDone = true

} else {
// out of area
// this.r = 255;
// this.g = 255;
// this.b = 255;
song.stop();
}

}
move() {
this.x += this.xSpd; // this.x = this.x + this.xSpd;
this.y += this.ySpd;
}
checkEdges() {
if (this.x < 0 || this.x > width) {
this.isDone = true;
}
if (this.y < 0 || this.y > height) {
this.isDone = true;
}
}
display() {
push();
noStroke();
// fill(this.r, this.g, this.b, 255);
// ellipse(this.x, this.y, this.rad*2 , this.rad*2);
image(dino, this.x, this.y);
pop();
}
}

class Goat {
constructor(x, y) {
this.x = x;
this.y = y;
this.xSpd = random(-2, 2);
this.ySpd = random(-5, -3);
this.rad = goat.height;
// this.r = random(255);
// this.g = random(255);
// this.b = random(255);
this.isDone = false;
this.lifespan = 10.0; // 100%
this.lifeReduction = random(0.001, 0.010); // 0.1% to 1%
}
updateLifespan() {
if (this.lifespan < 0.0) {
this.lifespan = 0.0;
this.isDone = true;
} else {
this.lifespan -= this.lifeReduction;
}
}

fall() {
this.ySpd += 0.03;
}
checkInteraction(x, y) {
let distance = dist(this.x, this.y, x, y); //(x1, y1, x2, y2)
if (distance < this.rad) {
// in the area
// this.r = 255;
// this.g = 255;
// this.b = 0;
moo.play();
this.isDone = true

} else {
// out of area
// this.r = 255;
// this.g = 255;
// this.b = 255;
moo.stop();
}

}
move() {
this.x += this.xSpd; // this.x = this.x + this.xSpd;
this.y += this.ySpd;
}
checkEdges() {
if (this.x < 0 || this.x > width) {
this.isDone = true;
}
if (this.y < 0 || this.y > height) {
this.isDone = true;
}
}
display() {
push();
noStroke();
// fill(this.r, this.g, this.b, 255);
// ellipse(this.x, this.y, this.rad*2 , this.rad*2);
image(goat, this.x, this.y);
pop();
}
}

Week 4 MLNI: Interactive game PoseNet (Crystal Liu)

Introduction

For this week’s assignment, I made a firecracker. To fire it, the players need to move their nose to get close to the firecracker fuse, since I have put the shape of fire on their nose by PoseNet. Once their nose is close enough to the fuse, the sparks appear and shoot vertically upward. Also if you move your mouse to the sparks, the color of it will change from golden yellow to blue; if you click the mouse, it then will turn to black, the same as the background, and then disappear.

The inspiration of this project is Brandon’s work from last week. His smoke was really beautiful and it reminded me of the beauty of sparks. Thus I decided to create a project of firecracker. The players can use their noses to light it and then use the mouse to change the color of it. In this way, the nose is the abstract button and the interaction is based on the mouse and the screen.

Technique problem

At first I wanted to achieve this effect: once you have successfully lightened the firecracker, you clicked the mouse and then the fire on your nose would keep staying on the fuse and the sparks would keep shooting. However, I cannot let it stay on it, so the users need to keep the nose within range so that the spark can continue to appear. After several trials, when I click the mouse, there would be a fire on the fuse but the fire on my nose could not disappear and it looked really weird so I gave up this effect and then tried to use the mouse to change the color of the sparks and finally got the final version.