Why isnt the .clearRect function working properly? - javascript

So im making this space invaders game in javascript. My problem right now is that when the enemies move left and right they dont clear rect so you can see thier footage as shown here:
I just wanted to know what i could fix to solve this problem. I would love if you could help here is my code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Space Invaders</title>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<style>
canvas{
position: absolute;
top:0px;
left:0px;
background: transparent;
}
#backgroundCanvas{
background-color: black;
}
</style>
</head>
<body>
<canvas id="backgroundCanvas" width="550" height="600"></canvas>
<canvas id="playerCanvas" width="550" height="600"></canvas>
<canvas id="enemiesCanvas" width="550" height="600"></canvas>
<script>
(function(){
$(document).ready(function(){
var game = {};
game.stars = [];
game.width = 550;
game.height = 600;
game.images = [];
game.doneImages = 0;
game.requiredImages = 0;
game.keys = [];
game.enemies = [];
game.count = 0;
game.division = 48;
game.left = false;
game.enemySpeed = 3;
game.contextBackground = document.getElementById("backgroundCanvas").getContext('2d');
game.contextPlayer = document.getElementById("playerCanvas").getContext('2d');
game.contextEnemies = document.getElementById("enemiesCanvas").getContext('2d');
game.player = {
x: game.width / 2 -50,
y: game.height - 110,
width:100,
height:100,
speed: 3,
rendered: false
}
$(document).keydown(function(e){
game.keys[e.keyCode ? e.keyCode : e.which] = true;
})
$(document).keyup(function(e){
delete game.keys[e.keyCode ? e.keyCode : e.which];
})
/*
up -38
down-40
left -37
right-39
w-87
a-65
s-83
d-68
space-32
*/
function init(){
for(i=0; i<600;i++){
game.stars.push({
x:Math.floor(Math.random()* game.width),
y:Math.floor(Math.random()* game.height),
size: Math.random()*5
})
}
for(y=0;y<5;y++){
for(x =0;x<5;x++){
game.enemies.push({
x: (x*70) + (70*x) + 10,
y: (y*70) + (10*y) + 40,
width:70,
height: 70,
image:1
})
}
}
loop();
}
function addStars(num){
for(i=0; i<num;i++){
game.stars.push({
x:Math.floor(Math.random()* game.width),
y:game.height+10,
size: Math.random()*5
})
}
}
function update(){
addStars(1);
game.count++;
for(i in game.stars){
if(game.stars[i].y <= -5){
game.stars.splice(i,1);
}
game.stars[i].y--;
}
if(game.keys[37] || game.keys[65]){
if(game.player.x>=0){
game.player.x-=game.player.speed;
game.player.rendered = false;
}
}
if(game.keys[39] || game.keys[68]){
if(game.player.x<=500-50){
game.player.x+=game.player.speed;
game.player.rendered = false;
}
}
if(game.count % game.division == 0){
game.left = !game.left;
}
for(i in game.enemies){
if(game.left){
game.enemies[i].x-=game.enemySpeed;
}else{
game.enemies[i].x+=game.enemySpeed;
}
}
}
function render(){
game.contextBackground.clearRect(0,0,game.width,game.height)
game.contextBackground.fillStyle = "white";
for(i in game.stars){
var star = game.stars[i];
game.contextBackground.fillRect(star.x,star.y,star.size,star.size);
}
if(!game.player.rendered){
game.contextPlayer.clearRect(0, 0, game.width, game.height);
game.contextPlayer.drawImage(game.images[0], game.player.x, game.player.y, game.player.width, game.player.height);
game.player.rendered = true;
}
for(i in game.enemies){
var enemy = game.enemies[i];
game.contextEnemies.clearRect(enemy.x, enemy.y, enemy.width, enemy.height);
game.contextEnemies.drawImage(game.images[enemy.image], enemy.x, enemy.y, enemy.width, enemy.height);
}
}
function loop(){
requestAnimFrame(function(){
loop();
});
update();
render();
}
function initImages(paths){
game.requiredImages = paths.length;
for(i in paths){
var img = new Image;
img.src = paths[i];
game.images[i] = img;
game.images[i].onload = function(){
game.doneImages++;
}
}
}
function checkImages(){
if(game.doneImages>=game.requiredImages){
init();
}
else{
setTimeout(function(){
checkImages();
},1)
}
}
game.contextBackground.font = "bold 50px monaco"
game.contextBackground.fillStyle = "white";
game.contextBackground.fillText("loading" , game.width/2-100 ,game.height/2-25)
initImages(["player.gif", "enemy.png", "bullet.png","Image1.png","Image2.png"])
checkImages();
});
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
})();
</script>
</body>
</html>

