xxxxxxxxxx
165
// Todo
// Code cleanup
// Fix trigger
// Add recoil-like feedback
let handPose;
let myVideo;
let myResults;
let marks = []; // bullet marks
let pos = [];
let fireTracker = [];
const markCount = 500;
const defaultMultiplier = 2.5;
const ALPHA = 0.2;
function setup() {
createCanvas(640, 480);
myVideo = createCapture(VIDEO);
myVideo.size(width, height);
myVideo.hide();
handPose = ml5.handpose();
handPose.detectStart(myVideo, gotResults);
strokeWeight(12);
fill(0);
stroke(0);
console.clear();
}
function gotResults(results) {
myResults = results;
}
function draw() {
background(220, 220, 220);
translate(width, 0); // move to far corner
scale(-1.0, 1.0); // flip x-axis backwards
drawMarks();
if (myResults) {
if (pos) {
processPoints();
}
updatePos();
}
}
function processPoints() {
for (let i = 0; i < pos.length; i++) {
const kp = pos[i];
const mrp = myResults.length > i ? myResults[i].keypoints : kp;
const p = getNewPoint(kp[5], kp[8], defaultMultiplier);
const g = new Gun();
if (typeof fireTracker[i] !== "boolean") {
fireTracker[i] = true;
}
g.detectTrigger(kp[4], kp[5], kp[6], mrp[3], kp[8], i);
g.drawGun(kp[5], kp[8], kp[17]);
g.drawCrosshair(p);
}
}
function getNewPoint(p1, p2, multiplier) {
let d = dist(p1.x, p1.y, p2.x, p2.y);
let theta = atan2(p2.y - p1.y, p2.x - p1.x);
let deltaX = multiplier * d * cos(theta);
let deltaY = multiplier * d * sin(theta);
let x2 = p1.x + deltaX;
let y2 = p1.y + deltaY;
return createVector(x2, y2);
}
function drawMarks() {
for (let i = 0; i < marks.length; i++) {
point(marks[i].x, marks[i].y);
}
}
function Gun() {
this.detectTrigger = function (p1, p2, p3, p4, p0, i) {
const np = createVector((p2.x + p3.x) / 2, (p2.y + p3.y) / 2);
const np2 = getNewPoint(p2, p0, 1.2);
if (dist(p1.x, p1.y, np.x, np.y) > dist(p1.x, p1.y, p4.x, p4.y) * 1.4) {
if (!fireTracker[i]) {
// fire
this.drawFlash(np2);
fireTracker[i] = true;
marks.push(getNewPoint(p2, p0, defaultMultiplier));
if (marks.length > markCount) {
marks.shift();
}
}
} else {
fireTracker[i] = false;
}
};
this.drawGun = function (p1, p2, p3) {
const np2 = getNewPoint(p1, p2, 1.2);
const np3 = getNewPoint(p1, p3, 0.6);
push();
strokeWeight(35);
line(p1.x, p1.y, np2.x, np2.y);
line(p1.x, p1.y, np3.x, np3.y);
pop();
};
this.drawCrosshair = function (p) {
push();
stroke(0, 255, 0);
strokeWeight(3);
line(p.x - 10, p.y, p.x + 10, p.y);
line(p.x, p.y - 10, p.x, p.y + 10);
pop();
};
this.drawFlash = function (p) {
push();
// Draw circle around the nozzle
strokeWeight(5);
stroke(255, 196, 12);
fill(238, 255, 27);
circle(p.x, p.y, 150);
// Draw light effect on the entire screen
noStroke();
fill("rgba(255,255,255,0.3)");
rect(0, 0, width, height);
pop();
};
this.drawRecoil = function () {};
this.update = function () {
this.detectTrigger();
this.drawGun();
this.drawCrosshair();
};
}
function exponentialSmoothing(oldValue, newValue, alpha) {
return alpha * newValue + (1 - alpha) * oldValue;
}
function updatePos() {
pos.length = myResults.length;
fireTracker.length = myResults.length;
for (let i = 0; i < myResults.length; i++) {
const kp = myResults[i].keypoints;
pos[i] = pos[i] || kp.map((p) => ({ x: p.x, y: p.y }));
pos[i] = kp.map((p, j) => {
const oldPos = pos[i][j];
const newX = exponentialSmoothing(oldPos.x, p.x, ALPHA);
const newY = exponentialSmoothing(oldPos.y, p.y, ALPHA);
return { x: newX, y: newY };
});
}
}