I don't understand why this isn't drawing, it might be a stupid mistake, although I have gone through all the code multiple times, but if it is, I apologize, I am trying to make the snake game and the snake won't draw. I wan't the array cells to keep track of where all of the cells are, and to do this you have to unshift and pop them accordingly.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var unit = 16;
var snakeone = {
maxcells: 3,
cellsx: [],
cellsy: [],
x: 20 * unit,
y: 20 * unit,
};
var apple = {
x: 20 * unit,
y: 20 * unit,
};
var color = 'white';
function setup() {
}
function style() {}
function clear() {
ctx.clearRect(0, 0, 656, 656);
}
document.onkeydown = keydown;
document.onkeyup = keyup;
function keydown(e) {
ctrl(e.code, true);
}
function keyup(e) {
ctrl(e.code, false);
}
function ctrl(code, bool) {
if(code == 37) {
snakeone.a = bool;
}
if(code == 38) {
snakeone.w = bool;
}
if(code == 39) {
snakeone.d = bool;
}
if(code == 40) {
snakeone.s = bool;
}
}
function move() {
if(snakeone.a === true) {
snakeone.x -= 1;
}
if(snakeone.w === true) {
snakeone.y -= 1;
}
if(snakeone.d === true) {
snakeone.x += 1;
}
if(snakeone.s === true) {
snakeone.y += 1;
}
}
function shift() {
if(snakeone.cellsx.length < snakeone.maxcells) {
snakeone.cellsx.unshift(snakeone.x);
snakeone.cellsy.unshift(snakeone.y);
}
}
function draw() {
for(i = 0; i < snakeone.maxcells; i++) {
ctx.fillStyle = color;
ctx.fillRect(snakeone.cellsx[i] + 1, snakeone.cellsy[i] + 1, unit - 2, unit - 2);
}
}
function pop() {
if(snakeone.cellsx.length > snakeone.maxcells) {
snakeone.cellsx.pop();
snakeone.cellsy.pop();
}
}
function timer() {
clear();
move();
shift();
draw();
pop();
}
setup();
setInterval(timer, 150);
body {
background-color: black;
text-align: center;
}
#canvas {
border: 2px solid white;
}
<html>
<head>
<title>
New Tab
</title>
<link href="snake.css" rel="stylesheet" type="text/css">
</head>
<body>
<canvas id="canvas"height="656" width="656"></canvas>
<script src="snake.js"></script>
</body>
</html>
`
Here's a working version of the code. I've made a few changes and simplified things a bit, but it might be helpful to you.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var unit = 16;
var color = "limegreen";
var snake = [];
var snakeone = {
x: 20 * unit,
y: 20 * unit
};
function clear() {
ctx.clearRect(0, 0, 400, 400);
}
function keyup(e) {
const code = e.keyCode;
const lastSegment = snake[snake.length - 1];
if (code == 37) {
// left arrow
snake.push({
...lastSegment,
x: lastSegment.x - 10
});
}
if (code == 38) {
// up arrow
snake.push({
...lastSegment,
y: lastSegment.y - 10
});
}
if (code == 39) {
// right arrow
snake.push({
...lastSegment,
x: lastSegment.x + 10
});
}
if (code == 40) {
// down arrow
snake.push({
...lastSegment,
y: lastSegment.y + 10
});
}
}
function draw() {
ctx.fillStyle = color;
snake.forEach(segment => {
ctx.fillRect(segment.x, segment.y, unit - 2, unit - 2);
});
}
function timer() {
clear();
draw();
}
document.onkeyup = keyup;
snake.push(snakeone);
setInterval(timer, 150);
body {
background-color: black;
text-align: center;
}
#canvas {
border: 2px solid white;
}
<html>
<head>
<title>
New Tab
</title>
</head>
<body>
<canvas id="canvas" height="400" width="400"></canvas>
</body>
</html>
Related
I've made a codepen in order to better explain my situation.
https://codepen.io/kennyfully1988/pen/yLqpBVp
I'm working on a game where the player collects apples. The collision is working correctly when a player touches an apple. (The player will get 1 point and the apple will be erased from the apples array).
What I'm confused about is the following function
checkSolidCollisions(walls) {
// check to see if player is colliding with walls
for (let i = 0; i < walls.length; i++) {
if (
this.dx <= walls[i].x + walls[i].width &&
this.dx + this.dw >= walls[i].x &&
this.dy <= walls[i].y + walls[i].height &&
this.dy + this.dh >= walls[i].y
) {
console.log(true);
return true;
}
}
return false;
}
I created this function hoping that this will be a check to see if the player is in collision with a wall (the wall array). However, it doesn't seem to work at all. I'm willing to provide as much information as needed in order to solve this problem. I want you all to know that this is OOP JavaScript.
So, what I tried to do is make the player not be able to pass the wall. However, the player always passes the wall.
In your example the
this.dx + this.dw >= walls[i].x
criteria proved to be problematic, because the right edge of the character is not to the right of the wall. A good way to check algorithmically is as follows:
checkSolidCollisions(walls) {
// check to see if player is colliding with walls
for (let i = 0; i < walls.length; i++) {
if (
!((this.dx + this.dw < walls[i].x) || (walls[i].x + walls[i].width < this.dx)) &&
!((this.dy + this.dh < walls[i].y) || (walls[i].y + walls[i].height < this.dy))
) {
console.log(true);
return true;
}
}
return false;
}
To summarize:
it is not true that
rectangle1 is to the left of rectangle2
or rectangle2 is to the left of rectangle1
and it is not true that
rectangle1 is above rectangle2
or rectangle2 is above rectangle1
So after a long time of testing everything out and rebuilding samples, I noticed what I did wrong. I didn't write the proper logic to see what happens after the player collides with the wall, such as pushing the player back into a safer position after the collision.
// Working code
'use strict';
window.onload = () => {
const gameBox = document.querySelector('.game-box');
const ctx = gameBox.getContext('2d');
const collisionCheckerLabel = document.querySelector(
'.collision-checker-label',
);
let collisionChecker = false;
const playerImage = new Image();
playerImage.src =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAApNJREFUeJztm7FuwjAQhg/Eg4QwsDEFiYGVZ2DhPXgG3oOl6iN0zVApnbpVqC0rb9Ahwh2QLcfY8V0S20W9fwGj5D7f1Q253wEgoPKsEHlWiJCMvhphDtKT+Dq/oc/5eB8DAMB8cSWdR2X10QQzGZkIAMB8UQjsxOaLK2kyetEkC4BWiLYVZ4tjDWwmricScwWYfF8Mk2m+t83jLqAZBAP2JdI3BqWQg64ATIC2iXRZAUPxsWoElOCnl1frwdvNCjWRrgmk4KsP8qwQErzdrBqTsI1twfokkJSfZ4WoTrWoTrXIs0IIIYT8zBxXp1o8lz933+9mDPneNf4r/DFYNJsuW8e2yut/vc/vSlXbHAMAHI5l68UqJv+uADKQ6/V8qVsn0yWBlPwxwO1/Qq+QXk05Pl9qBd/v1s4LUZcEUvK93wL6CfvdWk3YBOvLUEq/eJlxsBex0PxB7wP6JJCKH6TZiHkj8xf5LCn2A9gPYD9Anc9+APsB7Ac8jh/gU2g/ICZ/RIUD3G4vzfv5Pgmk5Fv9AKqoCfj8gJh8cgEw/XhIDc23+gEuMKYf9wnjB8TkB/UDXOBYfgCGz35AW8CuSt2Pp+azpNgPYD+A/QB1PvsB7AewH/BYfoC5J29OJLQfEItv9QPklvJsurQGw/TjlARS8p3tMGVL25WAq8PD+AGx+I0CbDerRuVc1ceIkkBK/gRA9eONKppwTDur7+e7lmCLH5CEz36A7WD2A3oqdT+ems+SYj+A/QD2A9T57AewH8B+wGP5ASY45u8FYvK9zwfYenPq/rwvgZR81PY45Xl9VwKU3wvE5HsLoPfmsl+nqos3EIuPej5APq9/OJZwOJak/XlMAin5/8YP0GOxH8B+AEvpF88gNOQKrAefAAAAAElFTkSuQmCC';
class Player {
constructor(config) {
this._rng = String(Math.floor(Math.random() * 100)).padEnd(3, '0'); // random number generator
this._id = `player_${new Date().getTime()}${this._rng}`; // generated id
this.image = playerImage;
this.currentFrame = 1; // helper variable to help with requestAnimationFrame()
this.totalFps = 60; // helper variable to help with requestAnimationFrame()
this.totalImageFrames = 4; // the number of frames in the sprite
this.sx = config?.sx || 0; // source x position
this.sy = config?.sy || 0; // source y position
this.sw = config?.sw || 16; // source width
this.sh = config?.sh || 16; // source height
this.dx = config?.dx || 0; //destination x position
this.dy = config?.dy || 0; //destination y position
this.dw = config?.dw || 16; //destination width
this.dh = config?.dh || 16; //destination height
this.animation = config?.animation || 0; // lazy way to play animations
this.movement = config?.movement || 'idle'; // player movement
window.addEventListener('keydown', (e) => {
e.preventDefault();
if (e.key === 'ArrowUp') {
this.movement = 'up';
} else if (e.key === 'ArrowDown') {
this.movement = 'down';
} else if (e.key === 'ArrowLeft') {
this.movement = 'left';
} else if (e.key === 'ArrowRight') {
this.movement = 'right';
} else {
this.movement = '';
}
});
window.addEventListener('keyup', (e) => {
e.preventDefault();
this.movement = 'idle';
});
}
render(ctx) {
ctx.drawImage(
this.image,
Math.floor(
this.currentFrame / (this.totalFps / this.totalImageFrames),
) * 16,
this.animation * 16,
this.sw,
this.sh,
this.dx,
this.dy,
this.dw,
this.dh,
);
}
update(ctx) {
// player animation
if (this.currentFrame >= this.totalFps - 1) {
this.currentFrame = 1;
} else {
this.currentFrame++;
}
// player movement
if (this.movement === 'up') {
this.animation = 1;
if (this.dy <= 0) return;
this.dy--;
} else if (this.movement === 'down') {
this.animation = 0;
if (this.dy + 16 >= ctx.canvas.height) return;
this.dy++;
} else if (this.movement === 'left') {
this.animation = 2;
if (this.dx <= 0) return;
this.dx--;
} else if (this.movement === 'right') {
this.animation = 3;
if (this.dx + 16 >= ctx.canvas.width) return;
this.dx++;
} else {
return;
}
}
}
class Rectangle {
constructor(config) {
this.color = config?.color || 'black';
this.x = config?.x || 0;
this.y = config?.y || 0;
this.width = config?.width || 16;
this.height = config?.height || 16;
}
render(ctx) {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
const rects = [
new Rectangle({ color: 'red', x: 32, y: 32 }),
new Rectangle({ color: 'green', x: 160 - 32, y: 160 - 32 }),
new Rectangle({ color: 'blue', x: 96, y: 96 }),
];
const player = new Player({ dx: 0, dy: 0 });
const renderScene = () => {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
rects.forEach((rect) => {
rect.render(ctx);
});
player.render(ctx);
player.update(ctx);
requestAnimationFrame(() => renderScene());
for (let i = 0; i < rects.length; i++) {
if (
//Check x
player.dx + player.dw > rects[i].x && // right side
player.dx < rects[i].x + rects[i].width && // left side
// Check y
player.dy + player.dh > rects[i].y && // bottom side
player.dy < rects[i].y + rects[i].height // top side
) {
collisionChecker = true;
collisionCheckerLabel.innerText = collisionChecker;
if (player.movement === 'up') {
player.dy++;
} else if (player.movement === 'down') {
player.dy--;
} else if (player.movement === 'left') {
player.dx++;
} else if (player.movement === 'right') {
player.dx--;
}
return;
}
collisionChecker = false;
collisionCheckerLabel.innerText = collisionChecker;
}
};
renderScene();
};
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: Arial, Helvetica, sans-serif;
}
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: lightgray;
height: 100vh;
}
.game-box {
border: 0.5rem solid black;
height: 90%;
image-rendering: pixelated;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no"
/>
<link rel="icon" type="image/x-icon" href="#" />
<link rel="stylesheet" href="app.css" />
<script defer src="app.js"></script>
<title>Rect Collisions</title>
</head>
<body>
<h1>Rect Collisions</h1>
<canvas class="game-box" width="160" height="160"></canvas>
<p>
Are the objects colliding? <span class="collision-checker-label">No</span>
</p>
</body>
</html>
I'm following a JavaScript tutorial from a german YouTuber. I have the same code as he has but for some reason, the program doesn't print "Collision!!!" into the console. I have the same code as he but my code does not work. His code it works. I don't know how to fix this. Please help!
Here is the URL to the video if anyone want to compare my and his code (It is already at the correct timestamp): https://youtu.be/eWLDAAMsD-c?t=5041
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Space Fight</title>
<style>
canvas {
background-color:rgba(0, 0, 0, 0.8);
}
</style>
<script>
let KEY_SPACE = false; // 32
let KEY_UP = false; // 38
let KEY_DOWN = false; // 40
let canvas;
let ctx;
let backgroundImage = new Image();
let divisor = 4
let rocket = {
x: 100,
y: 200,
width: 640 / divisor,
height: 366 / divisor,
src: "assets/img/rocket.png"
};
let ufos = []
document.onkeydown = function(e) {
if (e.keyCode == 32) { // SPACE KEY WAS PRESSED
KEY_SPACE = true;
}
if (e.keyCode == 38) { // UP-ARROW KEY WAS PRESSED
KEY_UP = true;
}
if (e.keyCode == 40) { // DOWN-ARROW KEY WAS PRESSED
KEY_DOWN = true;
}
}
document.onkeyup = function(e) {
if (e.keyCode == 32) { // SPACE KEY WAS RELEASED
KEY_SPACE = false;
}
if (e.keyCode == 38) { // UP-ARROW KEY WAS RELEASED
KEY_UP = false;
}
if (e.keyCode == 40) { // DOWN-ARROW KEY WAS RELEASED
KEY_DOWN = false;
}
}
function startGame() {
canvas = document.getElementById("canvas");
ctx = canvas.getContext('2d');
loadImages();
setInterval(update, 1000 / 60);
setInterval(createUfos, 5000);
setInterval(checkForCollision, 1000 / 60);
draw();
//calculate
}
function checkForCollision() {
ufos.forEach(function(ufo) {
if (
rocket.x + rocket.width > ufo.x &&
rocket.y + rocket.height > ufo.y &&
rocket.x < ufo.x &&
rocket.y < ufo.y
) {
console.log('Collision!!!');
}
});
}
function createUfos() {
let ufo = {
x: 800,
y: 200,
width: 640 / divisor,
height: 324 / divisor,
src: "assets/img/ufo.png",
img: new Image()
};
ufo.img.src = ufo.src;
ufos.push(ufo);
}
function update() {
if (KEY_UP) {
rocket.y -= 2;
}
if (KEY_DOWN) {
rocket.y += 2;
}
ufos.forEach(function(ufo){
ufo.x -= 4
});
}
function loadImages() {
backgroundImage.src = 'assets/img/background.jpg';
rocket.img = new Image();
rocket.img.src = rocket.src;
}
function draw() {
ctx.drawImage(backgroundImage, 0, 0);
ctx.drawImage(rocket.img, rocket.x, rocket.y, rocket.width, rocket.height);
ufos.forEach(function(ufo) {
ctx.drawImage(ufo.img, ufo.x, ufo.y, ufo.width, ufo.height);
})
requestAnimationFrame(draw);
}
</script>
</head>
<body onload="startGame()">
<canvas id="canvas" width="720" height="480"></canvas>
</body>
</html>
This is the file-structure: https://i.stack.imgur.com/iTl9E.png
Your collision check condition was not correct. Update it as follow.
(rocket.x + rocket.width > ufo.x &&
rocket.x < ufo.x) ||
(rocket.y + rocket.height > ufo.y &&
rocket.y < ufo.y)
Your code was like this.
rocket.x + rocket.width > ufo.x &&
rocket.y + rocket.height > ufo.y &&
rocket.x < ufo.x &&
rocket.y < ufo.y
You can check collision like this:
function dotInBox (dotX, dotY, box) {
const {x, y, w, h} = box
return x <= dotX && dotX <= x + w && y <= dotY && dotY <= y + h
}
if (dotInBox(b.x, b.y, a) ||
dotInBox(b.x + b.w, b.y, a) ||
dotInBox(b.x, b.y + b.h, a) ||
dotInBox(b.x + b.w, b.y + b.h, a)) {
// Collision
}
Function dotInBox checks if any dot in your square given by coordinates and dimensions. By checking every dot of other square we can say that collision happened.
I have a selection menu in my HTML canvas that I would like to trigger corresponding audio files. I have tried implementing this by declaring the images inside the if (this.hovered) & (this.clicked) part of the makeSelection function within the selectionForMenu prototype, such that on each new selection the selected audio file is redefined, but this causes problems like slow loading and overlapping audio. It is also problematic as I am trying to get the speaker button at the bottom of the screen to play the audio corresponding to the current selection too, so if it is only defined within that function it is not accessible to the makeButton function.
You can see the selection menu and speaker button in the snippet below. Each new selection in the menu should play once an audio file that corresponds to it (which I have not been able to add to this demonstration). It can be replayed by re-clicking the selection or clicking the speaker button, but each click should only provoke one play of the audio and of course overlapping is undesired. Any help will be appreciated.
var c=document.getElementById('game'),
canvasX=c.offsetLeft,
canvasY=c.offsetTop,
ctx=c.getContext('2d');
var button = function(id, x, strokeColor) {
this.id = id;
this.x = x;
this.strokeColor = strokeColor;
this.hovered = false;
this.clicked = false;
}
button.prototype.makeInteractiveButton = function() {
if (this.hovered) {
if (this.clicked) {
this.fillColor = '#DFBCDE';
} else {
this.fillColor = '#CA92C8'
}
} else {
this.fillColor = '#BC77BA'
}
ctx.strokeStyle=this.strokeColor;
ctx.fillStyle=this.fillColor;
ctx.beginPath();
ctx.lineWidth='5';
ctx.arc(this.x, 475, 20, 0, 2*Math.PI);
ctx.closePath();
ctx.stroke();
ctx.fill();
}
button.prototype.hitTest = function(x, y) {
return (Math.pow(x-this.x, 2) + Math.pow(y-475, 2) < Math.pow(20, 2));
}
var selectionForMenu = function(id, text, y) {
this.id = id;
this.text = text;
this.y = y;
this.hovered = false;
this.clicked = false;
this.lastClicked = false;
}
selectionForMenu.prototype.makeSelection = function() {
var fillColor='#A84FA5';
if (this.hovered) {
if (this.clicked) {
if (this.lastClicked) {
fillColor='#E4C7E2';
} else {
fillColor='#D5A9D3';
}
} else if (this.lastClicked) {
fillColor='#D3A4D0';
} else {
fillColor='#BA74B7';
}
} else if (this.lastClicked) {
fillColor='#C78DC5';
} else {
fillColor='#A84FA5';
}
ctx.beginPath();
ctx.fillStyle=fillColor;
ctx.fillRect(0, this.y, 350, 30)
ctx.stroke();
ctx.font='10px Noto Sans';
ctx.fillStyle='white';
ctx.textAlign='left';
ctx.fillText(this.text, 10, this.y+19);
}
selectionForMenu.prototype.hitTest = function(x, y) {
return (x >= 0) && (x <= (350)) && (y >= this.y) && (y <= (this.y+30)) && !((x >= 0) && (y > 450));
}
var Paint = function(element) {
this.element = element;
this.shapes = [];
}
Paint.prototype.addShape = function(shape) {
this.shapes.push(shape);
}
Paint.prototype.render = function() {
ctx.clearRect(0, 0, this.element.width, this.element.height);
for (var i=0; i<this.shapes.length; i++) {
try {
this.shapes[i].makeSelection();
}
catch(err) {}
}
ctx.beginPath();
ctx.fillStyle='#BC77BA';
ctx.fillRect(0, 450, 750, 50);
ctx.stroke();
for (var i=0; i<this.shapes.length; i++) {
try {
this.shapes[i].makeInteractiveButton();
}
catch(err) {}
}
var speaker = new Image(25, 25);
speaker.src='https://i.stack.imgur.com/lXg2I.png';
ctx.drawImage(speaker, 162.5, 462.5);
}
Paint.prototype.setHovered = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].hovered = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.setClicked = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].clicked = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.setUnclicked = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].clicked = false;
if (Number.isInteger(this.shapes[i].id)) {
this.shapes[i].lastClicked = this.shapes[i] == shape;
}
}
this.render();
}
Paint.prototype.select = function(x, y) {
for (var i=this.shapes.length-1; i >= 0; i--) {
if (this.shapes[i].hitTest(x, y)) {
return this.shapes[i];
}
}
return null
}
var paint = new Paint(c);
var btn = new button('speaker', 175, '#FFFCF8');
var selection = [];
for (i=0; i<15; i++) {
selection.push(new selectionForMenu(i+1, i, i*30));
}
paint.addShape(btn);
for (i=0; i<15; i++) {
paint.addShape(selection[i])
}
paint.render();
function mouseDown(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
paint.setClicked(shape);
}
function mouseUp(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
paint.setUnclicked(shape);
}
function mouseMove(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
paint.setHovered(shape);
}
c.addEventListener('mousedown', mouseDown);
c.addEventListener('mouseup', mouseUp);
c.addEventListener('mousemove', mouseMove);
canvas {
z-index: -1;
margin: 1em auto;
border: 1px solid black;
display: block;
background: #9F3A9B;
}
img {
z-index: 0;
position: absolute;
pointer-events: none;
}
#speaker {
top: 480px;
left: 592px;
}
#snail {
top: 475px;
left: 637.5px;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>uTalk Demo</title>
<link rel='stylesheet' type='text/css' href='wordpractice.css' media='screen'></style>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
</head>
<body>
<canvas id="game" width = "350" height = "500"></canvas>
<script type='text/javascript' src='wordpractice copy.js'></script>
</body>
</html>
When you want responsiveness with audio, forget about MediaElements, and go with the Web Audio API.
MediaElements (<audio> and <video>) are slow, and http caching is an nightmare.
With the Web Audio API, you can first download all you media as arrayBuffers, decode their audio data to AudioBuffers, that you'll attach to your js objects.
From there, you'll be able to play new instances of these media in µs.
Beware, ES6 syntax below, for older browsers, here is an ES5 rewrite, also note that Internet Explorer < Edge does not support the Web Audio API, if you need to support these browsers, you'll have to make an fallback with audio elements.
(function myFirstDrumKit() {
const db_url = 'https://dl.dropboxusercontent.com/s/'; // all our medias are stored on dropbox
// we'll need to first load all the audios
function initAudios() {
const promises = drum.parts.map(part => {
return fetch(db_url + part.audio_src) // fetch the file
.then(resp => resp.arrayBuffer()) // as an arrayBuffer
.then(buf => drum.a_ctx.decodeAudioData(buf)) // then decode its audio data
.then(AudioBuf => {
part.buf = AudioBuf; // store the audioBuffer (won't change)
return Promise.resolve(part); // done
});
});
return Promise.all(promises); // when all are loaded
}
function initImages() {
// in this version we have only an static image,
// but we could have multiple per parts, with the same logic as for audios
var img = new Image();
img.src = db_url + drum.bg_src;
drum.bg = img;
return new Promise((res, rej) => {
img.onload = res;
img.onerror = rej;
});
}
let general_solo = false;
let part_solo = false;
const drum = {
a_ctx: new AudioContext(),
generate_sound: (part) => {
// called each time we need to play a source
const source = drum.a_ctx.createBufferSource();
source.buffer = part.buf;
source.connect(drum.gain);
// to keep only one playing at a time
// simply store this sourceNode, and stop the previous one
if(general_solo){
// stop all playing sources
drum.parts.forEach(p => (p.source && p.source.stop(0)));
}
else if (part_solo && part.source) {
// stop only the one of this part
part.source.stop(0);
}
// store the source
part.source = source;
source.start(0);
},
parts: [{
name: 'hihat',
x: 90,
y: 116,
w: 160,
h: 70,
audio_src: 'kbgd2jm7ezk3u3x/hihat.mp3'
},
{
name: 'snare',
x: 79,
y: 192,
w: 113,
h: 58,
audio_src: 'h2j6vm17r07jf03/snare.mp3'
},
{
name: 'kick',
x: 80,
y: 250,
w: 200,
h: 230,
audio_src: '1cdwpm3gca9mlo0/kick.mp3'
},
{
name: 'tom',
x: 290,
y: 210,
w: 110,
h: 80,
audio_src: 'h8pvqqol3ovyle8/tom.mp3'
}
],
bg_src: '0jkaeoxls18n3y5/_drumkit.jpg?dl=0',
};
drum.gain = drum.a_ctx.createGain();
drum.gain.gain.value = .5;
drum.gain.connect(drum.a_ctx.destination);
function initCanvas() {
const c = drum.canvas = document.createElement('canvas');
const ctx = drum.ctx = c.getContext('2d');
c.width = drum.bg.width;
c.height = drum.bg.height;
ctx.drawImage(drum.bg, 0, 0);
document.body.appendChild(c);
addEvents(c);
}
const isHover = (x, y) =>
(drum.parts.filter(p => (p.x < x && p.x + p.w > x && p.y < y && p.y + p.h > y))[0] || false);
function addEvents(canvas) {
let mouse_hovered = false;
canvas.addEventListener('mousemove', e => {
mouse_hovered = isHover(e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop)
if (mouse_hovered) {
canvas.style.cursor = 'pointer';
} else {
canvas.style.cursor = 'default';
}
})
canvas.addEventListener('mousedown', e => {
e.preventDefault();
if (mouse_hovered) {
drum.generate_sound(mouse_hovered);
}
});
const checkboxes = document.querySelectorAll('input');
checkboxes[0].onchange = function() {
general_solo = this.checked;
general_solo && (checkboxes[1].checked = part_solo = true);
};
checkboxes[1].onchange = function() {
part_solo = this.checked;
!part_solo && (checkboxes[0].checked = general_solo = false);
};
}
Promise.all([initAudios(), initImages()])
.then(initCanvas);
})()
/*
Audio Samples are from https://sampleswap.org/filebrowser-new.php?d=DRUMS+%28FULL+KITS%29%2FSpasm+Kit%2F
Original image is from http://truimg.toysrus.co.uk/product/images/UK/0023095_CF0001.jpg?resize=500:500
*/
<label>general solo<input type="checkbox"></label><br>
<label>part solo<input type="checkbox"></label><br>
You could create an Audio Loader, that loads all the audios and keeps track of them:
function load(srcs){
var obj={};
srcs.forEach(src=>obj[src]=new Audio(src));
return obj;
}
Then you could do sth like this onload:
var audios=load(["audio1.mp3", "audio2.mp3"]);
And later:
(audios[src] || new Audio(src)).play();
This will just load the audio if it isnt already in the audios object.
I tried to make it so that when my 'Player' reaches a jumpDistance of 50, it falls down, so he makes a small ' jump ' .
The code might not be exactly "clean" at this point, but I'm getting started with Javascript.
I made the player jump by using a for loop with a delay. I tried to make him go down the same way, but this didn't work out the way I planned.
Fiddle demo
** NOTE : Press space to start!
<!DOCTYPE html>
<html>
<style>
#canvas {
background-color: rgba(177, 177, 177, 1);
}
</style>
<body>
<div>
<p id="jumpDistance"></p>
<p id="jumpDirection"></p>
</div>
<canvas id="canvas" width="800" height="400"></canvas>
<script>
var canvas = document.querySelector('#canvas');
var context = canvas.getContext('2d');
var xPos = 150;
var yPos = 375;
var jumpDistance = 0;
function spelerObj() {
canvas.width=canvas.width;
context.rect(xPos, yPos, 25, 25);
context.stroke();
context.fillStyle = "#FF0000";
context.fillRect(xPos, yPos, 25, 25);
}
function jump(e) { //Here the player jumps, with a loop that calculates it's jump-distance.
//alert(e.keyCode);
if (e.keyCode == 32) {//
function upLoop() {
setTimeout(function () {
if(jumpDistance < 50) {
yPos -= 1;
jumpDistance++;
upLoop();
spelerObj();
document.getElementById("jumpDistance").innerHTML = jumpDistance.toString();
}
}, 1)
}
upLoop();
spelerObj();
}
}
document.onkeydown = jump;
</script>
</body>
</html>
You'd need a downloop that you can switch to at the top of the jump:
function upLoop() {
setTimeout(function() {
if (jumpDistance < 50) {
yPos -= 1;
jumpDistance++;
upLoop();
} else {
downLoop();
}
spelerObj();
document.getElementById("jumpDistance").innerHTML = jumpDistance.toString();
}, 1)
}
function downLoop() {
setTimeout(function() {
if (jumpDistance > 0) {
yPos += 1;
jumpDistance--;
downLoop();
}
spelerObj();
document.getElementById("jumpDistance").innerHTML = jumpDistance.toString();
}, 1)
}
Demo 1
You could also vary the timeout duration to add a pseudo-gravity effect.
Demo 2
I found this game online and am trying to get it to run from my desktop. Is there something special that a person has to do with the URL's or images to make the file recognize were everything is running and located at. I have all of the files and .png files in one folder and on the same level.
I would think that I should see the game on the screen. It is like a left to right horizontal scroll-er with enemy ships that come out of the right side of the screen and the main ship is on the right side of the screen. (Similar to that of the old style defender game)
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="app.css">
</head>
<body>
<div id="game-over-overlay"></div>
<div id="game-over">
<h1>GAME OVER</h1>
<button id="play-again">Play Again</button>
</div>
<div class="wrapper">
<div id="instructions">
<div>
move with <span class="key">arrows</span> or <span class="key">wasd</span>
</div>
<div>
shoot with <span class="key">space</span>
</div>
</div>
<div id="score"></div>
</div>
<script type="text/javascript" src="resources.js"></script>
<script type="text/javascript" src="input.js"></script>
<script type="text/javascript" src="sprite.js"></script>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
sprite.js
(function() {
function Sprite(url, pos, size, speed, frames, dir, once) {
this.pos = pos;
this.size = size;
this.speed = typeof speed === 'number' ? speed : 0;
this.frames = frames;
this._index = 0;
this.url = url;
this.dir = dir || 'horizontal';
this.once = once;
};
Sprite.prototype = {
update: function(dt) {
this._index += this.speed*dt;
},
render: function(ctx) {
var frame;
if(this.speed > 0) {
var max = this.frames.length;
var idx = Math.floor(this._index);
frame = this.frames[idx % max];
if(this.once && idx >= max) {
this.done = true;
return;
}
}
else {
frame = 0;
}
var x = this.pos[0];
var y = this.pos[1];
if(this.dir == 'vertical') {
y += frame * this.size[1];
}
else {
x += frame * this.size[0];
}
ctx.drawImage(resources.get(this.url),
x, y,
this.size[0], this.size[1],
0, 0,
this.size[0], this.size[1]);
}
};
window.Sprite = Sprite;
resources.js
(function() {
var resourceCache = {};
var loading = [];
var readyCallbacks = [];
// Load an image url or an array of image urls
function load(urlOrArr) {
if(urlOrArr instanceof Array) {
urlOrArr.forEach(function(url) {
_load(url);
});
}
else {
_load(urlOrArr);
}
}
function _load(url) {
if(resourceCache[url]) {
return resourceCache[url];
}
else {
var img = new Image();
img.onload = function() {
resourceCache[url] = img;
if(isReady()) {
readyCallbacks.forEach(function(func) { func(); });
}
};
resourceCache[url] = false;
img.src = url;
}
}
function get(url) {
return resourceCache[url];
}
function isReady() {
var ready = true;
for(var k in resourceCache) {
if(resourceCache.hasOwnProperty(k) &&
!resourceCache[k]) {
ready = false;
}
}
return ready;
}
function onReady(func) {
readyCallbacks.push(func);
}
window.resources = {
load: load,
get: get,
onReady: onReady,
isReady: isReady
};
input.js
(function() {
var pressedKeys = {};
function setKey(event, status) {
var code = event.keyCode;
var key;
switch(code) {
case 32:
key = 'SPACE'; break;
case 37:
key = 'LEFT'; break;
case 38:
key = 'UP'; break;
case 39:
key = 'RIGHT'; break;
case 40:
key = 'DOWN'; break;
default:
key = String.fromCharCode(code);
}
pressedKeys[key] = status;
}
document.addEventListener('keydown', function(e) {
setKey(e, true);
});
document.addEventListener('keyup', function(e) {
setKey(e, false);
});
window.addEventListener('blur', function() {
pressedKeys = {};
});
window.input = {
isDown: function(key) {
return pressedKeys[key.toUpperCase()];
}
};
apps.js
// A cross-browser requestAnimationFrame
// See https://hacks.mozilla.org/2011/08/animating-with-javascript-from-setinterval-to-requestanimationframe/
var requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 480;
document.body.appendChild(canvas);
// The main game loop
var lastTime;
function main() {
var now = Date.now();
var dt = (now - lastTime) / 1000.0;
update(dt);
render();
lastTime = now;
requestAnimFrame(main);
};
function init() {
terrainPattern = ctx.createPattern(resources.get('terrain.png'), 'repeat');
document.getElementById('play-again').addEventListener('click', function() {
reset();
});
reset();
lastTime = Date.now();
main();
}
resources.load([
'sprites.png',
'terrain.png'
]);
resources.onReady(init);
// Game state
var player = {
pos: [0, 0],
sprite: new Sprite('sprites.png', [0, 0], [39, 39], 16, [0, 1])
};
var bullets = [];
var enemies = [];
var explosions = [];
var lastFire = Date.now();
var gameTime = 0;
var isGameOver;
var terrainPattern;
var score = 0;
var scoreEl = document.getElementById('score');
// Speed in pixels per second
var playerSpeed = 200;
var bulletSpeed = 500;
var enemySpeed = 100;
// Update game objects
function update(dt) {
gameTime += dt;
handleInput(dt);
updateEntities(dt);
// It gets harder over time by adding enemies using this
// equation: 1-.993^gameTime
if(Math.random() < 1 - Math.pow(.993, gameTime)) {
enemies.push({
pos: [canvas.width,
Math.random() * (canvas.height - 39)],
sprite: new Sprite('sprites.png', [0, 78], [80, 39],
6, [0, 1, 2, 3, 2, 1])
});
}
checkCollisions();
scoreEl.innerHTML = score;
};
function handleInput(dt) {
if(input.isDown('DOWN') || input.isDown('s')) {
player.pos[1] += playerSpeed * dt;
}
if(input.isDown('UP') || input.isDown('w')) {
player.pos[1] -= playerSpeed * dt;
}
if(input.isDown('LEFT') || input.isDown('a')) {
player.pos[0] -= playerSpeed * dt;
}
if(input.isDown('RIGHT') || input.isDown('d')) {
player.pos[0] += playerSpeed * dt;
}
if(input.isDown('SPACE') && !isGameOver && Date.now() - lastFire > 100) {
var x = player.pos[0] + player.sprite.size[0] / 2;
var y = player.pos[1] + player.sprite.size[1] / 2;
bullets.push({ pos: [x, y],
dir: 'forward',
sprite: new Sprite('sprites.png', [0, 39], [18, 8]) });
bullets.push({ pos: [x, y],
dir: 'up',
sprite: new Sprite('sprites.png', [0, 50], [9, 5]) });
bullets.push({ pos: [x, y],
dir: 'down',
sprite: new Sprite('sprites.png', [0, 60], [9, 5]) });
lastFire = Date.now();
}
}
function updateEntities(dt) {
// Update the player sprite animation
player.sprite.update(dt);
// Update all the bullets
for(var i=0; i<bullets.length; i++) {
var bullet = bullets[i];
switch(bullet.dir) {
case 'up': bullet.pos[1] -= bulletSpeed * dt; break;
case 'down': bullet.pos[1] += bulletSpeed * dt; break;
default:
bullet.pos[0] += bulletSpeed * dt;
}
// Remove the bullet if it goes offscreen
if(bullet.pos[1] < 0 || bullet.pos[1] > canvas.height ||
bullet.pos[0] > canvas.width) {
bullets.splice(i, 1);
i--;
}
}
// Update all the enemies
for(var i=0; i<enemies.length; i++) {
enemies[i].pos[0] -= enemySpeed * dt;
enemies[i].sprite.update(dt);
// Remove if offscreen
if(enemies[i].pos[0] + enemies[i].sprite.size[0] < 0) {
enemies.splice(i, 1);
i--;
}
}
// Update all the explosions
for(var i=0; i<explosions.length; i++) {
explosions[i].sprite.update(dt);
// Remove if animation is done
if(explosions[i].sprite.done) {
explosions.splice(i, 1);
i--;
}
}
}
// Collisions
function collides(x, y, r, b, x2, y2, r2, b2) {
return !(r <= x2 || x > r2 ||
b <= y2 || y > b2);
}
function boxCollides(pos, size, pos2, size2) {
return collides(pos[0], pos[1],
pos[0] + size[0], pos[1] + size[1],
pos2[0], pos2[1],
pos2[0] + size2[0], pos2[1] + size2[1]);
}
function checkCollisions() {
checkPlayerBounds();
// Run collision detection for all enemies and bullets
for(var i=0; i<enemies.length; i++) {
var pos = enemies[i].pos;
var size = enemies[i].sprite.size;
for(var j=0; j<bullets.length; j++) {
var pos2 = bullets[j].pos;
var size2 = bullets[j].sprite.size;
if(boxCollides(pos, size, pos2, size2)) {
// Remove the enemy
enemies.splice(i, 1);
i--;
// Add score
score += 100;
// Add an explosion
explosions.push({
pos: pos,
sprite: new Sprite('sprites.png',
[0, 117],
[39, 39],
16,
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
null,
true)
});
// Remove the bullet and stop this iteration
bullets.splice(j, 1);
break;
}
}
if(boxCollides(pos, size, player.pos, player.sprite.size)) {
gameOver();
}
}
}
function checkPlayerBounds() {
// Check bounds
if(player.pos[0] < 0) {
player.pos[0] = 0;
}
else if(player.pos[0] > canvas.width - player.sprite.size[0]) {
player.pos[0] = canvas.width - player.sprite.size[0];
}
if(player.pos[1] < 0) {
player.pos[1] = 0;
}
else if(player.pos[1] > canvas.height - player.sprite.size[1]) {
player.pos[1] = canvas.height - player.sprite.size[1];
}
}
// Draw everything
function render() {
ctx.fillStyle = terrainPattern;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Render the player if the game isn't over
if(!isGameOver) {
renderEntity(player);
}
renderEntities(bullets);
renderEntities(enemies);
renderEntities(explosions);
};
function renderEntities(list) {
for(var i=0; i<list.length; i++) {
renderEntity(list[i]);
}
}
function renderEntity(entity) {
ctx.save();
ctx.translate(entity.pos[0], entity.pos[1]);
entity.sprite.render(ctx);
ctx.restore();
}
// Game over
function gameOver() {
document.getElementById('game-over').style.display = 'block';
document.getElementById('game-over-overlay').style.display = 'block';
isGameOver = true;
}
// Reset game to original state
function reset() {
document.getElementById('game-over').style.display = 'none';
document.getElementById('game-over-overlay').style.display = 'none';
isGameOver = false;
gameTime = 0;
score = 0;
enemies = [];
bullets = [];
player.pos = [50, canvas.height / 2];
app.css
html, body {
margin: 0;
padding: 0;
background-color: #151515;
}
canvas {
display: block;
margin: auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.wrapper {
width: 512px;
margin: 0 auto;
margin-top: 2em;
}
#instructions {
float: left;
font-family: sans-serif;
color: #757575;
}
#score {
float: right;
color: white;
font-size: 2em;
}
.key {
color: #aaffdd;
}
#game-over, #game-over-overlay {
margin: auto;
width: 512px;
height: 480px;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
display: none;
}
#game-over-overlay {
background-color: black;
opacity: .5;
}
#game-over {
height: 200px;
text-align: center;
color: white;
}
#game-over h1 {
font-size: 3em;
font-family: sans-serif;
}
#game-over button {
font-size: 1.5em;
}
Here is the original game link
http://jlongster.com/Making-Sprite-based-Games-with-Canvas
If someone could put up a fiddle to see why it is not working it would be most appreciated.
Errors from the console
Uncaught SyntaxError: Unexpected end of input resources.js:61
Uncaught SyntaxError: Unexpected end of input input.js:43
Uncaught SyntaxError: Unexpected end of input sprite.js:54
Uncaught SyntaxError: Unexpected end of input app.js:302
Are you running this on a webserver? The way that you phrased it makes me think you you downloaded those files and just double-clicked index.html.
You need to download something like https://www.apachefriends.org/index.html and start the Apache service up, then put your files in the /xampp/htdocs/ folder... then goto
http://localhost/index.html
to get it to load.