Right now, you clear contextEnemies with this
game.contextEnemies.clearRect(enemy.x, enemy.y, enemy.width, enemy.height);
The problem here is that you are clearing the square where you are about to draw the enemy, not the square where it was drawn last time. You need to keep track of where you drew the enemy at previous render (enemy.x), and clear that part.
Either you add something like enemy.lastx to the enemy object, to keep track of it's previous position. Or the easiest solution would be to just clear the entire contextEnemies as you are doing with contextBackground.
game.contextBackground.clearRect(0, 0, game.width, game.height);
game.contextEnemies.clearRect(0, 0, game.width, game.height);
Hope that helps. Cheers!

Related

How do i Make a sprite change with keyboard input i'm using vanilla JS

so ive been testing out HTML canvas. im trying to get a sprite to change on keyboard input.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id='Game' width='200' height='200' style='border: 2px solid #000000;'></canvas>
<script>
window.onload = function(){
var Game = document.getElementById('Game');
var context = Game.getContext('2d')
var room = new Image();
var lx = 0;
var ly = 0;
var li = 0;
var lo = 0;
var lwidth = 100;
var lheight = 100;
room.onload = function(){
context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200);
}
room.src = 'https://i.ibb.co/D7fL7yN/Room.png';
var sprite = new Image();
var cx = 0;
var cy = 125;
var sy = 0;
var sx = 0;
var swidth = 35;
var sheight = 34;
sprite.onload = function(){
context.drawImage(sprite,sx,sy,swidth,sheight,cx,cy,50,50);
}
sprite.src = 'https://i.ibb.co/7VhjqPr/John-Sheet.png';
}
</script>
</body>
</html>
ive been searching on how to change the SX with Keyboard input so my character changes sprites. can you help me? a code example would be best!
Tracking keyboard state.
You can create an object that hold the state of the keyboard, specifically the keys you are interested in reacting to. Use the "keydown" and "keyup" KeyboardEvent to update the keyboard state as the events fire. Use the KeyboardEvent property code to workout which key is changing. DO NOT use keyCode as that has depreciated and is Non Standard
You also want to prevent the default behaviour of keys. Eg prevent arrow keys scrolling the page. This is done by calling the event preventDefault function
const keys = {
ArrowRight: false,
ArrowLeft: false,
ArrowUp: false,
ArrowDown: false,
}
addEventListener("keydown", keyEvent);
addEventListener("keyup", keyEvent);
function keyEvent(event) {
if (keys[event.code] !== undefined) {
keys[event.code] = event.type === "keydown";
event.preventDefault();
}
}
Then in the game you need only check the keyboard state
if (keys.ArrowRight) { moveRight() }
if (keys.ArrowLeft) { moveLeft() }
// and so on
In the demo below the keys are binded to game actions, meaning that what and how many keys are used are independent of the action. The are also set via configuration, so that key binding can be changed without changing game code. You can also bind other inputs as in example
Animation
To animate you should use the timer function requestAnimationFrame as it is specifically designed to give the best animation results. It will call your rendering function, you can consider the rendering function like a loop, that is call every time you step forward in animation time.
Putting it together
The demo below use the above (modified) methods to get keyboard input and render the scene via animation frame requests.
It also uses some techniques (simple versions of) that help make your game a better product.
Encapsulates the player as an object
Maintains game state by holding the current rendering function in currentRenderState
Has configuration config so all important values are in one place, and could be loaded (from JSON file) to easily change the game without changing code.
Has configurable keyboard binding, Note more than one key can be bound to a game action. In example movement is via WASD or arrow keys.
All text is configurable (making it language independent)
Passes the 2D context to all rendering code.
Separates the game from the rendering. This makes it easier to port the game to low end or high end devices or even move it to a server where ctx is replaced with coms and the game can be broadcast . The game does not change only how it is rendered
var currentRenderState = getFocus; // current game state
const config = {
player: {
start: {x: 100, y:100},
speed: 2,
imageURL: "https://i.stack.imgur.com/C7qq2.png?s=64&g=1",
},
keys: { // link key code to game action
up: ["ArrowUp", "KeyW"],
down: ["ArrowDown", "KeyS"],
left: ["ArrowLeft", "KeyA"],
right: ["ArrowRight", "KeyD"],
},
touchableTime: 140, // in ms. Set to 0 or remove to deactivate
text: {
focus: "Click canvas to get focus",
loading: "Just a moment still loading media!",
instruct: "Use arrow keys or WASD to move",
}
};
requestAnimationFrame(mainLoop); // request first frame
const ctx = gameCanvas.getContext("2d");
const w = gameCanvas.width, h = gameCanvas.height;
const player = {
image: (()=> {
const img = new Image;
img.src = config.player.imageURL;
img.addEventListener("load", () => player.size = img.width, {once: true});
return img;
})(),
x: config.player.start.x,
y: config.player.start.y,
size: 0,
speed: config.player.speed,
direction: 0,
update() {
var oldX = this.x, oldY = this.y;
if (actions.left) { this.x -= this.speed }
if (actions.right) { this.x += this.speed }
if (actions.up) { this.y -= this.speed }
if (actions.down) { this.y += this.speed }
if (this.x < 0) { this.x = 0 }
else if (this.x > w - this.size) { this.x = w - this.size }
if (this.y < 0) { this.y = 0 }
else if (this.y > h - this.size) { this.y = h - this.size }
const mx = this.x - oldX, my = this.y - oldY;
if (mx !== 0 || my !== 0) { this.direction = Math.atan2(my, mx) }
},
draw(ctx) {
if (ctx) {
ctx.setTransform(1, 0, 0, 1, this.x + this.size / 2, this.y + this.size / 2);
ctx.rotate(this.direction + Math.PI / 2); // rotate 90 deg as image points up
ctx.drawImage(this.image,-this.size / 2, -this.size / 2, this.size, this.size);
}
}
}
function drawText(ctx, text, size, color) {
if (ctx) {
ctx.fillStyle = color;
ctx.font = size + "px Arial";
ctx.textAlign = "center";
ctx.fillText(text, w / 2, h * (1/4));
}
}
function getFocus(ctx) {
drawText(ctx, config.text.focus, 24, "black");
}
function drawScene(ctx) {
if (!player.size === 0) {
drawText(ctx, config.text.loading, 16, "blue")
actions.hasInput = false; // ensure instruction are up when ready
} else {
if (!actions.hasInput) { drawText(ctx, config.text.instruct, 16, "blue") }
player.update();
player.draw(ctx);
}
}
function mainLoop() {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, w, h);
currentRenderState(ctx);
requestAnimationFrame(mainLoop); // request next frame
}
// keys holds action name for each named key. eg for up action ArrowUp: "up", KeyW: "up",
const keys = Object.entries(config.keys)
.reduce((keys, [action,codes]) =>
codes.reduce((keys, code) => (keys[code] = action, keys), keys), {});
// actions are set true when key down. NOTE first up key for action cancels action
const actions = Object.keys(config.keys)
.reduce((actions,action) => (actions[action] = false, actions),{});
addEventListener("keydown", keyEvent);
addEventListener("keyup", keyEvent);
function keyEvent(event) {
if (keys[event.code] !== undefined) {
actions[keys[event.code]] = event.type === "keydown";
event.preventDefault();
actions.hasInput = true;
}
}
if (config.touchableTime) {
const actionTimers = {};
touchable.addEventListener("click", (e) => {
if (e.target.dataset.action) {
actions[e.target.dataset.action] = true;
clearTimeout(actionTimers[e.target.dataset.action]);
actionTimers[e.target.dataset.action] = setTimeout(() => actions[e.target.dataset.action] = false, config.touchableTime);
actions.hasInput=true;
if (currentRenderState !== drawScene) {
window.focus();
currentRenderState = drawScene;
}
}
});
} else {
touchable.classList.add("hide");
}
gameCanvas.addEventListener("click", () => currentRenderState = drawScene, {once: true});
canvas {border: 1px solid black}
#game {
width:402px;
height:182px;
font-size: 24px;
user-select: none;
}
.left {
position: absolute;
top: 160px;
left: 10px;
cursor: pointer;
}
.right {
position: absolute;
top: 160px;
left: 355px;
cursor: pointer;
}
#touchable span:hover {color: red}
.hide { display: none }
<div id="game">
<canvas id="gameCanvas" width="400" height="180"></canvas>
<div id="touchable">
<div class="left">
<span data-action="up">▲</span>
<span data-action="down">▼</span>
</div>
<div class="right">
<span data-action="left">◄</span>
<span data-action="right">►</span>
</div>
</div>
</div>
Click to snippet frame area for focusing keyboard events
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id='Game' width='200' height='200' style='border: 2px solid #000000;'></canvas>
<script>
window.onload = function(){
// Keyboard collect
const keys = [];
document.onkeydown = e => {
var code = e.which;
if(keys.indexOf(code) < 0){
keys.push(code);
}
};
document.onkeyup = e => keys.splice(keys.indexOf(e.which),1);
// constants
const Game = document.getElementById('Game');
const context = Game.getContext('2d')
const room = new Image();
const lx = 0;
const ly = 0;
const li = 0;
const lo = 0;
const lwidth = 100;
const lheight = 100;
room.onload = function(){
context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200);
}
room.src = 'https://i.ibb.co/D7fL7yN/Room.png';
const sprite = new Image();
const swidth = 35;
const sheight = 34;
const sy = 0;
sprite.onload = function(){
context.drawImage(sprite,0,sy,swidth,sheight,0,cy,50,50);
}
sprite.src = 'https://i.ibb.co/7VhjqPr/John-Sheet.png';
// variables
let cx = 0;
let cy = 125;
let sx = 0;
// new variables
const frames_per_step = 20;
let moving = false; // moving flag
let step = 0; // frame counter (for steps)
// main loop function
function tick() {
// keyboard process
if (keys.length) {
keys.forEach(item => {
switch(item){
case 68:case 39://D and right arrow
cx += 1; // move right
// change sprite
if (step++ < frames_per_step / 2) {
sx = 35; // leg up
} else {
sx = 70; // leg down
if(step > frames_per_step) step = 0;
}
moving = true;
break;
case 65:case 37://A and left arrow
cx -= 1; // move left
// change sprite
if (step++ < frames_per_step / 2) {
sx = 105;
} else {
sx = 140;
if(step > frames_per_step) step = 0;
}
moving = true;
break;
// no sprite mechanics here, just move
case 87:case 38://W adn arrow up
cy -= 1;
break;
case 83:case 40://S adn arrow down
cy += 1;
break;
}
});
// render
context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200);
context.drawImage(sprite,sx,sy,swidth,sheight,cx,cy,50,50);
} else if(moving) { // return sprite to stay position
sx = 0;
context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200);
context.drawImage(sprite,sx,sy,swidth,sheight,cx,cy,50,50);
moving = false;
} // else do nothing
requestAnimationFrame(tick);
}
tick();
}
</script>
</body>
</html>

