Compare commits
10 Commits
1e48f4a69e
...
f673efc1dd
Author | SHA1 | Date | |
---|---|---|---|
f673efc1dd | |||
04e887f706 | |||
f688f75ce6 | |||
d27f84134e | |||
9c9865f149 | |||
79b023829f | |||
df9229cb78 | |||
71aeb0057a | |||
687009f8fc | |||
035cd541a0 |
89
src/Gun.js
89
src/Gun.js
|
@ -7,33 +7,88 @@ import bulletHole from '../data/bulletHole.png';
|
||||||
import shot from '../data/shot.mp3';
|
import shot from '../data/shot.mp3';
|
||||||
import empty from '../data/empty.mp3';
|
import empty from '../data/empty.mp3';
|
||||||
|
|
||||||
/*
|
|
||||||
The Gun is the mouse. To draw it, hide the cursor with the noCursor function and then draw instead the image cursor.png at the location of the mouse. To load and play sounds with p5.js take a look at this example. The sound files are shot.mp3 and empty.mp3, depending on whether there are still shots available. Finally, when you shoot with the gun, add a random CURSOR_SIZE noise (or a similarly small amount) to alter the bullet's final hitting location.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
class Gun extends Subject {
|
||||||
|
constructor(totShots) {
|
||||||
|
super();
|
||||||
|
this.cursor = null;
|
||||||
|
this.shotSound = null;
|
||||||
|
this.emptySound = null;
|
||||||
|
|
||||||
class Gun {
|
this.totshots = totShots;
|
||||||
noCursor() { }
|
this.remainingShots = totShots;
|
||||||
draw() { }
|
this.bullets = []; // array of Bullet objects
|
||||||
setup() {
|
|
||||||
shot = loadSound('data/shot.mp3');
|
|
||||||
empty = loadSound('data/empty.mp3');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// mousePressed() {
|
setup() {
|
||||||
// if () {
|
this.cursor = loadImage(cursor);
|
||||||
// shot.play();
|
this.shotSound = loadSound(shot);
|
||||||
// } else { // no bullets left
|
this.emptySound = loadSound(empty);
|
||||||
// empty.play();
|
}
|
||||||
// }
|
draw() {
|
||||||
// }
|
noCursor();
|
||||||
|
|
||||||
|
if (this.cursor) {
|
||||||
|
image(this.cursor, mouseX - CURSOR_SIZE / 2, mouseY - CURSOR_SIZE / 2, CURSOR_SIZE, CURSOR_SIZE);
|
||||||
|
} // custom cursor
|
||||||
|
|
||||||
|
for (let bullet of this.bullets) {
|
||||||
|
bullet.draw();
|
||||||
|
} // draw bullet holes
|
||||||
|
|
||||||
|
this.bullets = this.bullets.filter(bullet => bullet.visible); // clean up bullet holes
|
||||||
|
}
|
||||||
|
|
||||||
|
shoot() {
|
||||||
|
if (this.remainingShots > 0) {
|
||||||
|
this.remainingShots--;
|
||||||
|
|
||||||
|
if (this.shotSound) this.shotSound.play();
|
||||||
|
|
||||||
|
const x = mouseX;
|
||||||
|
const y = mouseY;
|
||||||
|
|
||||||
|
const bullet = new Bullet(x, y);
|
||||||
|
this.bullets.push(bullet);
|
||||||
|
|
||||||
|
this.notifySubscribers('gun', x, y, this.remainingShots);
|
||||||
|
} else {
|
||||||
|
if (this.emptySound) this.emptySound.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reload() {
|
||||||
|
this.remainingShots = this.totShots;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRemainingShots() {
|
||||||
|
return this.remainingShots;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Bullet
|
// Bullet
|
||||||
class Bullet {
|
class Bullet {
|
||||||
|
constructor(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.visible = true;
|
||||||
|
|
||||||
// TO DO
|
this.img = null;
|
||||||
|
loadImage(bulletHole, (img) => {
|
||||||
|
this.img = img;
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.visible = false;
|
||||||
|
}, BULLET_DURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
if (this.visible && this.img) {
|
||||||
|
image(this.img, this.x - BULLET_HOLE_SIZE / 2, this.y - BULLET_HOLE_SIZE / 2, BULLET_HOLE_SIZE, BULLET_HOLE_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,32 +1,28 @@
|
||||||
import { BULLET_SIZE, FONT_SIZE, TOT_SHOTS } from './Constants.js';
|
import { BULLET_SIZE, FONT_SIZE} from './Constants.js';
|
||||||
import { Gun } from './Gun.js';
|
|
||||||
import { Target } from './Target';
|
|
||||||
|
|
||||||
import bullet from '../data/bullet.png';
|
import bullet from '../data/bullet.png';
|
||||||
|
|
||||||
|
|
||||||
class ScoreDisplay {
|
class ScoreDisplay {
|
||||||
constructor(initialBullets) {
|
constructor(initialBullets) {
|
||||||
this.bulletImg = null;
|
this.img = null;
|
||||||
this.shotLeft = initialBullets;
|
this.shotLeft = initialBullets;
|
||||||
this.score = 0;
|
this.score = 0;
|
||||||
|
|
||||||
loadImage(bullet, (img) => {
|
loadImage(bullet, (PImage) => {
|
||||||
this.bulletImg = img;
|
this.img = PImage;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
// Draw score on top-left
|
// score
|
||||||
textFont('Arial');
|
textFont('Arial');
|
||||||
textSize(25);
|
textSize(FONT_SIZE);
|
||||||
fill(255, 0, 0);
|
fill(255, 0, 0);
|
||||||
textAlign(LEFT, TOP);
|
textAlign(LEFT, TOP);
|
||||||
text(`Score: ${this.score}`, 10, 10);
|
text(`Score: ${this.score}`, 10, 10);
|
||||||
|
|
||||||
if (!this.bulletImg) return;
|
// bullets
|
||||||
|
if (!this.img) return;
|
||||||
// Draw remaining bullets on top-right
|
|
||||||
const bulletSpacing = 20;
|
const bulletSpacing = 20;
|
||||||
const marginRight = 10;
|
const marginRight = 10;
|
||||||
const bulletsWidth = this.shotLeft * bulletSpacing;
|
const bulletsWidth = this.shotLeft * bulletSpacing;
|
||||||
|
@ -34,10 +30,32 @@ class ScoreDisplay {
|
||||||
let y = 10;
|
let y = 10;
|
||||||
|
|
||||||
for (let i = 0; i < this.shotLeft; i++) {
|
for (let i = 0; i < this.shotLeft; i++) {
|
||||||
image(this.bulletImg, startX + i * bulletSpacing, y, BULLET_SIZE, BULLET_SIZE);
|
image(this.img, startX + i * bulletSpacing, y, BULLET_SIZE, BULLET_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetScore() {
|
||||||
|
this.score = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
addScore(scoreToAdd) {
|
||||||
|
this.score += scoreToAdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBullets(numBullets) {
|
||||||
|
this.shotLeft = numBullets;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(source, ...others) {
|
||||||
|
if (source === 'gun') {
|
||||||
|
const [, , remainingShots] = others;
|
||||||
|
this.setBullets(remainingShots);
|
||||||
|
} else if (source === 'target-hit') {
|
||||||
|
const [points] = others;
|
||||||
|
this.addScore(points);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { ScoreDisplay };
|
export { ScoreDisplay };
|
||||||
|
|
|
@ -1,5 +1,26 @@
|
||||||
class Subject {
|
class Subject {
|
||||||
// TO DO
|
constructor() {
|
||||||
|
this.observers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe(observer) {
|
||||||
|
if (!observer || typeof observer.update !== 'function') return;
|
||||||
|
this.observers.push(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsubscribe(observer) {
|
||||||
|
this.observers = this.observers.filter(o => o !== observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsubscribeAll() {
|
||||||
|
this.observers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
notifySubscribers(source, ...others) {
|
||||||
|
for (let observer of this.observers) {
|
||||||
|
observer.update(source, ...others);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Subject };
|
export { Subject };
|
||||||
|
|
134
src/Target.js
134
src/Target.js
|
@ -1,21 +1,135 @@
|
||||||
|
import { MAX_TARGETS, TARGET_WIDTH } from './Constants';
|
||||||
import { Subject } from './Subject';
|
import { Subject } from './Subject';
|
||||||
import { Gun } from './Gun';
|
|
||||||
|
|
||||||
import teddy from '../data/teddy.png';
|
import teddy from '../data/teddy.png';
|
||||||
import duck from '../data/duck.png';
|
import duck from '../data/duck.png';
|
||||||
import squirrel from '../data/squirrel.png';
|
import squirrel from '../data/squirrel.png';
|
||||||
|
|
||||||
class Target {
|
class Target extends Subject {
|
||||||
// TO DO
|
constructor(x, y, width) {
|
||||||
|
super();
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = width;
|
||||||
|
this.height = width;
|
||||||
|
this.visible = true;
|
||||||
|
this.img = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
if (this.visible && this.img) {
|
||||||
|
image(this.img, this.x, this.y, this.width, this.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPoints() {
|
||||||
|
return 0; // to be overridden
|
||||||
|
}
|
||||||
|
|
||||||
|
isHit(x, y) {
|
||||||
|
return (
|
||||||
|
this.visible &&
|
||||||
|
x >= this.x &&
|
||||||
|
x <= this.x + this.width &&
|
||||||
|
y >= this.y &&
|
||||||
|
y <= this.y + this.height
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
shoot(x, y) {
|
||||||
|
if (this.isHit(x, y)) {
|
||||||
|
this.visible = false;
|
||||||
|
this.notifySubscribers('target-hit', this.getPoints());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update(source, ...others) {
|
||||||
|
if (source === 'gun') {
|
||||||
|
const [x, y] = others;
|
||||||
|
this.shoot(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO DO
|
class TeddyTarget extends Target {
|
||||||
// class TeddyTarget ...
|
constructor(x, y) {
|
||||||
// class DuckTarget ...
|
super(x, y, TARGET_WIDTH);
|
||||||
// class SquirrelTarget ...
|
this.img = loadImage(teddy);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPoints() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DuckTarget extends Target {
|
||||||
|
constructor(x, y) {
|
||||||
|
super(x, y, TARGET_WIDTH);
|
||||||
|
this.img = loadImage(duck);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPoints() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SquirrelTarget extends Target {
|
||||||
|
constructor(x, y) {
|
||||||
|
super(x, y, TARGET_WIDTH);
|
||||||
|
this.img = loadImage(squirrel);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPoints() {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TargetFactory {
|
class TargetFactory {
|
||||||
// TO DO
|
static instance;
|
||||||
|
|
||||||
|
static getInstance() {
|
||||||
|
if (!TargetFactory.instance) {
|
||||||
|
TargetFactory.instance = new TargetFactory();
|
||||||
|
}
|
||||||
|
return TargetFactory.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTargetsByName(targetNames, targetWidth, y) {
|
||||||
|
const targets = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < targetNames.length && i < MAX_TARGETS; i += 1) {
|
||||||
|
const name = targetNames[i];
|
||||||
|
const x = 100 + i * targetWidth;
|
||||||
|
|
||||||
|
switch (name) {
|
||||||
|
case 'teddy':
|
||||||
|
targets.push(new TeddyTarget(x, y));
|
||||||
|
break;
|
||||||
|
case 'duck':
|
||||||
|
targets.push(new DuckTarget(x, y));
|
||||||
|
break;
|
||||||
|
case 'squirrel':
|
||||||
|
targets.push(new SquirrelTarget(x, y));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRandomTargets(numTargets, targetWidth, y) {
|
||||||
|
const names = ['teddy', 'duck', 'squirrel'];
|
||||||
|
const targetNames = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < numTargets; i++) {
|
||||||
|
targetNames.push(random(names));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getTargetsByName(targetNames, targetWidth, y);
|
||||||
|
} // create random targets
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Target, TargetFactory };
|
export {
|
||||||
|
Target, TeddyTarget,
|
||||||
|
DuckTarget,
|
||||||
|
SquirrelTarget, TargetFactory
|
||||||
|
};
|
||||||
|
|
33
src/main.js
33
src/main.js
|
@ -14,21 +14,36 @@ function setup() {
|
||||||
createCanvas(800, 600);
|
createCanvas(800, 600);
|
||||||
|
|
||||||
// 1. Init gun and score display
|
// 1. Init gun and score display
|
||||||
|
gun = new Gun(TOT_SHOTS);
|
||||||
|
gun.setup();
|
||||||
score = new ScoreDisplay(TOT_SHOTS);
|
score = new ScoreDisplay(TOT_SHOTS);
|
||||||
// 2. Init the targets
|
// 2. Init the targets
|
||||||
|
initTargets();
|
||||||
// 3. Subscribe gun
|
// 3. Subscribe gun
|
||||||
|
gun.subscribe(score);
|
||||||
|
|
||||||
|
// Subscribe each target to gun
|
||||||
|
for (let target of targets) {
|
||||||
|
gun.subscribe(target);
|
||||||
|
target.subscribe(score);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
background('#eeeeee');
|
background('#eeeeee');
|
||||||
|
for (let target of targets) {
|
||||||
|
target.draw();
|
||||||
|
}
|
||||||
|
gun.draw();
|
||||||
score.draw();
|
score.draw();
|
||||||
// draw targets, gun, bullets, score
|
// draw targets, gun, bullets, score
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shoot
|
// Shoot
|
||||||
function mousePressed() {
|
function mousePressed() {
|
||||||
|
gun.shoot();
|
||||||
// shoot
|
// shoot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,13 +51,27 @@ function mousePressed() {
|
||||||
function keyPressed() {
|
function keyPressed() {
|
||||||
if (key === ' ') {
|
if (key === ' ') {
|
||||||
// reset score and targets
|
// reset score and targets
|
||||||
|
gun.remainingShots = TOT_SHOTS;
|
||||||
|
score.shotLeft = TOT_SHOTS;
|
||||||
|
score.score = 0;
|
||||||
|
|
||||||
|
initTargets();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// init the targets
|
// init the targets
|
||||||
function initTargets() {
|
function initTargets() {
|
||||||
// Create new targets from the factory
|
// Create new targets from the factory
|
||||||
|
const factory = TargetFactory.getInstance();
|
||||||
// Remember to unsubscribe the previous targets and to subscribe the new ones
|
// Remember to unsubscribe the previous targets and to subscribe the new ones
|
||||||
|
gun.unsubscribeAll();
|
||||||
|
|
||||||
|
targets = factory.getRandomTargets(MAX_TARGETS, TARGET_WIDTH, 200);
|
||||||
|
gun.subscribe(score);
|
||||||
|
for (let target of targets) {
|
||||||
|
gun.subscribe(target);
|
||||||
|
target.subscribe(score);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not touch these
|
// Do not touch these
|
||||||
|
|
Loading…
Reference in New Issue
Block a user