Hi I'm new here and also in the programming world, so, I have a little problem: I'm making a simple game engine in JS because I want to replicate the original Space Invaders game, the problem is just the bullet is not moving, only appears and disappears, like a laser shot. I want to have a moving bullet. Anyone can help me?
P.S. I don't wanna use object oriented programming here, I want to make the whole game in a single .js file.
console.log('GAME LOADED//SPACE INVADERS');
////////////////////////////////////////////////
let screen = document.createElement('canvas');
screen.width = 800;
screen.height = 900;
document.body.appendChild(screen);
document.addEventListener('keydown', keyDown, false);
document.addEventListener('keyup', keyUp, false);
let ctx = screen.getContext('2d');
////////////////////////////////////////////////
let x = screen.width / 2;
let y = screen.height / 2;
let bulletY = screen.height - 20;
let bulletSpeed = 10;
let bulletLifeTime = 3;
let size = 20;
let sWidth = screen.width;
let sHeight = screen.height;
let columns = screen.width / size;
let rows = screen.height / size;
let speed = 5;
let right = false;
let left = false;
let up = false;
let down = false;
let shot = false;
let gravity = 10;
let jumpForce = 40 ;
/////////////////////////////////////////////////
function keyDown() {
switch(event.keyCode) {
case 39: right = true;
break;
case 37: left = true;
break;
//case 40: up = true;
//break;
//case 38: down = true;
//break;
case 32: shot = true;
break;
}
}
function keyUp() {
switch(event.keyCode) {
case 39: right = false;
break;
case 37: left = false;
break;
//case 40: up = false;
//break;
//case 38: down = false;
//break;
case 32: shot = false;
break;
}
}
function move() {
if(right == true) {
x += 1 * speed;
} else if(left == true) {
x -= 1 * speed;
}
//if(up == true) {
//y += 1 * speed;
//} else if(down == true) {
//y -= 1 * speed;
//}
}
function player() {
ctx.fillStyle = '#1CE80D';
ctx.fillRect(x, y, size, size);
ctx.fillRect(x+10, y, size, size);
ctx.fillRect(x+20, y, size, size);
ctx.fillRect(x-10, y, size, size);
ctx.fillRect(x-20, y, size, size);
ctx.fillRect(x+5, y-1, 10, 10);
ctx.fillRect(x+5, y-2, 10, 10);
ctx.fillRect(x+5, y-3, 10, 10);
ctx.fillRect(x+5, y-4, 10, 10);
ctx.fillRect(x+5, y-5, 10, 10);
ctx.fillRect(x+5, y-6, 10, 10);
ctx.fillRect(x+5, y-7, 10, 10);
ctx.fillRect(x+9, y-8, 2, 2);
ctx.fillRect(x+9, y-9, 2, 2);
ctx.fillRect(x+9, y-10, 2, 2);
ctx.fillStyle = 'black';
ctx.fillRect(x+35, y, 5, 5);
ctx.fillRect(x-20, y, 5, 5);
}
function enemy1() {
ctx.fillStyle = '#1CE80D';
ctx.fillRect(444, 375, 5, 5);
ctx.fillRect(411, 375, 5, 5);
ctx.fillRect(416, 380, 5, 5);
ctx.fillRect(439, 380, 5, 5);
ctx.fillRect(410, 385, 40, 5);
ctx.fillRect(405, 390, 50, 5);
ctx.fillRect(400, 395, 60, 5);
ctx.fillRect(400, 400, 60, 5);
ctx.fillRect(400, 405, 5, 5);
ctx.fillRect(455, 405, 5, 5);
ctx.fillRect(410, 405, 40, 5);
ctx.fillStyle = '#001119';
ctx.fillRect(415, 405, 30, 5);
ctx.fillStyle = '#1CE80D';
ctx.fillRect(415, 410, 30, 5);
ctx.fillStyle = '#001119';
ctx.fillRect(440, 390, 5, 5);
ctx.fillRect(414, 390, 5, 5);
ctx.fillRect(428, 410, 4, 5);
}
function draw() {
ctx.fillStyle = '#001119';
let background = ctx.fillRect(0, 0, sWidth, sHeight);
player();
ctx.fillStyle = '#EAEEB7';
let wall1 = ctx.fillRect(0, 0, 10, sHeight);
let wall2 = ctx.fillRect(0, 0, sHeight, 10);
let wall3 = ctx.fillRect(0, sHeight - 10, sWidth, 10);
let wall4 = ctx.fillRect(sWidth - 10, 0, 10, sHeight);
}
function gravityForce() {
y += gravity;
}
function jumpAct() {
if(jump == true) {
y -= jumpForce;
} else if(jump == false) {
gravityForce();
}
}
function bulletDraw() {
let bulletX = x;
ctx.fillStyle = '#1CE80D';
ctx.fillRect(bulletX + 9, bulletY, 1, 8);
}
function bulletPos() {
bulletY -= bulletSpeed;
}
function attack() {
if(shot == true) {
for(let i = screen.height; i > 0; i--) {
bulletDraw();
bulletPos();
}
console.log('shooting');
} else if(shot == false) {
bulletY = 880;
}
}
function gameLoop() {
ctx.clearRect(0, 0, screen.width, screen.height);
draw();
move();
gravityForce();
attack();
enemy1();
bulletPos();
requestAnimationFrame(gameLoop);
if(x > screen.width - size - 30) {
x = 30
} else if(x < 30) {
x = screen.width - 50;
}
if(y > screen.height - size - 10) {
y -= (size - 10);
} else if(y < 0) {
y += size;
}
}
////////////////////////////////////////////////
requestAnimationFrame(gameLoop);
I think you have to manage both timer interval and canvas redraw function. I don't thing it is a good idea to redraw the entire canvas in each interval, instead you can redraw only the required canvas area. I have modified your code with setTimeout() function without redraw the entire canvas. You can see the complete code in this fiddler path https://jsfiddle.net/SyamKumarD/knyt7shf/18/
Some of the key code you need to check
1. setTimeout() method
2. hitTarget() method and its instantiation
3. Key down and up events
Hope this will helps
Related
Good afternoon.
I'm currently developing a simple 2D game using HTML5. The objective of the game is the following: the player (a green ball of size "n") has to move around the map (using the right, left, up and down arrow) and collect the smaller-sized balls. Every time the user collects a ball, its size increases a 25%. The game finishes when the ball gets so big that it's barely impossible to see any balls in the map, in which case a "Game over" text is loaded onto the canvas.
The problem that I'm having is that, in order to know if a player has collected a certain ball I calculate the distance between the user and said ball. If it is less than 3, it counts as collected. To calculate that distance I use a math formula. The thing is, it doesn't work properly. Sometimes the ball disappears when the user is definitely further than a distance of 3. I would like to modify the code so that the ball disappears only when the user touches it. I tried by putting distance === 0, but that's still giving unexpected results.
Here's the source code:
const canvas = document.getElementById("gameArea");
const ctx = canvas.getContext("2d");
let x = 100;
let y = 100;
let radius = 50;
let speed = 10;
let upPressed = false;
let downPressed = false;
let leftPressed = false;
let rightPressed = false;
let gameOver = false;
let points = [ [300, 500], [400, 700], [100, 600], [469, 586], [578, 234], [587, 489] ];
var gAccess = 0;
var multiplier = 1;
let counter = 0;
function drawGame() {
requestAnimationFrame(drawGame);
clearScreen();
inputs();
boundryCheck();
drawGreenBlob();
drawNextTarget();
checkCollision();
}
function checkCollision() {
let b = points[gAccess][0] - x;
let a = points[gAccess][1] - y;
var c = Math.sqrt(a * a + b * b);
if (c < 220) {
gAccess = (gAccess + 1) % points.length;
multiplier += 0.25;
counter++;
console.log(counter);
}
if (counter >= 25) {
gameover = true;
const context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
drawgameOver();
}
}
function drawNextTarget() {
if (gameOver === true) return;
ctx.beginPath();
ctx.arc(points[gAccess][0], points[gAccess][0], radius / 3, 0, Math.PI * 2);
ctx.fill();
}
function drawgameOver() {
var ctx = canvas.getContext("2d");
var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
grd.addColorStop(0, "black");
grd.addColorStop(1, "gray");
// Fill with gradient
ctx.fillStyle = grd;
ctx.fillRect(0, 0, canvas.width, canvas.height);
var ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
ctx.font = "36px Arial";
ctx.fillText("Game Over!", 50, 50);
}
function boundryCheck() {
//up
if (y < radius) {
y = radius;
}
//down
if (y > canvas.height - radius) {
y = canvas.height - radius;
}
//left
if (x < radius) {
x = radius;
}
//right
if (x > canvas.width - radius) {
x = canvas.width - radius;
}
}
function inputs() {
if (upPressed) {
y = y - speed;
}
if (downPressed) {
y = y + speed;
}
if (leftPressed) {
x = x - speed;
}
if (rightPressed) {
x = x + speed;
}
}
function drawGreenBlob() {
ctx.fillStyle = "green";
if (upPressed) {
ctx.fillStyle = "red";
}
if (downPressed) {
ctx.fillStyle = "blue";
}
if (leftPressed) {
ctx.fillStyle = "yellow";
}
if (rightPressed) {
ctx.fillStyle = "purple";
}
ctx.beginPath();
ctx.arc(x, y, radius * multiplier, 0, Math.PI * 2);
ctx.fill();
}
function clearScreen() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
document.body.addEventListener("keydown", keyDown);
document.body.addEventListener("keyup", keyUp);
function keyDown(event) {
//up
if (event.keyCode == 38) {
upPressed = true;
}
//down
if (event.keyCode == 40) {
downPressed = true;
}
//left
if (event.keyCode == 37) {
leftPressed = true;
}
//right
if (event.keyCode == 39) {
rightPressed = true;
}
}
function keyUp(event) {
//up
if (event.keyCode == 38) {
upPressed = false;
}
//down
if (event.keyCode == 40) {
downPressed = false;
}
//left
if (event.keyCode == 37) {
leftPressed = false;
}
//right
if (event.keyCode == 39) {
rightPressed = false;
}
}
drawGame();
<canvas id="gameArea" width=800 height=800></canvas>
Thank you very much for your help and for your attention.
Just like #ChrisG mentioned you need to include the radius on the checkCollision if statement:
if (c < radius * multiplier + radius / 3) {
the radius * multiplier is the radius of our player
the radius / 3 is the radius of the balls
This is technically making the collision when the two perimeters touch if you want to make something different you can change that math, for example if instead of a plus we change it to a minus we create the illusion on the player swallowing the balls.
You also have a bug on the function drawNextTarget() you did not pick the right coordinate:
ctx.arc(points[gAccess][0], points[gAccess][0],
VS
ctx.arc(points[gAccess][0], points[gAccess][1],
All looks good now:
const canvas = document.getElementById("gameArea");
const ctx = canvas.getContext("2d");
let x = 100;
let y = 100;
let radius = 50;
let speed = 10;
let upPressed = false;
let downPressed = false;
let leftPressed = false;
let rightPressed = false;
let gameOver = false;
let points = [ [300, 500], [400, 700], [100, 600], [469, 586], [578, 234], [587, 489] ];
var gAccess = 0;
var multiplier = 1;
let counter = 0;
function drawGame() {
requestAnimationFrame(drawGame);
clearScreen();
inputs();
boundryCheck();
drawGreenBlob();
drawNextTarget();
checkCollision();
}
function checkCollision() {
let b = points[gAccess][0] - x;
let a = points[gAccess][1] - y;
var c = Math.sqrt(a * a + b * b);
if (c < radius * multiplier + radius / 3) {
gAccess = (gAccess + 1) % points.length;
multiplier += 0.25;
counter++;
console.log(counter);
}
if (counter >= 25) {
gameover = true;
const context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
drawgameOver();
}
}
function drawNextTarget() {
if (gameOver === true) return;
ctx.beginPath();
ctx.arc(points[gAccess][0], points[gAccess][1], radius / 3, 0, Math.PI * 2);
ctx.fill();
}
function drawgameOver() {
var ctx = canvas.getContext("2d");
var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
grd.addColorStop(0, "black");
grd.addColorStop(1, "gray");
// Fill with gradient
ctx.fillStyle = grd;
ctx.fillRect(0, 0, canvas.width, canvas.height);
var ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
ctx.font = "36px Arial";
ctx.fillText("Game Over!", 50, 50);
}
function boundryCheck() {
//up
if (y < radius) {
y = radius;
}
//down
if (y > canvas.height - radius) {
y = canvas.height - radius;
}
//left
if (x < radius) {
x = radius;
}
//right
if (x > canvas.width - radius) {
x = canvas.width - radius;
}
}
function inputs() {
if (upPressed) {
y = y - speed;
}
if (downPressed) {
y = y + speed;
}
if (leftPressed) {
x = x - speed;
}
if (rightPressed) {
x = x + speed;
}
}
function drawGreenBlob() {
ctx.fillStyle = "green";
if (upPressed) {
ctx.fillStyle = "red";
}
if (downPressed) {
ctx.fillStyle = "blue";
}
if (leftPressed) {
ctx.fillStyle = "yellow";
}
if (rightPressed) {
ctx.fillStyle = "purple";
}
ctx.beginPath();
ctx.arc(x, y, radius * multiplier, 0, Math.PI * 2);
ctx.fill();
}
function clearScreen() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
document.body.addEventListener("keydown", keyDown);
document.body.addEventListener("keyup", keyUp);
function keyDown(event) {
//up
if (event.keyCode == 38) {
upPressed = true;
}
//down
if (event.keyCode == 40) {
downPressed = true;
}
//left
if (event.keyCode == 37) {
leftPressed = true;
}
//right
if (event.keyCode == 39) {
rightPressed = true;
}
}
function keyUp(event) {
//up
if (event.keyCode == 38) {
upPressed = false;
}
//down
if (event.keyCode == 40) {
downPressed = false;
}
//left
if (event.keyCode == 37) {
leftPressed = false;
}
//right
if (event.keyCode == 39) {
rightPressed = false;
}
}
drawGame();
<canvas id="gameArea" width=800 height=800></canvas>
I'm making a html5 canvas game and I have a problem with the collision.
The problem is when the ball collides with any platform, the ball gravity should be -1 and go up as the same velocity as the platforms but it only works with the last platform and the left one. How can I fix it? Thanks!
HTML:
<html>
<head>
<title>Falldown</title>
</head>
<body>
<canvas id="canvas" width = "380" height= "640"></canvas>
<script src="beta.js"></script>
</body>
</html>
JS Code:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var isMenu = true;
var isPlaying = false;
var testing = true;
var pressingLeft = false;
var pressingRight = false;
var Platforms = [];
var difficulty = 1;
var gravity = 1;
var Player = {
color: "red",
radius: 7.5,
stepY: 1.5,
x: 175,
y: 75
};
function RectCircleColliding(circle, rect) {
var distX = Math.abs(circle.x - rect.x - rect.width / 2);
var distY = Math.abs(circle.y - rect.y - 20 / 2);
if (distX > (rect.width / 2 + circle.radius)) return false;
if (distY > (20 / 2 + circle.radius)) return false;
if (distX <= (rect.width / 2)) return true;
if (distY <= (20 / 2)) return true;
var dx = distX - rect.width / 2;
var dy = distY - 20 / 2;
return (dx * dx + dy * dy <= (circle.radius * circle.radius));
}
function drawBackground() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (isMenu && !isPlaying) {
createText("60px monospace", "white", "FallDown", 45, 130);
createText("34px Arial", "white", "PLAY", 130, 260);
createText("34px Arial", "white", "LEADERBOARD", 50, 340);
createText("34px Arial", "white", "SETTINGS", 90, 420);
} else {
if (testing) {
Platforms = [];
for (var i = 0; i < 13; i++) {
Platforms.push({
"x": 10,
"y": 160 + (i * 70),
"width": (Math.random() * canvas.width) - 60
});
}
testing = false;
}
for (var i in Platforms) {
ctx.fillStyle = "#00ffff";
ctx.fillRect(10, Platforms[i].y, Platforms[i].width, 20);
var totalTest = Platforms[i].width + 60;
ctx.fillRect(totalTest + 30, Platforms[i].y, canvas.width - totalTest, 20);
Platforms[i].y -= 1;
if (RectCircleColliding(Player, Platforms[i])) {
gravity = -1;
} else {
gravity = 1;
}
}
detectBorderCollision();
detectPlayerCollision();
drawPlayer();
drawBorder();
if (Platforms.length === 7) Platforms = [];
}
}
function detectBorderCollision() {
if (Player.x > 370 - Player.radius) {
Player.x = 370 - Player.radius;
} else if (Player.x < 3.8 + Player.radius * 2) {
Player.x = 3.8 + Player.radius * 2
}
}
function detectPlayerCollision() {
}
function drawPlayer() {
ctx.beginPath();
ctx.fillStyle = Player.color;
ctx.arc(Player.x, Player.y, Player.radius, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
Player.y += gravity;
if (pressingRight) {
Player.x += 2;
} else if (pressingLeft) {
Player.x -= 2;
}
/*
ctx.fillStyle = "#00ffff";
ctx.fillRect(10, 160, 300, 20);
*/
}
function drawBorder() {
ctx.beginPath();
ctx.strokeStyle = "#00ffff";
ctx.lineWidth = 10;
ctx.moveTo(5, 0);
ctx.lineTo(5, 640);
ctx.moveTo(375, 0);
ctx.lineTo(375, 640);
ctx.stroke();
ctx.closePath();
}
function createText(font, color, value, posX, posY) {
ctx.font = font;
ctx.fillStyle = color;
ctx.fillText(value, posX, posY)
}
function isInside(realX, realY, x1, x2, y1, y2) {
return (realX > x1 && realX < x2) && (realY > y1 && realY < y2)
}
function drawGame() {
drawBackground();
}
function startDrawing() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawGame();
requestAnimationFrame(startDrawing);
}
function Init() {
requestAnimationFrame(startDrawing);
canvas.addEventListener("click", function(evt) {
var rect = canvas.getBoundingClientRect();
var mouseX = evt.clientX - rect.left;
var mouseY = evt.clientY - rect.top;
if (isMenu && !isPlaying) {
if (isInside(mouseX, mouseY, 115, 230, 220, 270)) {
isPlaying = true;
isMenu = false;
} else if (isInside(mouseX, mouseY, 35, 320, 300, 345)) {
console.log("Leaderboard");
} else if (isInside(mouseX, mouseY, 75, 270, 380, 430)) {
console.log("Settings");
}
}
});
window.addEventListener("keydown", function(evt) {
if (!isMenu && isPlaying) {
if (evt.keyCode === 39) { // right
pressingRight = true;
} else if (evt.keyCode === 37) { // left
pressingLeft = true;
}
}
});
window.addEventListener("keyup", function(evt) {
if (!isMenu && isPlaying) {
if (evt.keyCode === 39) { // right
pressingRight = false;
} else if (evt.keyCode === 37) { // left
pressingLeft = false;
}
}
});
}
Init();
There are so many magic numbers in your code that debugging it is difficult and tedious. Replace all the number literals with identifiers which describe what the values represent.
The following amendment to part of the drawBackground function causes all the collisions with left hand platforms to work, but not perfectly.
var hasCollided;
for (var i in Platforms) {
ctx.fillStyle = "#00ffff";
ctx.fillRect(10, Platforms[i].y, Platforms[i].width, 20);
var totalTest = Platforms[i].width + 60;
ctx.fillRect(totalTest + 30, Platforms[i].y, canvas.width - totalTest, 20);
Platforms[i].y -= 1;
if (!hasCollided) {
if (RectCircleColliding(Player, Platforms[i])) {
gravity = -1;
hasCollided = true;
} else {
gravity = 1;
}
}
}
I made a simple game. The goal is to stay alive by avoiding the lasers. However, every time the player moves across the laser warning, it erases the laser, making it look weird. I have already tried redrawing the warnings, but every time that happens it doesn't work and makes it buggier.
<!DOCTYPE html>
<html>
<head>
<title>
Drift 2
</title>
</head>
<body>
<center>
<h1>Drift 2</h1>
<h2>by Milesman34</h2>
<canvas id="canvas" width="400" height="400"></canvas>
<strong><p id="score">Score</p></strong>
</center>
<script src="https://code.jquery.com/jquery-2.1.0.js"></script>
<script>
//MARK: Set up the canvas + canvas variables and draws the border + background
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
var blockSize = 20;
var widthInBlocks = width / blockSize;
var heightInBlocks = height / blockSize;
ctx.fillStyle = "rgb(225, 225, 225)";
ctx.fillRect(0, 0, width, height);
//MARK: Defines arrays and variables
var shooterArray = [];
var score = 0;
//MARK: Defines functions
function getRandomFromInterval(interval) {
return Math.floor(Math.random() * interval);
};
//MARK: Defines game over function
var gameEnd = 0;
function gameOver () {
setTimeout (function () {
gameEnd = 1;
clearInterval (scoreEffects);
clearInterval (fireEffects);
ctx.clearRect(blockSize, blockSize, width - (blockSize * 2), height - (blockSize * 2))
ctx.fillStyle = "rgb(225, 225, 225)";
ctx.fillRect(blockSize, blockSize, width - (blockSize * 2), height - (blockSize * 2));
ctx.font = "60px Courier";
ctx.fillStyle = "Black";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("Game Over", width / 2, height / 2);
}, 149);
};
//MARK: Defines the player
function Player (x, y) {
this.x = x;
this.y = y;
};
//MARK: Defines the function that draws the player
Player.prototype.draw = function () {
ctx.fillStyle = "rgb(185, 185, 185)";
ctx.fillRect(this.x, this.y, blockSize, blockSize);
};
var player = new Player(width / 2, height / 2);
player.draw();
//MARK: Defines the functions that move the player
Player.prototype.moveLeft = function () {
ctx.clearRect(this.x, this.y, blockSize, blockSize);
this.x = this.x - 20;
ctx.fillStyle = "rgb(225, 225, 225)";
ctx.fillRect(this.x + 20, this.y, blockSize, blockSize);
};
Player.prototype.moveRight = function () {
ctx.clearRect(this.x, this.y, blockSize, blockSize);
this.x = this.x + 20;
ctx.fillStyle = "rgb(225, 225, 225)";
ctx.fillRect(this.x - 20, this.y, blockSize, blockSize);
};
Player.prototype.moveUp = function () {
ctx.clearRect(this.x, this.y, blockSize, blockSize);
this.y = this.y - 20;
ctx.fillStyle = "rgb(225, 225, 225)";
ctx.fillRect(this.x, this.y + 20, blockSize, blockSize);
};
Player.prototype.moveDown = function () {
ctx.clearRect(this.x, this.y, blockSize, blockSize);
this.y = this.y + 20;
ctx.fillStyle = "rgb(225, 225, 225)";
ctx.fillRect(this.x, this.y - 20, blockSize, blockSize);
};
Player.prototype.checkWallCollision = function () {
if (this.x === 0 || this.x === width - 20 || this.y === 0 || this.y === height - 20) {
gameOver();
};
};
//MARK: Defines the Shooter
function Shooter (x, y, direction) {
this.x = x;
this.y = y;
if (["left", "right", "up", "down"].indexOf(direction) != -1) {
this.direction = direction;
};
shooterArray.push(this);
};
//MARK: Defines the function that draws the Shooter
Shooter.prototype.draw = function () {
ctx.fillStyle = "rgb(185, 185, 185)";
ctx.fillRect(this.x, this.y, blockSize, blockSize);
};
//MARK: Defines the function that fires the Shooter
var timeoutID = null;
function fireLeftRight(y) {
if (gameEnd === 0) {
ctx.fillStyle = "Red";
ctx.fillRect(blockSize, y, width - (blockSize * 2), blockSize);
if (player.y === y) {
gameOver();
};
};
};
function fireLeftRightWarn(y) {
ctx.fillStyle = "Red";
for (i = 1;i < widthInBlocks - 1;i++) {
ctx.fillRect(i * blockSize + (blockSize / 4), y + (blockSize / 4 * 1.5), blockSize / 2, blockSize / 4);
};
timeoutID2 = setTimeout (function () {
clearTimeout (timeoutID);
timeoutID = setTimeout (fireLeftRight(y), 100);
}, 600);
};
function fireUpDown(x) {
if (gameEnd === 0) {
ctx.fillStyle = "Red";
ctx.fillRect(x, blockSize, blockSize, height - (blockSize * 2));
if (player.x === x) {
gameOver();
};
};
};
function fireUpDownWarn(x) {
ctx.fillStyle = "Red";
for (i = 1;i < heightInBlocks - 1;i++) {
ctx.fillRect(x + (blockSize / 4 * 1.5), i * blockSize + (blockSize / 4), blockSize / 4, blockSize / 2);
};
timeoutID2 = setTimeout (function () {
clearTimeout (timeoutID);
timeoutID = setTimeout (fireUpDown(x))
}, 600);
};
Shooter.prototype.fire = function () {
if (this.direction === "left" || this.direction === "right") {
timeoutID = setTimeout (fireLeftRightWarn(this.y), 1);
} else {
timeoutID = setTimeout (fireUpDownWarn(this.x), 1)
};
};
//MARK: Creates the required shooters
for (i = 1;i < heightInBlocks - 1;i++) {
new Shooter(0, i * blockSize, "right");
};
for (i = 1;i < heightInBlocks - 1;i++) {
new Shooter(width - blockSize, i * blockSize, "left");
};
for (i = 1;i < widthInBlocks - 1;i++) {
new Shooter(i * blockSize, 0, "down");
};
for (i = 1;i < widthInBlocks - 1;i++) {
new Shooter(i * blockSize, height - blockSize, "up")
};
for (i = 0;i < shooterArray.length;i++) {
shooterArray[i].draw();
};
ctx.fillStyle = "rgb(185, 185, 185)";
ctx.fillRect(0, 0, blockSize, blockSize);
ctx.fillRect(width - blockSize, 0, blockSize, blockSize);
ctx.fillRect(0, height - blockSize, blockSize, blockSize);
ctx.fillRect(width - blockSize, height - blockSize, blockSize, blockSize);
//MARK: Draws the score
function drawScore () {
$("#score").text("Score: " + Math.floor(score));
};
//MARK: Convert keycodes to directions
var directions = {
37: "left",
38: "up",
39: "right",
40: "down"
};
//MARK: This is the interval loop
var scoreEffects = setInterval (function () {
score += 0.1;
drawScore();
player.draw();
player.checkWallCollision();
}, 100);
$("body").keyup(function (event) {
if (gameEnd != 1) {
var moveDir = directions[event.keyCode];
if (moveDir === "left") {
player.moveLeft();
} else if (moveDir === "right") {
player.moveRight();
} else if (moveDir === "up") {
player.moveUp();
} else if (moveDir === "down") {
player.moveDown();
};
};
});
var fireEffects = setInterval (function () {
ctx.clearRect(blockSize, blockSize, width - (blockSize * 2), height - (blockSize * 2))
ctx.fillStyle = "rgb(225, 225, 225)";
ctx.fillRect(blockSize, blockSize, width - (blockSize * 2), height - (blockSize * 2));
ctx.fillStyle = "rgb(185, 185, 185)";
ctx.fillRect(0, 0, blockSize, blockSize);
ctx.fillRect(width - blockSize, 0, blockSize, blockSize);
ctx.fillRect(0, height - blockSize, blockSize, blockSize);
ctx.fillRect(width - blockSize, height - blockSize, blockSize, blockSize);
for (i = 0;i < shooterArray.length;i++) {
if (getRandomFromInterval(30) === 0) {
shooterArray[i].fire();
};
};
}, 750);
</script>
Working project here: https://jsfiddle.net/ay7kp7yb/1/
I think the best approach to solve this problem is to work with the classic pattern. That way all events that occur in the game need to be chained to a main loop (see: Game Loop).
something like:
while (true)
{
processInputs();
updateObjects();
render();
}
In your code some events happening independently with setTimeout / setInterval, it becomes very complex to "align" events.
I recommend you to use two combined techniques, the first is to centralize all events in a single loop (main game loop) and the second is to create a set of states to control the game.
Game states:
// game states
var STATE_TITLE_SCREEN = 0;
var STATE_GAME_STARTING = 1;
var STATE_RUNNING = 2;
var STATE_PLAYER_DYING = 3;
var STATE_GAME_OVER_SCREEN = 4;
// current state
var gameState = STATE_TITLE_SCREEN;
// time of the current state
var gameStateTime = 0;
// change the state and reset the time of last state
function setGameState(state) {
gameState = state;
gameStateTime = 0;
}
Main loop:
var minIteractionTime = 10;
var mainGameLoop = setInterval (function () {
switch (gameState) {
case STATE_TITLE_SCREEN:
// Title screen (on press space bar, change gameState to STATE_GAME_STARTING)
break;
case STATE_GAME_STARTING:
// Starting game, reset variables, countdown to start, etc...
score = 0.0;
// run the game after 5s
if (gameStateTime>5000)
setGameState(STATE_RUNNING);
break;
case STATE_RUNNING:
score += (0.1/100.0); // 0.1 points after 100 miliseconds
// CLEAR THE SCREEN HERE
drawScore();
player.draw(); // draw the player ONLY here
laserWarning.draw(); // Draws the warnings AFTER draw the player
laserbeam.burn(player); // try to burn the player
break;
case STATE_PLAYER_DYING:
// player dying animation..
// after 5s, change show the game over screen
if (gameStateTime>5000)
setGameState(STATE_GAME_OVER_SCREEN);
break;
case STATE_GAME_OVER_SCREEN:
// draw the game over screen here
// after 5s returns to the title screen
if (gameStateTime>5000)
setGameState(STATE_TITLE_SCREEN);
break;
}
// add the loop time in the gameStateTime variable
gameStateTime += minIteractionTime;
}, minIteractionTime);
LaserWarning class:
LaserWarning.prototype.draw = function () {
this.warningTime += minIteractionTime;
if (this.warningTime>100) {
// draw the warning only after firsts 100ms
}
if (this.warningTime>=750) { // warning time
// timer reset
this.warningTime = 0;
}
}
LaserBeam class:
Laserbeam.prototype.burn = function (player) {
this.warmingUpTime += minIteractionTime;
if (this.warmingUpTime>=750) { // same time of warning
// draw the laser
// check if burns the player
if (player.checkWallCollision()) {
setGameState(STATE_PLAYER_DYING);
}
}
}
Input iteractions:
var gameKeys = {
37: "left",
38: "up",
39: "right",
40: "down",
32: "space"
};
// for all iteractions, check the current state
$("body").keyup(function (event) {
var lastKey = gameKeys[event.keyCode];
switch (gameState) {
case STATE_RUNNING:
if (lastKey === "left") {
player.moveLeft();
} else if (lastKey === "right") {
player.moveRight();
} else if (lastKey === "up") {
player.moveUp();
} else if (lastKey === "down") {
player.moveDown();
};
break;
case STATE_TITLE_SCREEN:
if (lastKey=="space")
setGameState(STATE_GAME_STARTING);
break;
}
});
You should only adjust the positioning variables when moving the player and do not redraw it in the new position, this will happen in player.draw ();, in the STATE_RUNNING, before draw the warning.
If the user press the right arrow two times in less than 10ms (minIteractionTime) the player will be drawn only once! If you need to be more precise just reduce the value of minIteractionTime.
The variable minIteractionTime controls the framerate.
I know it´s not well seen, that you post a link to a code, but I followed this tutorial of making a html drawing canvas app and implemented the source to my wordpress site and everything works fine, except, that all the mouse-click-events seems offset to the left by 100px.
If I open the example html, everything works fine, so I think it has something to do with the parent container css´s or something.
Since I am not very well in js, I thought maybe you can help me figure it out, since I try since a few days now.
This is the tutorial and source code and this is the code of the js file
var drawingApp = (function () {
"use strict";
var canvas,
context,
canvasWidth = 490,
canvasHeight = 220,
colorPurple = "#cb3594",
colorGreen = "#659b41",
colorYellow = "#ffcf33",
colorBrown = "#986928",
outlineImage = new Image(),
crayonImage = new Image(),
markerImage = new Image(),
eraserImage = new Image(),
crayonBackgroundImage = new Image(),
markerBackgroundImage = new Image(),
eraserBackgroundImage = new Image(),
crayonTextureImage = new Image(),
clickX = [],
clickY = [],
clickColor = [],
clickTool = [],
clickSize = [],
clickDrag = [],
paint = false,
curColor = colorPurple,
curTool = "crayon",
curSize = "normal",
mediumStartX = 18,
mediumStartY = 19,
mediumImageWidth = 93,
mediumImageHeight = 46,
drawingAreaX = 111,
drawingAreaY = 11,
drawingAreaWidth = 267,
drawingAreaHeight = 200,
toolHotspotStartY = 23,
toolHotspotHeight = 38,
sizeHotspotStartY = 157,
sizeHotspotHeight = 36,
totalLoadResources = 8,
curLoadResNum = 0,
sizeHotspotWidthObject = {
huge: 39,
large: 25,
normal: 18,
small: 16
},
// Clears the canvas.
clearCanvas = function () {
context.clearRect(0, 0, canvasWidth, canvasHeight);
},
// Redraws the canvas.
redraw = function () {
var locX,
locY,
radius,
i,
selected,
drawCrayon = function (x, y, color, selected) {
context.beginPath();
context.moveTo(x + 41, y + 11);
context.lineTo(x + 41, y + 35);
context.lineTo(x + 29, y + 35);
context.lineTo(x + 29, y + 33);
context.lineTo(x + 11, y + 27);
context.lineTo(x + 11, y + 19);
context.lineTo(x + 29, y + 13);
context.lineTo(x + 29, y + 11);
context.lineTo(x + 41, y + 11);
context.closePath();
context.fillStyle = color;
context.fill();
if (selected) {
context.drawImage(crayonImage, x, y, mediumImageWidth, mediumImageHeight);
} else {
context.drawImage(crayonImage, 0, 0, 59, mediumImageHeight, x, y, 59, mediumImageHeight);
}
},
drawMarker = function (x, y, color, selected) {
context.beginPath();
context.moveTo(x + 10, y + 24);
context.lineTo(x + 10, y + 24);
context.lineTo(x + 22, y + 16);
context.lineTo(x + 22, y + 31);
context.closePath();
context.fillStyle = color;
context.fill();
if (selected) {
context.drawImage(markerImage, x, y, mediumImageWidth, mediumImageHeight);
} else {
context.drawImage(markerImage, 0, 0, 59, mediumImageHeight, x, y, 59, mediumImageHeight);
}
};
// Make sure required resources are loaded before redrawing
if (curLoadResNum < totalLoadResources) {
return;
}
clearCanvas();
if (curTool === "crayon") {
// Draw the crayon tool background
context.drawImage(crayonBackgroundImage, 0, 0, canvasWidth, canvasHeight);
// Draw purple crayon
selected = (curColor === colorPurple);
locX = selected ? 18 : 52;
locY = 19;
drawCrayon(locX, locY, colorPurple, selected);
// Draw green crayon
selected = (curColor === colorGreen);
locX = selected ? 18 : 52;
locY += 46;
drawCrayon(locX, locY, colorGreen, selected);
// Draw yellow crayon
selected = (curColor === colorYellow);
locX = selected ? 18 : 52;
locY += 46;
drawCrayon(locX, locY, colorYellow, selected);
// Draw brown crayon
selected = (curColor === colorBrown);
locX = selected ? 18 : 52;
locY += 46;
drawCrayon(locX, locY, colorBrown, selected);
} else if (curTool === "marker") {
// Draw the marker tool background
context.drawImage(markerBackgroundImage, 0, 0, canvasWidth, canvasHeight);
// Draw purple marker
selected = (curColor === colorPurple);
locX = selected ? 18 : 52;
locY = 19;
drawMarker(locX, locY, colorPurple, selected);
// Draw green marker
selected = (curColor === colorGreen);
locX = selected ? 18 : 52;
locY += 46;
drawMarker(locX, locY, colorGreen, selected);
// Draw yellow marker
selected = (curColor === colorYellow);
locX = selected ? 18 : 52;
locY += 46;
drawMarker(locX, locY, colorYellow, selected);
// Draw brown marker
selected = (curColor === colorBrown);
locX = selected ? 18 : 52;
locY += 46;
drawMarker(locX, locY, colorBrown, selected);
} else if (curTool === "eraser") {
context.drawImage(eraserBackgroundImage, 0, 0, canvasWidth, canvasHeight);
context.drawImage(eraserImage, 18, 19, mediumImageWidth, mediumImageHeight);
}
// Draw line on ruler to indicate size
switch (curSize) {
case "small":
locX = 467;
break;
case "normal":
locX = 450;
break;
case "large":
locX = 428;
break;
case "huge":
locX = 399;
break;
default:
break;
}
locY = 189;
context.beginPath();
context.rect(locX, locY, 2, 12);
context.closePath();
context.fillStyle = '#333333';
context.fill();
// Keep the drawing in the drawing area
context.save();
context.beginPath();
context.rect(drawingAreaX, drawingAreaY, drawingAreaWidth, drawingAreaHeight);
context.clip();
// For each point drawn
for (i = 0; i < clickX.length; i += 1) {
// Set the drawing radius
switch (clickSize[i]) {
case "small":
radius = 2;
break;
case "normal":
radius = 5;
break;
case "large":
radius = 10;
break;
case "huge":
radius = 20;
break;
default:
break;
}
// Set the drawing path
context.beginPath();
// If dragging then draw a line between the two points
if (clickDrag[i] && i) {
context.moveTo(clickX[i - 1], clickY[i - 1]);
} else {
// The x position is moved over one pixel so a circle even if not dragging
context.moveTo(clickX[i] - 1, clickY[i]);
}
context.lineTo(clickX[i], clickY[i]);
// Set the drawing color
if (clickTool[i] === "eraser") {
//context.globalCompositeOperation = "destination-out"; // To erase instead of draw over with white
context.strokeStyle = 'white';
} else {
//context.globalCompositeOperation = "source-over"; // To erase instead of draw over with white
context.strokeStyle = clickColor[i];
}
context.lineCap = "round";
context.lineJoin = "round";
context.lineWidth = radius;
context.stroke();
}
context.closePath();
//context.globalCompositeOperation = "source-over";// To erase instead of draw over with white
context.restore();
// Overlay a crayon texture (if the current tool is crayon)
if (curTool === "crayon") {
context.globalAlpha = 0.4; // No IE support
context.drawImage(crayonTextureImage, 0, 0, canvasWidth, canvasHeight);
}
context.globalAlpha = 1; // No IE support
// Draw the outline image
context.drawImage(outlineImage, drawingAreaX, drawingAreaY, drawingAreaWidth, drawingAreaHeight);
},
// Adds a point to the drawing array.
// #param x
// #param y
// #param dragging
addClick = function (x, y, dragging) {
clickX.push(x);
clickY.push(y);
clickTool.push(curTool);
clickColor.push(curColor);
clickSize.push(curSize);
clickDrag.push(dragging);
},
// Add mouse and touch event listeners to the canvas
createUserEvents = function () {
var press = function (e) {
// Mouse down location
var sizeHotspotStartX,
mouseX = e.pageX - this.offsetLeft,
mouseY = e.pageY - this.offsetTop;
if (mouseX < drawingAreaX) { // Left of the drawing area
if (mouseX > mediumStartX) {
if (mouseY > mediumStartY && mouseY < mediumStartY + mediumImageHeight) {
curColor = colorPurple;
} else if (mouseY > mediumStartY + mediumImageHeight && mouseY < mediumStartY + mediumImageHeight * 2) {
curColor = colorGreen;
} else if (mouseY > mediumStartY + mediumImageHeight * 2 && mouseY < mediumStartY + mediumImageHeight * 3) {
curColor = colorYellow;
} else if (mouseY > mediumStartY + mediumImageHeight * 3 && mouseY < mediumStartY + mediumImageHeight * 4) {
curColor = colorBrown;
}
}
} else if (mouseX > drawingAreaX + drawingAreaWidth) { // Right of the drawing area
if (mouseY > toolHotspotStartY) {
if (mouseY > sizeHotspotStartY) {
sizeHotspotStartX = drawingAreaX + drawingAreaWidth;
if (mouseY < sizeHotspotStartY + sizeHotspotHeight && mouseX > sizeHotspotStartX) {
if (mouseX < sizeHotspotStartX + sizeHotspotWidthObject.huge) {
curSize = "huge";
} else if (mouseX < sizeHotspotStartX + sizeHotspotWidthObject.large + sizeHotspotWidthObject.huge) {
curSize = "large";
} else if (mouseX < sizeHotspotStartX + sizeHotspotWidthObject.normal + sizeHotspotWidthObject.large + sizeHotspotWidthObject.huge) {
curSize = "normal";
} else if (mouseX < sizeHotspotStartX + sizeHotspotWidthObject.small + sizeHotspotWidthObject.normal + sizeHotspotWidthObject.large + sizeHotspotWidthObject.huge) {
curSize = "small";
}
}
} else {
if (mouseY < toolHotspotStartY + toolHotspotHeight) {
curTool = "crayon";
} else if (mouseY < toolHotspotStartY + toolHotspotHeight * 2) {
curTool = "marker";
} else if (mouseY < toolHotspotStartY + toolHotspotHeight * 3) {
curTool = "eraser";
}
}
}
}
paint = true;
addClick(mouseX, mouseY, false);
redraw();
},
drag = function (e) {
if (paint) {
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
redraw();
}
// Prevent the whole page from dragging if on mobile
e.preventDefault();
},
release = function () {
paint = false;
redraw();
},
cancel = function () {
paint = false;
};
// Add mouse event listeners to canvas element
canvas.addEventListener("mousedown", press, false);
canvas.addEventListener("mousemove", drag, false);
canvas.addEventListener("mouseup", release);
canvas.addEventListener("mouseout", cancel, false);
// Add touch event listeners to canvas element
canvas.addEventListener("touchstart", press, false);
canvas.addEventListener("touchmove", drag, false);
canvas.addEventListener("touchend", release, false);
canvas.addEventListener("touchcancel", cancel, false);
},
// Calls the redraw function after all neccessary resources are loaded.
resourceLoaded = function () {
curLoadResNum += 1;
if (curLoadResNum === totalLoadResources) {
redraw();
createUserEvents();
}
},
// Creates a canvas element, loads images, adds events, and draws the canvas for the first time.
init = function () {
// Create the canvas (Neccessary for IE because it doesn't know what a canvas element is)
canvas = document.createElement('canvas');
canvas.setAttribute('width', canvasWidth);
canvas.setAttribute('height', canvasHeight);
canvas.setAttribute('id', 'canvas');
document.getElementById('canvasDiv').appendChild(canvas);
if (typeof G_vmlCanvasManager !== "undefined") {
canvas = G_vmlCanvasManager.initElement(canvas);
}
context = canvas.getContext("2d"); // Grab the 2d canvas context
// Note: The above code is a workaround for IE 8 and lower. Otherwise we could have used:
// context = document.getElementById('canvas').getContext("2d");
// Load images
crayonImage.onload = resourceLoaded;
crayonImage.src = cvtemplateDir+ "/images/crayon-outline.png";
markerImage.onload = resourceLoaded;
markerImage.src = cvtemplateDir+ "/images/marker-outline.png";
eraserImage.onload = resourceLoaded;
eraserImage.src = cvtemplateDir+ "/images/eraser-outline.png";
crayonBackgroundImage.onload = resourceLoaded;
crayonBackgroundImage.src = cvtemplateDir+ "/images/crayon-background.png";
markerBackgroundImage.onload = resourceLoaded;
markerBackgroundImage.src = cvtemplateDir+ "/images/marker-background.png";
eraserBackgroundImage.onload = resourceLoaded;
eraserBackgroundImage.src = cvtemplateDir+ "/images/eraser-background.png";
crayonTextureImage.onload = resourceLoaded;
crayonTextureImage.src = cvtemplateDir+ "/images/crayon-texture.png";
outlineImage.onload = resourceLoaded;
outlineImage.src = cvtemplateDir+ "/images/watermelon-duck-outline.png";
};
return {
init: init
};
}());
It´s being implemented on my website here
This is the code to initialize it, which is being called on the hook:
add_action( 'comment_form_before', 'canvas_comments');
function canvas_comments() {
?>
<div id="canvasDiv" style="width:490px;height:220px;"></div>
<script type="text/javascript">
drawingApp.init();
</script>
<?php
}
EDIT:
Just realized in Firefox it´s offsetting even more, I used chrome
Did you try to get offsetTop and offsetLeft explicitly from the canvas?
You are now accessing offsetTop by this in a listener-function. Are you sure that this points to your canvas and not to something else?
I finally fixed with treeno´s suggestion to use canvas.getBoundingClientRect();
I inserted it at the end of the createUserEvents-function like this:
cancel = function () {
......
var rect = canvas.getBoundingClientRect();
// Add mouse event listeners to canvas element
canvas.addEventListener("mousedown", press, false);
.....
},
And finally i replaced every this.OffsetLeft and this.OffsetTop with rect.left and rect.top
Works like a charm!
Here's my code:
var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d")
var w = canvas.width
var h = canvas.height
var player = {
x: w / 2,
y: h / 2,
r: 10,
vx: 0,
vy: 0,
speed: 5,
}
var keys = []
window.onkeydown = function(event) {keys[event.keyCode] = true}
window.onkeyup = function(event) {keys[event.keyCode] = false}
setInterval(update, 1000 / 60)
function update() {
if (keys[38] && player.vy > -player.speed) {
player.vy--
}
if (keys[40] && player.vy < player.speed) {
player.vy++
}
if (keys[37] && player.vx > -player.speed) {
player.vx--
}
if (keys[39] && player.vx < player.speed) {
player.vx++
}
player.vx *= 0.9
player.vy *= 0.9
player.x += player.vx
player.y += player.vy
ctx.clearRect(0,0,w,h)
ctx.beginPath()
ctx.arc(player.x, player.y, player.r, 0, Math.PI * 2, false)
ctx.fillStyle = "red"
ctx.fill()
}
http://jsfiddle.net/wfNse/
So, my question is, how can I make the player come out on the other side when you go out side of the bounds? Now this is pretty easily done, so there's one more thing, when half the player is out of the bounds, I want it to be seen half on both sides, like this:
You can do this :
// coordinates replaced in the board
player.x = (player.x+w)%w;
player.y = (player.y+h)%h;
// player position drawing
ctx.clearRect(0,0,w,h)
ctx.beginPath()
for (var i=-1; i<=1; i++) {
for (var j=-1; j<=1; j++) {
var x = player.x+i*w, y = player.y+j*h;
if (x+player.r<0 || x-player.r>w || y+player.r<0 || y-player.r>h) continue;
ctx.arc(x, y, player.r, 0, Math.PI * 2, false)
}
}
Demonstration