Ball-Box Collision Detection

var canvas,cxt,h,w,mousePos;
var player= {
x: 10,
y: 10,
height: 20,
width: 20,
color: 'black'
};
function init(){
canvas= document.querySelector('#style');
cxt= canvas.getContext('2d');
h= canvas.height;
w= canvas.width;
createBalls(10);
main();
}
function createBalls(c){
ball= [];
var i;
for(i=0;i<c;i++){
var k= {
x: h/2,
y: w/2,
color: colorGenerate(),
radius: 5+Math.round(30*Math.random()),
a: -5+Math.round(10*Math.random()),
b: -5+Math.round(10*Math.random())
}
ball.push(k);
}
}
function main(){
cxt.clearRect(0,0,h,w);
canvas.addEventListener('mousemove',function(evt){
mousePos= getMousePos(canvas,evt);
});
createPlayer();
draw(ball.length);
ballAlive();
move(ball.length);
movePlayer();
requestAnimationFrame(main);
}
function ballAlive(){
cxt.save();
cxt.font="30px Arial";
if(ball.length==0) cxt.fillText("You Win",20,20);
else cxt.fillText(ball.length,20,40);
cxt.restore();
}
function getMousePos(canvas,evt){
var rect= canvas.getBoundingClientRect();
return{
x: evt.clientX-rect.left,
y: evt.clientY-rect.top
}
}
function createPlayer(){
cxt.save();
cxt.translate(0,0);
cxt.fillStyle= player.color;
cxt.fillRect(player.x,player.y,player.height,player.width);
cxt.restore();
}
function movePlayer(){
if(mousePos !== undefined){
player.x= mousePos.x;
player.y= mousePos.y;
}
}
function draw(d){
var i;
for(i=0;i<d;i++){
cxt.save();
cxt.translate(0,0);
cxt.beginPath();
cxt.fillStyle= ball[i].color;
cxt.arc(ball[i].x,ball[i].y,ball[i].radius,0,2*Math.PI)
cxt.fill();
cxt.restore();
}
}
function move(m){
var i;
for(i=0;i<m;i++){
ball[i].x+= ball[i].a;
ball[i].y+= ball[i].b;
checkCollision(ball[i]);
checkCollisionPlayer(ball[i],i);
}
}
function checkCollision(n){
if(n.x+n.radius>w){
n.a= -n.a;
n.x= w-n.radius;
}
else if(n.x-n.radius<0){
n.a= -n.a;
n.x= n.radius;
}
if(n.y+n.radius>h){
n.b= -n.b;
n.y= h-n.radius;
}
else if(n.y-n.radius<0){
n.b= -n.b;
n.y= n.radius;
}
}
function checkCollisionPlayer(n,j){
if(overlap(n.x,n.y,n.radius,player.x,player.y,player.height,player.width)){
ball.splice(j,1);
}
}
function overlap(cx,cy,r,px,py,ph,pw){
var testX= cx;
var testY= cy;
// THESE LINES ARE FOR MOVING THE BALLS TOWARDS THE PLAYER
if(testX<px) testX=px;
if(testX>(px+pw)) testX=px+pw;
if(testY<py) testy=py;
if(testY>(py+ph)) testY=py+ph;
//DISTANCE FORMULA FOR CHECKING THE OVERLAPING BETWEEN THE BOX AND CIRCLE
return((cx-px)*(cx-px)+(cy-py)*(cy-py)<r*r);
}
function colorGenerate(){
var col= ['green','blue','pink','red','brown','yellow','black','orange','grey','golden'];
var i= Math.round((col.length-1)*Math.random()); //RETURN VALUES FROM 0 TO 9
return col[i];
}
#style{
border: 4px dotted green;
}
<!DOCTYPE html>
<html lang= 'en-us'>
<head>
<title>Feed The Monster</title>
</head>
<body onload= 'init();'>
<canvas id= 'style' height= '400' width= '400'>
Your browser does not support canvas...
</canvas>
</body>
</html>
I am getting error in my code as soon as the first collision takes place between the player(box) and the balls.
The only part which is giving error is the splice() function. If I comment the splice() function then code is not showing any error.
But if I use splice() function part in my code then it is showing error in the move function and I don't know why this is happening.
Please help me…
var canvas,cxt,h,w,mousePos;
var player= {
x: 10,
y: 10,
height: 20,
width: 20,
color: 'black'
};
function init(){
canvas= document.querySelector('#style');
cxt= canvas.getContext('2d');
h= canvas.height;
w= canvas.width;
createBalls(10);
main();
}
function createBalls(c){
ball= [];
var i;
for(i=0;i<c;i++){
var k= {
x: h/2,
y: w/2,
color: colorGenerate(),
radius: 5+Math.round(30*Math.random()),
a: -5+Math.round(10*Math.random()),
b: -5+Math.round(10*Math.random())
}
ball.push(k);
}
}
function main(){
cxt.clearRect(0,0,h,w);
canvas.addEventListener('mousemove',function(evt){
mousePos= getMousePos(canvas,evt);
});
createPlayer();
draw(ball.length);
ballAlive();
move(ball);
movePlayer();
requestAnimationFrame(main);
}
function ballAlive(){
cxt.save();
cxt.font="30px Arial";
if(ball.length==0) cxt.fillText("You Win",20,20);
else cxt.fillText(ball.length,20,40);
cxt.restore();
}
function getMousePos(canvas,evt){
var rect= canvas.getBoundingClientRect();
return{
x: evt.clientX-rect.left,
y: evt.clientY-rect.top
}
}
function createPlayer(){
cxt.save();
cxt.translate(0,0);
cxt.fillStyle= player.color;
cxt.fillRect(player.x,player.y,player.height,player.width);
cxt.restore();
}
function movePlayer(){
if(mousePos !== undefined){
player.x= mousePos.x;
player.y= mousePos.y;
}
}
function draw(d){
var i;
for(i=0;i<d;i++){
cxt.save();
cxt.translate(0,0);
cxt.beginPath();
cxt.fillStyle= ball[i].color;
cxt.arc(ball[i].x,ball[i].y,ball[i].radius,0,2*Math.PI)
cxt.fill();
cxt.restore();
}
}
function move(m){
var i;
for(i=0;i<m.length;i++){
ball[i].x+= ball[i].a;
ball[i].y+= ball[i].b;
checkCollision(ball[i]);
checkCollisionPlayer(ball[i],i);
}
}
function checkCollision(n){
if(n.x+n.radius>w){
n.a= -n.a;
n.x= w-n.radius;
}
else if(n.x-n.radius<0){
n.a= -n.a;
n.x= n.radius;
}
if(n.y+n.radius>h){
n.b= -n.b;
n.y= h-n.radius;
}
else if(n.y-n.radius<0){
n.b= -n.b;
n.y= n.radius;
}
}
function checkCollisionPlayer(n,j){
if(overlap(n.x,n.y,n.radius,player.x,player.y,player.height,player.width)){
ball.splice(j,1);
}
}
function overlap(cx,cy,r,px,py,ph,pw){
var testX= cx;
var testY= cy;
// THESE LINES ARE FOR MOVING THE BALLS TOWARDS THE PLAYER
if(testX<px) testX=px;
if(testX>(px+pw)) testX=px+pw;
if(testY<py) testy=py;
if(testY>(py+ph)) testY=py+ph;
//DISTANCE FORMULA FOR CHECKING THE OVERLAPING BETWEEN THE BOX AND CIRCLE
return((cx-px)*(cx-px)+(cy-py)*(cy-py)<r*r);
}
function colorGenerate(){
var col= ['green','blue','pink','red','brown','yellow','black','orange','grey','golden'];
var i= Math.round((col.length-1)*Math.random()); //RETURN VALUES FROM 0 TO 9
return col[i];
}
#style{
border: 4px dotted green;
}
<!DOCTYPE html>
<html lang= 'en-us'>
<head>
<title>Feed The Monster</title>
</head>
<body onload= 'init();'>
<canvas id= 'style' height= '400' width= '400'>
Your browser does not support canvas...
</canvas>
</body>
</html>
If you pass the array as an argument in the move() function not the length of the array then your code will work perfectly.
This appears to be to do with the loop inside your move function. You call it with the parameter m representing the lengh of the ball array - but the function checkCollisionPlayer which is called within that can remove a ball from the array if there is a collision. This means that the array is now shorter, so whenever you try to access a property on ball[m-1] later in the loop, as you will, you'll get this error.
I'm sure there are lots of different ways to fix this. The easiest I can think of (which I have successfully used myself in making a browser game) is to, instead of directly deleting the balls from the array upon collision, set a property on them to mark them as "deleted". Then add a "cleanup" function onto the end of the loop to filter the array into only those that are not marked as deleted. This ensures that every object you are looping over actually exists, while still changing the length dynamically to reflect the game state.

