“Top-Down Shooter” by trog
https://openprocessing.org/sketch/2136620
License CreativeCommons Attribution ShareAlike
https://creativecommons.org/licenses/by-sa/3.0
{{filePath}}
{{width}} x {{height}}
Report Sketch
Oh, that naughty sketch! Please let us know what the issue is below.
Apply Template
Applying this template will reset your sketch and remove all your changes. Are you sure you would like to continue?
Report Sketch
Report Comment
Please confirm that you would like to report the comment below.
We will review your submission and take any actions necessary per our Community Guidelines. In addition to reporting this comment, you can also block the user to prevent any future interactions.
Please report comments only when necessary. Unnecessary or abusive use of this tool may result in your own account being suspended.
Are you sure you want to delete your sketch?
Any files uploaded will be deleted as well.
Delete Comment?
This will also delete all the replies to this comment.
Delete this tab? Any code in it will be deleted as well.
Select a collection to submit your sketch
We Need Your Support
Since 2008, OpenProcessing has provided tools for creative coders to learn, create, and share over a million open source projects in a friendly environment.
Niche websites like ours need your continued support for future development and maintenance, while keeping it an ad-free platform that respects your data and privacy!
Please consider subscribing below to show your support with a "Plus" badge on your profile and get access to many other features!
A fork of Top-Down Shooter by Daniel (NessDan) Kaspo
CC Attribution ShareAlike
Top-Down Shooter
xxxxxxxxxx
//TODO: Implement states (Gameover, menus, etc.)
//TODO: Implement networking.
//TODO: this.HasHit should be a var (methinks).
//TODO: Use a timer for drops and enemies.
//TODO: Make player zombie when killed (for effect).
//TODO: Add movement acceleration to enemy and player.
Array.prototype.remove = function(item) {
var index = this.indexOf(item);
if (index !== -1) this.splice(index, 1);
};
/**** Global Variables ****/
var myPlayer;
var thePlayers;
var theBullets;
var theEnemies;
var theDrops;
var NORTH = 1;
var EAST = 2;
var SOUTH = 4;
var WEST = 8;
// Not sure if these should be globals, but...
var spawnInterval;
var spawnSpeed;
var spawnModifier;
var dropInterval;
var dropSpeed;
var dropModifier;
var screenFade;
var showDebug;
var gamestate; // 0 = Main menu, 1 = Game, 2 = Gameover
function setup() {
createCanvas(600, 480);
frameRate(60);
cursor(CROSS);
initialize();
}
function draw() {
if (gamestate === 1) {
gameUpdate();
gameDraw();
} else if (gamestate === 2) {
gameUpdate();
gameDraw();
gameoverDraw();
}
if (showDebug) {
debugDraw();
}
}
function keyPressed() {
switch (key) {
// For player direction.
case "w":
case "W":
myPlayer.direction |= NORTH;
break;
case "d":
case "D":
myPlayer.direction |= EAST;
break;
case "s":
case "S":
myPlayer.direction |= SOUTH;
break;
case "a":
case "A":
myPlayer.direction |= WEST;
break;
// Restart game if it's over
case "r":
case "R":
if (gamestate === 2) {
initialize();
}
break;
// Enable showDebug mode.
case "t":
case "T":
if (showDebug) {
showDebug = false;
} else {
showDebug = true;
}
break;
// Enemy Spawning (For Debugging!)
//case('z'): theEnemies.push(new Enemy());break;
//case('z'): spawnInterval = 100;break;
}
}
function keyReleased() {
// For player direction.
switch (key) {
case "w":
case "W":
myPlayer.direction ^= NORTH;
break;
case "d":
case "D":
myPlayer.direction ^= EAST;
break;
case "s":
case "S":
myPlayer.direction ^= SOUTH;
break;
case "a":
case "A":
myPlayer.direction ^= WEST;
break;
}
}
function mouseMoved() {
myPlayer.mouseLoc.x = mouseX;
myPlayer.mouseLoc.y = mouseY;
}
function mouseDragged() {
myPlayer.mouseLoc.x = mouseX;
myPlayer.mouseLoc.y = mouseY;
}
function mousePressed() {
myPlayer.weapon.firing = true;
}
function mouseReleased() {
// If the gun is an automatic, enable this.
// Fixes an issue with fully/semi auto guns.
if (myPlayer.weapon.type === 1) {
myPlayer.weapon.firing = false;
}
}
class Bullet {
/**** Constructors ****/
constructor(tempOwner) {
this.owner = tempOwner;
this.location = createVector(this.owner.location.x, this.owner.location.y); // Copy it so we don't modify the original owner's location
this.breadth = 10;
this.speed = 15.0;
this.damage = 1;
this.trajectory = p5.Vector.sub(this.owner.mouseLoc, this.location);
this.trajectory.normalize();
this.trajectory.mult(this.speed);
this.colNorm = color(25, 75, 200);
this.colCur = this.colNorm;
}
/**** Methods ****/
travel() {
// Bullet is moved avar the this.trajectory
this.location.add(this.trajectory);
}
stillAlive() {
// Delete bullet if it goes off screen.
// Also, a cheesy Portal reference.
// http://www.youtube.com/watch?v=8IGS9qY7xko
// Also, thanks amnon.owned from the Processing forums
// for the refactored code tip!
if (
this.location.y > height ||
this.location.y < 0 ||
this.location.x > width ||
this.location.x < 0
) {
this.destroy();
}
}
hasHit() {
for (var i = 0; i < theEnemies.length; i++) {
var e = theEnemies[i];
var distance = dist(
this.location.x,
this.location.y,
e.location.x,
e.location.y
);
if (distance < this.breadth / 2 + e.breadth / 2) {
e.takeDamage(this);
this.destroy();
break;
}
}
}
destroy() {
theBullets.remove(this);
}
/**** Update and Display ****/
update() {
this.travel();
this.stillAlive();
this.hasHit();
}
display() {
push();
ellipseMode(CENTER);
fill(this.colCur);
ellipse(this.location.x, this.location.y, this.breadth, this.breadth);
noFill();
pop();
}
}
class Drop {
constructor(i) {
this.location = createVector(
Math.floor(random(width)),
Math.floor(random(height))
);
this.breadth = 10;
this.type = i;
switch (i) {
// Health pack.
case 0:
this.colNorm = color(255, 118, 246);
break;
// SMG
case 1:
this.colNorm = color(255, 255, 0);
break;
}
}
hasHit() {
for (var i = 0; i < thePlayers.length; i++) {
var p = thePlayers[i];
var distance = dist(
this.location.x,
this.location.y,
p.location.x,
p.location.y
);
if (distance < this.breadth / 2 + p.breadth / 2) {
this.giveEffect(p);
this.destroy();
break;
}
}
}
giveEffect(p) {
switch (this.type) {
case 0:
p.heal(3);
break;
case 1:
p.weapon = new SMG(p);
break;
}
}
destroy() {
theDrops.remove(this);
}
update() {
this.hasHit();
}
display() {
fill(this.colNorm);
ellipse(this.location.x, this.location.y, this.breadth, this.breadth);
noFill();
}
}
class Enemy {
/**** Constructors ****/
constructor() {
this.location = this.generateSpawn();
this.target = this.getTarget();
this.speed = 1;
this.breadth = 18;
this.healthMax = 2;
this.healthCur = this.healthMax;
this.damage = 1;
this.hitTimer = new Timer(5);
this.colNorm = color(125, 75, 100);
this.colHit = color(255, 0, 0);
this.colCur = this.colNorm;
}
/**** Methods ****/
walk() {
if (gamestate === 1) {
// Enemy walks towards player.
this.trajectory = p5.Vector.sub(this.target.location, this.location);
this.trajectory.normalize();
this.trajectory.mult(this.speed);
}
this.location.add(this.trajectory);
}
hasHit() {
for (var i = 0; i < thePlayers.length; i++) {
var p = thePlayers[i];
if (p.alive === true) {
var distance = dist(
this.location.x,
this.location.y,
p.location.x,
p.location.y
);
// If there is a collision...
if (distance < this.breadth / 2 + p.breadth / 2) {
p.takeDamage(this);
this.destroy();
break;
}
}
}
}
takeDamage(b) {
this.healthCur -= b.damage;
if (this.healthCur <= 0) {
this.destroy();
b.owner.score++;
} else {
// Flash colors
this.colCur = this.colHit;
this.hitTimer.start();
}
}
colChange() {
if (this.colCur === this.colHit && this.hitTimer.isFinished()) {
this.colCur = this.colNorm;
}
}
destroy() {
theEnemies.remove(this);
}
shamble() {
this.speed = random(0.3, 0.6);
this.trajectory.set([random(-50, 50), random(-50, 50), 0]);
this.trajectory.normalize();
this.trajectory.mult(this.speed);
}
generateSpawn() {
var side;
var spawnX = 0;
var spawnY = 0;
side = Math.floor(random(4));
switch (side) {
case 0:
spawnX = Math.floor(random(width));
spawnY = 0;
break;
case 1:
spawnX = width;
spawnY = Math.floor(random(height));
break;
case 2:
spawnX = Math.floor(random(width));
spawnY = height;
break;
case 3:
spawnX = 0;
spawnY = Math.floor(random(height));
break;
}
return createVector(spawnX, spawnY);
}
getTarget() {
var targetId;
targetId = Math.floor(random(thePlayers.length));
return thePlayers[targetId];
}
/**** Update and Display ****/
update() {
this.walk();
this.hasHit();
this.colChange();
}
display() {
// Draw the enemy.
push();
ellipseMode(CENTER);
fill(this.colCur);
ellipse(this.location.x, this.location.y, this.breadth, this.breadth);
pop();
// Show health as number
fill(this.colNorm);
textAlign(CENTER);
text(this.healthCur, this.location.x, this.location.y - 25);
// Health Bar!
push();
stroke(0);
strokeWeight(1);
fill(255, 0, 0);
rect(this.location.x - 25, this.location.y - 20, 50, 5);
stroke(0, 255);
fill(0, 255, 0);
rect(
this.location.x - 25,
this.location.y - 20,
map(this.healthCur, 0, this.healthMax, 0, 50),
5
);
noFill();
pop();
}
}
/**** Alternate Enemies ****/
class FastEnemy extends Enemy {
/**** Constructors ****/
constructor() {
super();
this.location = this.generateSpawn();
this.speed = 1.9;
this.breadth = 18;
this.healthMax = 1;
this.healthCur = this.healthMax;
this.damage = 1;
this.colNorm = color(25, 255, 25);
this.colHit = color(255, 0, 0);
this.colCur = this.colNorm;
}
}
class BossEnemy extends Enemy {
/**** Constructors ****/
constructor() {
super();
this.location = this.generateSpawn();
this.speed = 1.8;
this.breadth = 125;
this.healthMax = 200;
this.healthCur = this.healthMax;
this.damage = 100;
this.colNorm = color(255, 0, 0);
this.colHit = color(255, 0, 0);
this.colCur = this.colNorm;
}
}
// Sets all globals to their defaults; handy for restarting.
function initialize() {
background(50);
showDebug = false;
gamestate = 1;
spawnInterval = 0.0;
spawnSpeed = 30.0;
spawnModifier = 0.05;
dropInterval = 0.0;
dropSpeed = 800.0;
dropModifier = 1;
screenFade = 0.0;
myPlayer = new Player();
thePlayers = [];
thePlayers.push(myPlayer);
theBullets = [];
theEnemies = [];
theDrops = [];
}
// Creates enemies on an varerval which decreases slowly.
// Should be a static method of "enemy" but Processing
// doesn't like static methods :(
function enemySpawn() {
if (spawnInterval < spawnSpeed) {
spawnInterval++;
} else {
spawnInterval = 0;
if (spawnSpeed > 15) {
spawnSpeed -= spawnModifier;
} else {
spawnSpeed = 15;
}
var enemyType;
enemyType = Math.floor(random(3));
switch (enemyType) {
case 0:
theEnemies.push(new Enemy());
break;
default:
theEnemies.push(new FastEnemy());
break;
}
}
}
// Creates drops on an varerval which decreases slowly.
// Should be a static method of "drop" but Processing
// doesn't like static methods :(
function dropSpawn() {
if (dropInterval < dropSpeed) {
dropInterval++;
} else {
dropInterval = 0;
if (dropSpeed > 300) {
dropSpeed -= dropModifier;
} else {
dropSpeed = 300;
}
var dropType;
dropType = Math.floor(random(3));
switch (dropType) {
case 0:
theDrops.push(new Drop(1));
break;
default:
theDrops.push(new Drop(0));
break;
}
}
}
function shambleTheZombies() {
for (var i = 0; i < theEnemies.length; i++) {
theEnemies[i].shamble();
}
}
// Adds a circle on the cursor, a line from the player to mouse,
// and some debug information at the top-left.
function debugDraw() {
fill(255);
textAlign(LEFT);
text("FPS: " + Number(frameRate()), 5, 40);
text("Drop Interval: " + dropInterval, 5, 55);
text("Drop Speed: " + dropSpeed, 5, 70);
text("Spawn Interval: " + spawnInterval, 5, 85);
text("Spawn Speed: " + spawnSpeed, 5, 100);
text("Game State: " + gamestate, 5, 115);
noFill();
fill(0);
/*
// Draws a line from each enemy to it's this.target.
for (var i = 0; i < theEnemies.length; i++) {
Enemy e = theEnemies[i];
line(e.location.x, e.location.y, e.target.location.x, e.target.location.y);
}
*/
noFill();
}
class Gun {
/**** Constructors ****/
constructor(tempOwner) {
this.owner = tempOwner;
this.firing = false;
// this.Type: 0 = SemiAuto, 1 = FullyAuto
this.type = 0;
}
/**** Methods *****/
fire() {
if (this.owner.alive) {
if (this.firing) {
theBullets.push(new Bullet(this.owner));
this.firing = false;
}
}
}
update() {
this.fire();
}
}
class Pistol extends Gun {
/**** Constructors ****/
constructor(tempOwner) {
super(tempOwner);
}
}
class SMG extends Gun {
/**** Constructors ****/
constructor(tempOwner) {
super(tempOwner);
this.type = 1;
this.fireRate = new Timer(115);
this.fireRate.start();
this.ammoMax = 100;
this.ammoCur = this.ammoMax;
}
/**** Methods *****/
// This fires the gun like an automatic weapon.
fire() {
if (this.owner.alive) {
if (this.firing && this.fireRate.isFinished() && this.ammoCur > 0) {
// Fire off a bullet and reduce ammo count.
theBullets.push(new Bullet(this.owner));
this.ammoCur--;
// If we have more ammo, restart the this.firing cooldown.
// Otherwise, give the player back the pistol.
if (this.ammoCur > 0) {
this.fireRate.start();
} else {
this.owner.weapon = new Pistol(this.owner);
}
}
}
}
}
// Timer class; currently used for hit flashing.
class Timer {
constructor(tempTimeLength) {
this.timeLength = tempTimeLength;
}
start() {
this.timeStarted = millis();
}
isFinished() {
var timePassed = millis() - this.timeStarted;
if (timePassed >= this.timeLength) {
return true;
} else {
return false;
}
}
}
class Player {
/**** Constructors ****/
constructor() {
this.location = createVector(width / 2, height / 2);
this.mouseLoc = createVector(0, 0);
this.direction = 0;
this.speed = 2;
this.breadth = 20;
this.alive = true;
this.healthMax = 10;
this.healthCur = this.healthMax;
this.score = 0;
this.weapon = new Pistol(this);
this.hitTimer = new Timer(25);
this.colNorm = color(200, 100, 0);
this.colHit = color(255, 0, 0);
this.colCur = this.colNorm;
}
/**** Methods ****/
walk() {
switch (this.direction) {
case NORTH:
this.location.y -= this.speed;
break;
case WEST:
this.location.x -= this.speed;
break;
case SOUTH:
this.location.y += this.speed;
break;
case EAST:
this.location.x += this.speed;
break;
/*
// Normalized?
case NORTH|WEST: this.location.y -= this.speed / 2; this.location.x -= this.speed / 2; break;
case NORTH|EAST: this.location.y -= this.speed / 2; this.location.x += this.speed / 2; break;
case SOUTH|WEST: this.location.y += this.speed / 2; this.location.x -= this.speed / 2; break;
case SOUTH|EAST: this.location.y += this.speed / 2; this.location.x += this.speed / 2; break;
*/
case NORTH | WEST:
this.location.y -= this.speed;
this.location.x -= this.speed;
break;
case NORTH | EAST:
this.location.y -= this.speed;
this.location.x += this.speed;
break;
case SOUTH | WEST:
this.location.y += this.speed;
this.location.x -= this.speed;
break;
case SOUTH | EAST:
this.location.y += this.speed;
this.location.x += this.speed;
break;
case NORTH | WEST | EAST:
this.location.y -= this.speed;
break;
case SOUTH | WEST | EAST:
this.location.y += this.speed;
break;
case NORTH | WEST | SOUTH:
this.location.x -= this.speed;
break;
case NORTH | SOUTH | EAST:
this.location.x += this.speed;
break;
}
this.location.x = constrain(
this.location.x,
0 + this.breadth / 2,
width - this.breadth / 2
);
this.location.y = constrain(
this.location.y,
0 + this.breadth / 2,
height - this.breadth / 2
);
}
takeDamage(e) {
this.healthCur = constrain(this.healthCur - e.damage, 0, this.healthMax);
if (this.healthCur <= 0) {
this.destroy();
} else {
// Flash colors
this.colCur = this.colHit;
this.hitTimer.start();
}
}
heal(healAmt) {
this.healthCur = constrain(this.healthCur + healAmt, 0, this.healthMax);
}
colChange() {
if (this.colCur === this.colHit && this.hitTimer.isFinished()) {
this.colCur = this.colNorm;
}
}
destroy() {
thePlayers.remove(this);
this.alive = false;
gamestate = 2;
for (var i = 0; i < thePlayers.length; i++) {
var p = thePlayers[i];
if (p.alive === true) {
gamestate = 1;
}
}
if (gamestate === 2) {
shambleTheZombies();
} else {
// If the game is still going, zombies choose a new this.target.
for (var i = 0; i < theEnemies.length; i++) {
var e = theEnemies[i];
if (e.target === this) {
e.target = e.getTarget();
}
}
}
}
/**** Update and Display ****/
update() {
this.walk();
this.colChange();
this.weapon.update();
}
display() {
// Draw the player.
fill(this.colCur);
ellipse(this.location.x, this.location.y, this.breadth, this.breadth);
// This code draws a rotating player - good for the future.
/*
push();
translate(this.location.x, this.location.y);
rotate(atan2(mouseY-this.location.y, mouseX-this.location.x));
fill(this.colCur);
ellipse(0, 0, this.breadth, this.breadth);
pop();
*/
// Show health as number.
fill(this.colNorm);
textAlign(CENTER);
text(this.healthCur, this.location.x, this.location.y - 25);
// Health Bar!
push();
stroke(0);
strokeWeight(1);
fill(255, 0, 0);
rect(this.location.x - 25, this.location.y - 20, 50, 5);
stroke(0, 255);
fill(0, 255, 0);
rect(
this.location.x - 25,
this.location.y - 20,
map(this.healthCur, 0, this.healthMax, 0, 50),
5
);
noFill();
pop();
// Only show the score for our player at the top.
if (this === myPlayer) {
fill(255);
textAlign(LEFT);
text("Score: " + this.score, 5, 20);
noFill();
}
}
}
function gameUpdate() {
/**** Miscellaneous ****/
background(50);
if (gamestate === 1) {
enemySpawn();
dropSpawn();
}
/**** Updates ****/
// Player
for (var i = 0; i < thePlayers.length; i++) {
thePlayers[i].update();
}
// Bullets
for (var i = 0; i < theBullets.length; i++) {
theBullets[i].update();
}
// Enemies
for (var i = 0; i < theEnemies.length; i++) {
theEnemies[i].update();
}
// Drops
for (var i = 0; i < theDrops.length; i++) {
theDrops[i].update();
}
}
function gameDraw() {
/**** Displays ****/
// Player
for (var i = 0; i < thePlayers.length; i++) {
thePlayers[i].display();
}
// Bullets
for (var i = 0; i < theBullets.length; i++) {
theBullets[i].display();
}
// Enemies
for (var i = 0; i < theEnemies.length; i++) {
theEnemies[i].display();
}
// Drops
for (var i = 0; i < theDrops.length; i++) {
theDrops[i].display();
}
}
function gameoverDraw() {
// Draw a big transparent square over the window and
// slowly make the square become less transparent.
fill(0, 0, 0, screenFade);
screenFade = constrain(screenFade + 0.8, 0, 255);
rect(0, 0, width, height);
noFill();
// Write the gameover text.
textAlign(CENTER);
textSize(24);
fill(255, 0, 0);
text("BRAAAAAAINS!", width / 2, 50);
textSize(16);
text("Score: " + myPlayer.score, width / 2, 75);
textSize(14);
text("Press R to restart.", width / 2, 100);
textSize(12);
noFill();
}
See More Shortcuts
Please verify your email to comment
Verify Email