evandewey evandewey - 24 days ago 4
HTML Question

Why isn't the else block firing?

I'm making an HTML Canvas game (Tron), and I'm want the game to detect when a player hits the border of the canvas. I have an

if
statement that checks if the player is in contact with either their own trail or the opponents, and that works fine, but the parameter for being outside of the canvas (
y >= 780
) doesn't seem to be doing anything. Added a snippet. There's a library for key event handling in there, that's not my code (the library is most likely no longer necessary after I made some changes but that shouldn't have anything to do with my problem). Thanks for your time.



/*! keydrown - v1.2.2 - 2016-03-23 - http://jeremyckahn.github.com/keydrown */
!function(a){var b=function(){var b={};b.forEach=function(a,b){var c;for(c in a)a.hasOwnProperty(c)&&b(a[c],c)};var c=b.forEach;b.getTranspose=function(a){var b={};return c(a,function(a,c){b[a]=c}),b},b.indexOf=function(a,b){if(a.indexOf)return a.indexOf(b);var c,d=a.length;for(c=0;d>c;c++)if(a[c]===b)return c;return-1};var d=b.indexOf;return b.pushUnique=function(a,b){return-1===d(a,b)?(a.push(b),!0):!1},b.removeValue=function(a,b){var c=d(a,b);return-1!==c?a.splice(c,1)[0]:void 0},b.documentOn=function(b,c){a.addEventListener?a.addEventListener(b,c,!1):document.attachEvent&&document.attachEvent("on"+b,c)},b.requestAnimationFrame=function(){return a.requestAnimationFrame||a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||function(b){a.setTimeout(b,1e3/60)}}(),b.noop=function(){},b}(),c={A:65,B:66,C:67,D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80,Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,ENTER:13,SHIFT:16,ESC:27,SPACE:32,LEFT:37,UP:38,RIGHT:39,DOWN:40,BACKSPACE:8,DELETE:46},d=b.getTranspose(c),e=[],f=function(){"use strict";function a(a){this.keyCode=a,this.cachedKeypressEvent=null}function c(a,b,c,d){c?a[b]=c:a[b](d)}return a.prototype._downHandler=b.noop,a.prototype._upHandler=b.noop,a.prototype._pressHandler=b.noop,a.prototype.isDown=function(){return-1!==b.indexOf(e,this.keyCode)},a.prototype.down=function(a){c(this,"_downHandler",a,this.cachedKeypressEvent)},a.prototype.up=function(a,b){c(this,"_upHandler",a,b)},a.prototype.press=function(a,b){this.cachedKeypressEvent=b,c(this,"_pressHandler",a,b)},a.prototype.unbindDown=function(){this._downHandler=b.noop},a.prototype.unbindUp=function(){this._upHandler=b.noop},a.prototype.unbindPress=function(){this._pressHandler=b.noop},a}(),g=function(e){"use strict";var g={};g.Key=f;var h=!1,i=Date.now?Date.now:function(){return+new Date},j=i();return g.tick=function(){var a,b=e.length;for(a=0;b>a;a++){var c=e[a],f=d[c];f&&g[f].down()}},g.run=function(c){h=!0;var d=i(),e=d-j;b.requestAnimationFrame.call(a,function(){h&&(g.run(c),c(e,d))}),j=d},g.stop=function(){h=!1},b.forEach(c,function(a,b){g[b]=new f(a)}),b.documentOn("keydown",function(a){var c=a.keyCode,f=d[c],h=b.pushUnique(e,c),i=g[f];if(i){var j=i.cachedKeypressEvent||{};(j.ctrlKey||j.shiftKey||j.metaKey)&&(h=!0),h&&i.press(null,a)}}),b.documentOn("keyup",function(a){var c=b.removeValue(e,a.keyCode),f=d[c];f&&g[f].up(null,a)}),b.documentOn("blur",function(a){b.forEach(e,function(a){var b=d[a];b&&g[b].up()}),e.length=0}),g}(e);"object"==typeof module&&"object"==typeof module.exports?module.exports=g:"function"==typeof define&&define.amd?define(function(){return g}):a.kd=g}(window);