The old elements of canvas reappears after clearing the canvas using clearRect(...)

Whenever I try to clear the canvas using clearRect(), at first it is cleared. But as soon as I start redrawing the old elements reappear. I even tried
context.width = context.width
but nothing seems to be working. The canvas is getting cleared initially but on clicking the clear button, it clears at first, but everything reappears. Please help me in debugging this error. The clearRect method is in the end of the code.
Below is the code
<script>
var canv = document.getElementById('canv'),
ctx = canv.getContext('2d'),
rect = [],
move = false;
var newRect;
var startX, startY, mouseX, mouseY;
var offsetX,offsetY;
function reOffset(){
var bound = canv.getBoundingClientRect();
offsetX = bound.left;
offsetY = bound.top;
}
reOffset();
function movement(){
canv.addEventListener('mousedown', mouseDown, false);
canv.addEventListener('mouseup', mouseUp, false);
canv.addEventListener('mousemove', mouseMove, false);
}
function mouseDown(event){
startX=parseInt(event.clientX-offsetX);
startY=parseInt(event.clientY-offsetY);
move = true;
}
function mouseUp(event){
mouseX=parseInt(event.clientX-offsetX);
mouseY=parseInt(event.clientY-offsetY);
move = false;
if(!overlap(newRect)){
rect.push(newRect);
}
make();
//ctx.fillRect(q.left,q.top,q.right-q.left,q.bottom-q.top);
}
function make(){
for(var i = 0; i < rect.length; i++){
var q = rect[i];
ctx.fillStyle = randomColour();
ctx.fillRect(q.left, q.top, q.right - q.left, q.bottom - q.top);
}
}
function mouseMove(event){
if(move){
mouseX=parseInt(event.clientX - offsetX);
mouseY=parseInt(event.clientY - offsetY);
newRect = {
left : Math.min(startX , mouseX),
right : Math.max(startX , mouseX),
top : Math.min(startY , mouseY),
bottom : Math.max(startY , mouseY),
}
ctx.clearRect(0, 0, canv.width, canv.height);
ctx.strokeRect(startX, startY, mouseX-startX, mouseY-startY);
}
}
function randomColour() {
var colour = [];
for (var i = 0; i < 3; i++) {
colour.push(Math.floor(Math.random() * 256));
}
return 'rgb(' + colour.join(',') + ')';
}
function overlap(newRect){
var q1 = newRect;
//if one rect is completely inside another rect
var inside = function(rectx, recty){
return(recty.left >= rectx.left &&
recty.right <= rectx.right &&
recty.top >= rectx.top &&
recty.bottom <= rectx.bottom);
}
//if the new rect is overlapping any existing rect
var isOverlaping = false;
for(var i = 0; i < rect.length; i++){
var q2 = rect[i];
var isIntersecting = !(q1.left > q2.right ||
q1.right < q2.left ||
q1.top > q2.bottom ||
q1.bottom < q2.top);
var isContain = inside(q2, q1) || inside(q1, q2);
if(isIntersecting || isContain){
isOverlaping=true;
}
}
return(isOverlaping);
}
movement();
//clear the canvas for redrawing
document.getElementById('clear').addEventListener('click', function () {
ctx.clearRect(0, 0, canv.width, canv.height);
}, false);
</script>`
<head>
<title>Simple Paint App</title>
</head>
<body>
<canvas id ="canv" width="1000" height="600" ></canvas>
<div id="button" style="position: absolute;">
<input type="button" id="clear" value="Clear">
</div>
</body>
You need to reset the rect array as well.
Adding this to your clear callback function:
document.getElementById('clear').addEventListener('click', function () {
rect = [];
console.log(rect);
ctx.clearRect(0, 0, canv.width, canv.height);
}, false);
should work, while removing the reset of the rect array (as in your clear callback function), logs the old rectangles as well.

How to display multiple arrows on canvas with multiple clicks

I am trying to display multiple arrows on canvas, so when after I click it creates completely new arrow. I am a beginner as you can see, thank you for any help or further ideas of how to improve the code!
var arrowX = -300;
var arrowY = 300;
window.onload = function() {
canvas = document.getElementById("myCanvas");
gc = canvas.getContext("2d");
document.addEventListener("click", mouseClickHandler, false);
window.setInterval(render, 200);
arrow = document.getElementById("arrow");
};
function mouseClickHandler(event) {
if (event.target.tagName.localeCompare("canvas") && event.button == 0) {
arrowX = event.clientX;
arrowY = event.clientY;
}
}
function arrowDown() {
//makes arrow go down
if (arrowY < 450) {
arrowY += 6;
}
if (arrowY < 250) {
arrowY -= 3;
}
}
function render() {
gc.clearRect(0, 0, 1500, 500);
gc.drawImage(arrow, arrowX, arrowY);
arrowDown();
}
<canvas id="myCanvas" width="1500" height="500" style="border:1px solid black;" tabindex="1" onclick="mouseClickHandler(event);">
<img id="arrow" class="arrow" src="https://i.stack.imgur.com/evnjm.png" width="20" height="50" />
</canvas>
I had a question very similar to this! Go ahead and try this code, using a constructor:
var id = 0;
var arrows = [];
function Arrow(x, y) {
this.id = id++
this.y = y || 0
this.x = x || 0
}
// remove the shape from the array function terminate(id) {
arrows = arrows.filter(arrow => arrow.id !== id)
}
// or add a shape
function spawn() {
arrows.push(new Arrow())
}
function moveAll(){
arrows.forEach(arrow => arrow.y++;)
}
function drawAll(){
arrows.forEach(arrow => /* draw arrow;*/)
}
I hope this helped!
Here's the similar question

Adding events to Raphael elements

Hey, I'm trying to add a mousemove and click event to an SVG Raphael Rectangle:
Fiddle: http://jsfiddle.net/neuroflux/nXKbW/1/
Code:
tile = ctx.rect(x*10,y*(i*10),10,10).attr({
fill:'#000000',
stroke: '#ffffff'
});
tile.mouseover(function(e) {
pX = e.pageX;
pY = e.pageY;
});
tile.click(function() {
console.log('x: '+pX+'| y:'+pY);
});
Obviously, for some reason this doesn't function - I get no output onClick :'(
Ok I've got the click function to work. Finally ^_^.
<!DOCTYPE html>
<html>
<head>
<title></title>
<script type="text/javascript" src="https://github.com/DmitryBaranovskiy/raphael/raw/master/raphael-min.js"></script>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script type="text/javascript">
/* GLOBAL VARIABLES */
var drawFrames;
var canvas;
var ctx;
var universe = new Array();
var tile;
var pX,pY = 0;
universe = (
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]
);
/* WINDOW LOAD */
window.onload = function() {
init();
}
/* INITIALISATION */
function init() {
ctx = Raphael(10, 50, 320, 200);
drawFrames = setInterval(drawFrame, 40);
}
/* FRAME RENDER LOOP */
function drawFrame() {
//ctx.clear();
for (var x = 0; x < universe.length; x++) {
for (var y = 0; y < universe[x].length; y++) {
for (var i= 0; i < 11; i++) {
tile = ctx.rect(x*10,y*(i*10),10,10).attr({
fill:'#000000',
stroke: '#ffffff'
}).click(function(e) {
console.log('x: '+e.pageX+'| y:'+e.pageY);
})
}
}
}
}
</script>
</head>
<body>
<canvas id="main" width="800" height="600">
<h1>No Support I'm Afraid...</h1>
</canvas>
</body>
</html>
first give your raphael object an id then bind the click event to it.
tile = ctx.rect(x*10,y*(i*10),10,10).attr({
fill:'#000000',
stroke: '#ffffff'
});
tile.node.id='tile_id';
$('#tile_id').bind('click',function(){alert('clicked');});

Categories

Resources