canvas {
width: 1000px;
height: 800px;
margin-left: auto;
margin-right: auto;
display: block;
border: 1px solid black;
}

h1,h2 {
width: 100px;
text-align: center;
margin-left: auto;
margin-right: auto;
font-family: sans-serif;
}

h2 {
border: 5px solid black;
padding: 5px;
}

h2:hover {
background-color: #7DF9FF;
cursor: pointer;
}

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tron</title>
<script src="keydrown.min.js"></script>
<link rel="stylesheet" type="text/css" href="tron.css"/>
</head>
<body>
<h1>TRON</h1>
<h2>PLAY</h2>
<canvas height="800px" width="1000px"></canvas>
<script>
function game() {
canvas = document.getElementsByTagName('canvas')[0];
ctx = canvas.getContext('2d');

ctx.fillStyle = "blue";
ctx.fillRect(0,0,20,20);
ctx.fillStyle = "red";
ctx.fillRect(980,780,20,20);

x = 980;
y = 780;
a = 0;
b = 0;

// P2 Controls

function moveRight() {
if(kd.UP.isDown() === false && kd.DOWN.isDown() === false && kd.LEFT.isDown() === false && ctx.getImageData(x+21, y, 1, 1).data[0] !== 255 && ctx.getImageData(x+21, y, 1, 1).data[2] !== 255) {
x+=10;
ctx.fillStyle = "red";
ctx.fillRect(x,y,20,20);
} else if(ctx.getImageData(x+21, y, 1, 1).data[0] === 255 || ctx.getImageData(x+21, y, 1, 1).data[2] === 255 || x >= 980) {
alert("Blue wins!");
}
}

kd.RIGHT.down(function() {
clearInterval(dir2);
dir2 = setInterval(moveRight, 25);
});

function moveLeft() {
if(kd.UP.isDown() === false && kd.DOWN.isDown() === false && kd.RIGHT.isDown() === false && ctx.getImageData(x-1, y, 1, 1).data[0] !== 255 && ctx.getImageData(x-1, y, 1, 1).data[2] !== 255) {
x-=10;
ctx.fillStyle = "red";
ctx.fillRect(x,y,20,20);
} else if(ctx.getImageData(x-1, y, 1, 1).data[0] === 255 || ctx.getImageData(x-1, y, 1, 1).data[2] === 255 || x <= 0) {
alert("Blue wins!");
}
}

kd.LEFT.down(function() {
clearInterval(dir2);
dir2 = setInterval(moveLeft, 25);
});

function moveUp() {
if(kd.LEFT.isDown() === false && kd.DOWN.isDown() === false && kd.RIGHT.isDown() === false && ctx.getImageData(x, y-1, 1, 1).data[0] !== 255 && ctx.getImageData(x, y-1, 1, 1).data[2] !== 255) {
y-=10;
ctx.fillStyle = "red";
ctx.fillRect(x,y,20,20);
} else if(ctx.getImageData(x, y-1, 1, 1).data[0] === 255 || ctx.getImageData(x, y-1, 1, 1).data[2] === 255 || y <= 0) {
alert("Blue wins!");
}
}

kd.UP.down(function() {
clearInterval(dir2);
dir2 = setInterval(moveUp, 25);
});

function moveDown() {
if(kd.LEFT.isDown() === false && kd.UP.isDown() === false && kd.RIGHT.isDown() === false && ctx.getImageData(x, y+21, 1, 1).data[0] !== 255 && ctx.getImageData(x, y+21, 1, 1).data[2] !== 255) {
y+=10;
ctx.fillStyle = "red";
ctx.fillRect(x,y,20,20);
} else if(ctx.getImageData(x, y+21, 1, 1).data[0] === 255 || ctx.getImageData(x, y+21, 1, 1).data[2] === 255 || y >= 780) {
alert("Blue wins!");
}
}

kd.DOWN.down(function() {
clearInterval(dir2);
dir2 = setInterval(moveDown, 25);
});

// Controls

function moveD() {
if(kd.W.isDown() === false && kd.S.isDown() === false && kd.A.isDown() === false && ctx.getImageData(a+21, b, 1, 1).data[0] !== 255 && ctx.getImageData(a+21, b, 1, 1).data[2] !== 255) {
a+=10;
ctx.fillStyle = "blue";
ctx.fillRect(a,b,20,20);
} else if(ctx.getImageData(a+21, b, 1, 1).data[0] === 255 || ctx.getImageData(a+21, b, 1, 1).data[2] === 255 || a >= 980) {
alert("Red wins!");
}
}

kd.D.down(function() {
clearInterval(dir1);
dir1 = setInterval(moveD, 25);
});

function moveA() {
if(kd.W.isDown() === false && kd.S.isDown() === false && kd.D.isDown() === false && ctx.getImageData(a-1, b, 1, 1).data[0] !== 255 && ctx.getImageData(a-1, b, 1, 1).data[2] !== 255) {
a-=10;
ctx.fillStyle = "blue";
ctx.fillRect(a,b,20,20);
} else if(ctx.getImageData(a-1, y, 1, 1).data[0] === 255 || ctx.getImageData(a-1, b, 1, 1).data[2] === 255 || a <= 0) {
alert("Red wins!");
}
}

kd.A.down(function() {
clearInterval(dir1);
dir1 = setInterval(moveA, 25);
});

function moveW() {
if(kd.A.isDown() === false && kd.S.isDown() === false && kd.D.isDown() === false && ctx.getImageData(a, b-1, 1, 1).data[0] !== 255 && ctx.getImageData(a, b-1, 1, 1).data[2] !== 255) {
b-=10;
ctx.fillStyle = "blue";
ctx.fillRect(a,b,20,20);
} else if(ctx.getImageData(a, b-1, 1, 1).data[0] === 255 || ctx.getImageData(a, b-1, 1, 1).data[2] === 255 || b <= 0) {
alert("Red wins!");
}
}

kd.W.down(function() {
clearInterval(dir1);
dir1 = setInterval(moveW, 25);
});

function moveS() {
if(kd.A.isDown() === false && kd.W.isDown() === false && kd.D.isDown() === false && ctx.getImageData(a, b+21, 1, 1).data[0] !== 255 && ctx.getImageData(a, b+21, 1, 1).data[2] !== 255) {
b+=10;
ctx.fillStyle = "blue";
ctx.fillRect(a,b,20,20);
} else if(ctx.getImageData(a, b+21, 1, 1).data[0] === 255 || ctx.getImageData(a, b+21, 1, 1).data[2] === 255 || b >= 780) {
alert("Red wins!");
}
}

kd.S.down(function() {
clearInterval(dir1);
dir1 = setInterval(moveS, 25);
});

kd.run(function () {
kd.tick();
});

dir1 = setInterval(moveD, 25);
dir2 = setInterval(moveLeft, 25);
}

document.getElementsByTagName('h2')[0].addEventListener('click', function(){
game();
});
</script>
</body>
</html>




Answer

Learning to program is not easy and little bugs can make it so much more of a chore than a joy.

I can not spot the bug because I am not 100% sure what it is you want to happen.

There is also so many bad things that are just waiting to cause you problems further on.

So rather than find and fix your problem I rewrote your code in a manner that is easier to manage, more friendly with the DOM, and a little less prone to bugs.

The keyboard handle you where using is way overkill for what you need so there is a simple keyboard handler at the top.

I hate the use of alerts. They can cause all sorts of problems, so I changed the code to show results on the canvas.

Rather than repeat the code for each player I added a function to handle a single play passed as an object. The player1, player2 objects contain everything that makes the player unique and the updatePlayer function is called with each player.

There is also a drawPlayer function to draw.

Rather than use timers I update the game with requestAnimationFrame. You should try to avoid using setInterval and setTimeout they do not make for good animation quality and can really make the timing get messed up.

The player keyboard handling is a little complex but at first I thought you wanted that it play with the rule, "move only when only one move key is down." Eg when up and left are held down you had no movement, and when you release the key the player stops (but I saw your keyboard handler was giving keydowns when no keys where down". You can add that behaviour back if that is what you wanted by replacing the & (bitwise and) with a === eg if(keyPadBits & UP){ becomes if(keyPadBits === UP) in the updatePlayer function

I added "use strict" to the top of the game function. This makes the code run faster but also forces you to be more careful when coding as the rules are very strict. This will stop you from creating situations where the code runs and does not report a bug. You may never have the bug happen, or it might and then you will be hard pressed to find it. With strict mode active the error is reported and you can fix it then and there.

(function game() {
    "use strict"; // NEVER CODE WITHOUT THIS LINE or you will live in the hell of a billion bugs
    var canvas = document.getElementsByTagName('canvas')[0];
    //var canvas = document.getElementById('can');
    var ctx = canvas.getContext('2d');
    
    //==============================================================================================================
    // Constants and variables
    // bit positions for keypad 
    const UP = 0b1000;
    const RIGHT = 0b100;
    const DOWN = 0b10;
    const LEFT = 0b1;
    // speeds and sizes
    const MOVE_SPEED = 10;
    const PLAYER_SIZE = 20;
    const PLAY_FIELD_SIZEW = canvas.width - PLAYER_SIZE;
    const PLAY_FIELD_SIZEH = canvas.height - PLAYER_SIZE;
    // Flags game state
    var gameOver = true;  // Must start game as gameover= true or it will not start
    //==============================================================================================================
    // keyboard handler to replace overkill  keydrown lib
    var keys = {};
    var filterKeys = "ArrowUp,ArrowDown,ArrowLeft,ArrowRight,KeyA,KeyS,KeyD,KeyW".split(","); // array of keys to listen to
    function keyboard(event){
        if(filterKeys.indexOf(event.code) > -1){ // filter out unwanted key events
            keys[event.code] = event.type === "keydown";  // true if key down false if not
            event.preventDefault();
        }
    }
    document.body.addEventListener("keydown",keyboard);
    document.body.addEventListener("keyup",keyboard);
    
    
    //==============================================================================================================
    // player restart function
    var restartPlayer = function(){
        this.x = this.start.x;
        this.y = this.start.y;
        this.dx = this.start.dx;
        this.dy = this.start.dy;
        this.crash = false;            
    }        
    // player details
    var player1 = {
        x : 980,       // Should be set to player start positions
        y : 0,
        dx : 0,   // movement direction
        dy : 0,
        start : {x: canvas.width - PLAYER_SIZE, y : canvas.height - PLAYER_SIZE,dx :-1,dy :0},
        crash : false,
        ID : 0,
        col : "red",
        restart : restartPlayer,
    }
    var player2 = {
        x : 0,       // Should be set to player start positions
        y : 0,
        dx : 0,   // movement direction
        dy : 0,
        start : {x: 0, y : 0,dx : 1,dy : 0},
        crash : false,
        ID : 1,
        col : "blue",
        restart : restartPlayer,
    }
    
    //==============================================================================================================
    // check player keys moves if needed and checks for crashes
    function updatePlayer(player){
        debugger
        var keyPadBits = 0;
        // set bits for all keys down
        if(player.ID === 0){ // first player
            keyPadBits |= keys.ArrowUp   ? UP : 0;
            keyPadBits |= keys.ArrowRight? RIGHT : 0;
            keyPadBits |= keys.ArrowDown ? DOWN : 0;
            keyPadBits |= keys.ArrowLeft ? LEFT : 0;
        }else{
            keyPadBits |= keys.KeyW ? UP : 0;
            keyPadBits |= keys.KeyD ? RIGHT : 0;
            keyPadBits |= keys.KeyS ? DOWN : 0;
            keyPadBits |= keys.KeyA ? LEFT : 0;
        }
        // get the movement direction in order of priority
        // Can have diagonals eg if(keyPadBits & (UP+LEFT))
        if(keyPadBits & UP){  // YES that is a single & as a bitwise and operator
            player.dy = -1;
            player.dx = 0;                
        }else if(keyPadBits & DOWN){// YES that is a single & as a bitwise and operator

            player.dy = 1;
            player.dx = 0;                
        }else if(keyPadBits & RIGHT){// YES that is a single & as a bitwise and operator

            player.dx = 1;
            player.dy = 0;                
        }else if(keyPadBits & LEFT){// YES that is a single & as a bitwise and operator

            player.dx = -1;
            player.dy = 0;                
        }
        // check pixel in direction of movement 
        var x = player.x + player.dx + PLAYER_SIZE * (player.dx > 0 ? 1 : 0);
        var y = player.y + player.dy + PLAYER_SIZE * (player.dy > 0 ? 1 : 0);
        var data = ctx.getImageData(x,y, 1, 1).data;
        if(data[0] !== 255 && data[2] !== 255){
            player.x += MOVE_SPEED * player.dx;
            player.y += MOVE_SPEED * player.dy;
            // player has moved so check edge crash
            if( player.x < 0 || player.y < 0 || player.x > PLAY_FIELD_SIZEW || player.y > PLAY_FIELD_SIZEH){ 
                player.crash = true;
            }                
        } else { // player must have crashed if red and blue are on
            player.crash = true;
        }
        
    }
    // draws the player at current position
    function drawPlayer(player){
        ctx.fillStyle = player.col;
        ctx.fillRect(player.x, player.y, PLAYER_SIZE, PLAYER_SIZE);  
    }
    // updates both players    
    function playerUpdate(){
        if(!gameOver){
            updatePlayer(player1);
            updatePlayer(player2);
            // draw both players after both have moved or you would give one player a slight advantage of being
            // invisible at his current position
            drawPlayer(player1);
            drawPlayer(player2);
            if(player1.crash || player2.crash){
                gameOver = true;
                var text = "";
                var col = "black";
                if(player1.crash && player2.crash){  // may be a draw if both crash at the same time
                    text = "It's a draw!";
                }else  if(player1.crash){  
                    text = "Blue wins!";
                    col= "blue";
                }else {
                    text = "Red wins!";
                    col = "red";
                }
                ctx.font = "64px Arial black";
                ctx.textAlign = "center";
                ctx.fillStyle = col;
                ctx.strokeStyle = "black";
                ctx.lineWidth = 4;
                if(col !== "black"){
                    ctx.strokeText(text,canvas.width * 0.5, canvas.height * 0.4);
                }
                ctx.fillText(text,canvas.width * 0.5, canvas.height * 0.4);
                ctx.fillStyle = "black";
                ctx.font = "24px Arial black";
                ctx.fillText("Click 'PLAY' to start again.",canvas.width * 0.5, canvas.height * 0.6 );
            }
        }
    }
    // function to restart the game
    function restartGame(){
        player1.restart();
        player2.restart();
        gameOver = false;
        ctx.clearRect(0,0,canvas.width,canvas.height);
    }

    // main animation loop is called 60 times a second.
    function update(time){
        playerUpdate();
        if(!gameOver){
            requestAnimationFrame(update);
        }
    }
    document.getElementsByTagName('h2')[0].addEventListener('click', function(){
        if(gameOver){
            restartGame();
            requestAnimationFrame(update);
        }
    });
})();  // run the function now (function(){})() is an immediately invoked function
  
      
canvas {
  width: 1000px;
  height: 800px;
  margin-left: auto;
  margin-right: auto;
  display: block;
  border: 1px solid black;
}

h1,h2 {
  width: 100px;
  text-align: center;
  margin-left: auto;
  margin-right: auto;
  font-family: sans-serif;
}

h2 {
  border: 5px solid black;
  padding: 5px;
}

h2:hover {
  background-color: #7DF9FF;
  cursor: pointer;
}
    <h1>TRON</h1>
    <h2>PLAY</h2>
    <canvas height="800px" width="1000px"></canvas>