Zoom In/Out Interferes with Canvas Drawing Location - javascript
I am working with some classmates to make a word search game. It is pretty much done, but the only problem that we are facing is when the user decides to zoom in or out. When the user clicks and drags their cursor within the canvas, a red line highlights the letters under the cursor. After zooming, the highlighting appears somewhere other than under the mouse.
At first, we thought the problem was caused by window scrolling because the canvases were bigger than the screen, so we made them
You can recreate the problem by going here, zooming in, scrolling down a little bit, and trying to highlight a string of letters.
Please only include suggestions with Javascript: no JQuery, additional libraries, any other languages.
<html>
<head>
<title>Canvas Testing</title>
<script type="text/javascript">
var canvas,//canvas html tag
wordsCanvas,//displays words
wContext,//wordsCanvas context
context,//to edit canvas items
words = ["KING","HOMEWORK","BASEBALL","SIDEWALK","CUPCAKE","WHITEHOUSE","ISLAND","SOCCER","INDEPENDENCE","LOVE","CALCULUS","BEACH","SUMMER","PET","MICHIGAN","CANDY","WORLD","SIX","SNOW","SWEET"],//array of the words users must find
found = new Array(),
w,//width
h,//height
w1,//width of each letter (board 6 letters wide right now)
h1,//height of each letter (board 6 letters tall right now)
draw = false,//tells when the game should highlight a letter
letterPoints = new Array(),//holds the coordinates of each letter
lines = new Array(),//holds the coordinates of each line for a correct word highlighted
startLP = null,//holds the Letter object (letter, x, y) for the start of the line
endLP = null;//holds the Letter object (letter, x, y) for the end of the line
function init(){//initializes the canvas, context,w,h variables
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
canvas.height = window.innerHeight*600/630;
canvas.width = window.innerWidth*900/1380;
w=canvas.width;
h=canvas.height;
w1 = w/20;
h1 = h/20;
context.font = h1+"px Courier";
context.textAlign = "left";
context.textBaseline = "top";
wordsCanvas = document.getElementById("wordsCanvas");
wordsCanvas.height = window.innerHeight*600/630;
wordsCanvas.width = window.innerWidth*400/1380;
wContext = wordsCanvas.getContext("2d");
wContext.font = h1+"px Georgia";
wContext.textAlign = "left";
wContext.textBaseline = "top";
background();
fillWords(-1);
//add event listeners for mouse actions
canvas.addEventListener("mousedown", function(event) {
setLine("press",event);
});
canvas.addEventListener("mouseup", function(event) {
setLine("release",event);
});
canvas.addEventListener("mousemove", function(event) {
setLine("drag",event);
});
canvas.addEventListener("mouseout", function(event) {
setLine("out",event);
});
}
function fillWords(greenIndex){//displays words to search for
wContext.clearRect(0,0,300,h);
var index = 0;
if(greenIndex != -1)
{
found[found.length] = words.splice(greenIndex,1);
}
for(var i=0; i<h; i+=h1)
{
if(index<words.length)
{
wContext.fillStyle = "red";
wContext.fillText(words[index],10,i);
}
else
{
wContext.fillStyle = "green";
wContext.fillText(found[found.length-((h/h1)-index)],10,i);
}
index++;
}
wContext.fillStyle = "black";
}
function background(){//sets the background to the letters and then draws the lines that lay on correctly highlighted words words
var letterCount = 0;//counts # of letters on the board
//one string that represents all letters on the board
var backLets = "harinavesenanotheasp"+
"oobalremmusicwonsdpa"+
"momvtcalclsxvaiybaev"+
"swttysumvkingbddmrzd"+
"ahaeuyacemjtavpniche"+
"nivpnagihcimbseaceoy"+
"tthesteatkijeodchqmj"+
"ueesnodbsggmaccutsea"+
"mhmikalviabahceraqwm"+
"motislandbtcvetiwmoi"+
"eurwtenkeeeterntanra"+
"tsoolotewheewhthsekl"+
"sewroxvrarwdbaseball"+
"nehtvxjglmsadalkazqt"+
"odceetenkenstcvepcap"+
"ttaabvdlcupcakepeaxm"+
"rieqindependenceplia"+
"afbacucakehowrkdkisf"+
"chldlrowbrqmmuscqflg"+
"amerivegdsuluclacoev";
context.fillStyle = "black";
for(var y=0; y<h; y+=h1)//goes through the board and draws each letter, then stores their coordinates in the letterPoints array
{
for(var x=0; x<w; x+=w1)
{
r = y/h1 + 1;//tells the row that the letter is in
c = x/w1 + 1;//tells the column that the letter is in
temp = new Letter(backLets.charAt(letterCount),x,y,r,c);
context.fillText(temp.letter.toUpperCase(),temp.x,temp.y);
if(letterPoints.length<400)
letterPoints[letterPoints.length] = temp;
letterCount++;
}
}
/*go through lines array holding coordinates for lines that lay on correct words*/
for(var z=0; z<lines.length; z++)
{
//this if structure allows the words matching the list to be highlighted in different colors so adjacent words will not be highlighted into blocks
context.fillStyle = "lime";
var coords = lines[z];//elements of lines array are not empty but the drawLine isn't processing them
drawLine(coords[0],coords[1]);
}
}
function setLine(action, e){//sets the coordinates for the lines to be drawn
if(action == "press")
{
startLP = findNearestLP(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);//gets nearest coordenate to a click and returns a letter object with that info
if(startLP != null)
{
draw = true;
}
}
if(action == "drag")
{
//updates the last coordinates that the dragged mouse is on and draws a line to that point from the start
if(draw)
{
context.clearRect(0,0,w,h);
background();
endLP = findNearestLP(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
context.fillStyle = "red";
drawLine(startLP, endLP);
}
}
if(action == "release" || (action == "out" && draw))
{
draw = false;
/*If a correct word is highlighted, store the start and end coordinates
else clear*/
endLP = findNearestLP(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
//Get the letters that are highlighted by the line
if(endLP != null)
{
var word = getWord();//returns the word that was made from the start to end point by adding the characters that were highlighted
if(word != null && matchWords(word)/*This string will be replaced by a word from the list of word search targets*/)
{
lines[lines.length] = [startLP,endLP];
}
}
context.clearRect(0,0,w,h);//clears the board of any drawn lines
background();//if the line highlighted a word from the list, this method should redraw that line
if(words.length == 0)
alert("Congratulations! You win!");
}
}
//searches through the words array to see if the highlighted word is there
function matchWords(target){
if(words.indexOf(target.toUpperCase()) != -1)
{
fillWords(words.indexOf(target.toUpperCase()));
return true;
}
return false;
}
//uses coordinates from setLine() to draw the lines
function drawLine(start, end){
context.globalAlpha = 0.6;//sets transparency of lines
/*
Check up,down,left,right,diagonals
See if start and end can make a valid line
If yes, find the letters from start to end and store it as a word
*/
if(start.x == end.x && end.y>start.y)//checking down
{
context.fillRect(start.x,start.y,w1,(end.y+h1)-start.y);
}
else if(start.x == end.x && end.y<start.y)//checking up
{
context.fillRect(end.x,end.y,w1,(start.y+h1)-end.y);
}
else if(start.y == end.y && end.x>start.x)//checking left to right
{
context.fillRect(start.x,start.y,(end.x+w1)-start.x,h1);
}
else if(start.y == end.y && end.x<start.x)//checking right to left
{
context.fillRect(end.x,end.y,(start.x+w1)-end.x,h1);
}
else if(start.y > end.y && start.x < end.x && ((start.r-end.r)/(end.c-start.c))==1)//checking left to right diagonal (down to up)
{
for(var z = letterPoints.length-1; z>=0; z--)
{
var temp = letterPoints[z];
if((start.x<=temp.x && end.x>=temp.x && start.y>=temp.y && end.y<=temp.y) && ((start.y-temp.y==0 && temp.x-start.x==0) || ((start.r-temp.r)/(temp.c-start.c))==1))//((start.r-temp.r)/(temp.c-start.c))==1 means if the slope of between two points is 1
{
context.fillRect(temp.x,temp.y,w1,h1);
}
}
}
else if(start.y < end.y && start.x < end.x && ((end.r-start.r)/(end.c-start.c))==1)//checking left to right diagonal (top to bottom)
{
for(var z = 0; z<letterPoints.length; z++)
{
var temp = letterPoints[z];
if((start.x<=temp.x && end.x>=temp.x && start.y<=temp.y && end.y>=temp.y) && ((start.y-temp.y==0 && temp.x-start.x==0) || ((temp.r-start.r)/(temp.c-start.c))==1))//((temp.r-start.r)/(temp.c-start.c))==1 means if the slope of between two points is 1
{
context.fillRect(temp.x,temp.y,w1,h1);
}
}
}
else if(start.y > end.y && start.x > end.x && ((start.r-end.r)/(start.c-end.c))==1)//checking right to left diagonal (down to up)
{
for(var z = letterPoints.length-1; z>=0; z--)
{
var temp = letterPoints[z];
if((start.x>=temp.x && end.x<=temp.x && start.y>=temp.y && end.y<=temp.y) && ((start.y-temp.y==0 && start.x-temp.x==0) || ((start.r-temp.r)/(start.c-temp.c))==1))
{
context.fillRect(temp.x,temp.y,w1,h1);
}
}
}
else if(start.y < end.y && start.x > end.x && ((end.r-start.r)/(start.c-end.c))==1)//checking right diagonal (top to bottom)
{
for(var z = 0; z<letterPoints.length; z++)
{
var temp = letterPoints[z];
if((start.x>=temp.x && end.x<=temp.x && start.y<=temp.y && end.y>=temp.y) && ((start.y-temp.y==0 && start.x-temp.x==0) || ((temp.r-start.r)/(start.c-temp.c))==1))
{
context.fillRect(temp.x,temp.y,w1,h1);
}
}
}
context.globalAlpha = 1.0;//sets transparency back to 1
}
function findNearestLP(clickX,clickY){//finds the nearest letter coordinate from the user's click
for(var z = 0; z<letterPoints.length; z++)
{
var lp = letterPoints[z];
if((clickX<=lp.x+w1 && clickX>=lp.x) && (clickY<=lp.y+h1 && clickY>=lp.y))
{
return letterPoints[z];
}
}
return null;
}
function getWord()
{
var result = "";
/*
Check up,down,left,right,diagonals
See if startLP and endLP can make a valid line
If yes, find the letters from start to end and store it as a word
*/
if(startLP.x == endLP.x && endLP.y>startLP.y)//checking down
{
for(var z = 0; z<letterPoints.length; z++)
{
var temp = letterPoints[z];
if(temp.x == startLP.x && temp.y>=startLP.y && temp.y<=endLP.y)
{
result += temp.letter;
}
}
}
else if(startLP.x == endLP.x && endLP.y<startLP.y)//checking up
{
for(var z = letterPoints.length-1; z>=0; z--)
{
var temp = letterPoints[z];
if(temp.x == startLP.x && temp.y<=startLP.y && temp.y>=endLP.y)
{
result += temp.letter;
}
}
}
else if(startLP.y == endLP.y && endLP.x>startLP.x)//checking left to right
{
for(var z = 0; z<letterPoints.length; z++)
{
var temp = letterPoints[z];
if(temp.y == startLP.y && temp.x>=startLP.x && temp.x<=endLP.x)
{
result += temp.letter;
}
}
}
else if(startLP.y == endLP.y && endLP.x<startLP.x)//checking right to left
{
for(var z = letterPoints.length-1; z>=0; z--)
{
var temp = letterPoints[z];
if(temp.y == startLP.y && temp.x<=startLP.x && temp.x>=endLP.x)
{
result += temp.letter;
}
}
}
else if(startLP.y > endLP.y && startLP.x < endLP.x && ((startLP.r-endLP.r)/(endLP.c-startLP.c))==1)//checking left to right diagonal (down to up)
{
for(var z = letterPoints.length-1; z>=0; z--)
{
var temp = letterPoints[z];
if((startLP.x<=temp.x && endLP.x>=temp.x && startLP.y>=temp.y && endLP.y<=temp.y) && ((startLP.y-temp.y==0 && temp.x-startLP.x==0) || ((startLP.r-temp.r)/(temp.c-startLP.c))==1))
{
result += temp.letter;
}
}
}
else if(startLP.y < endLP.y && startLP.x < endLP.x && ((endLP.r-startLP.r)/(endLP.c-startLP.c))==1)//checking left to right diagonal (top to bottom)
{
for(var z = 0; z<letterPoints.length; z++)
{
var temp = letterPoints[z];
if((startLP.x<=temp.x && endLP.x>=temp.x && startLP.y<=temp.y && endLP.y>=temp.y) && ((startLP.y-temp.y==0 && temp.x-startLP.x==0) || ((temp.r-startLP.r)/(temp.c-startLP.c))==1))
{
result += temp.letter;
}
}
}
else if(startLP.y > endLP.y && startLP.x > endLP.x && ((startLP.r-endLP.r)/(startLP.c-endLP.c))==1)//checking right to left diagonal (down to up)
{
for(var z = letterPoints.length-1; z>=0; z--)
{
var temp = letterPoints[z];
if((startLP.x>=temp.x && endLP.x<=temp.x && startLP.y>=temp.y && endLP.y<=temp.y) && ((startLP.y-temp.y==0 && startLP.x-temp.x==0) || ((startLP.r-temp.r)/(startLP.c-temp.c))==1))
{
result += temp.letter;
}
}
}
else if(startLP.y < endLP.y && startLP.x > endLP.x && ((endLP.r-startLP.r)/(startLP.c-endLP.c))==1)//checking right diagonal (top to bottom)
{
for(var z = 0; z<letterPoints.length; z++)
{
var temp = letterPoints[z];
if((startLP.x>=temp.x && endLP.x<=temp.x && startLP.y<=temp.y && endLP.y>=temp.y) && ((startLP.y-temp.y==0 && startLP.x-temp.x==0) || ((temp.r-startLP.r)/(startLP.c-temp.c))==1))
{
result += temp.letter;
}
}
}
if(result != "")
return result;
return null;
}
//letter class
function Letter(letter,x,y){
//The letter variable is mainly used for getting the highlighted word
this.letter = letter.charAt(0);
//the x and y coordinate variables are used for drawing the highlighting line and getting the letter variable at certain coordinates
this.x = x;
this.y = y;
//the r and c variables are used to keep track of which rows and columns the letters are on. This is very helpful for drawing the highlighting line on a diagonal
this.r = r;
this.c = c;
}
</script>
</head>
<body onload="init()">
<canvas id="canvas" style="border: 1px solid #000000;"></canvas>
<canvas id="wordsCanvas" style="border: none;"></canvas>
</body>
By using only offsetLeft and offsetTop inside setLine, you do not take into account the scrolling.
Killer weapon here is getBoundingClientRect, that will give you relative position of the canvas taking every aspect into account :
function setLine(action, e){//sets the coordinates for the lines to be drawn
var bRect = canvas.getBoundingClientRect();
var relX = e.clientX - bRect.left ;
var relY = e.clientY - bRect.top;
if(action == "press")
{
startLP = findNearestLP(relX, relY);//gets nearest coordenate to a click and returns a letter object with that info
if(startLP != null)
{
draw = true;
}
}
if(action == "drag")
{
//updates the last coordinates that the dragged mouse is on and draws a line to that point from the start
if(draw)
{
context.clearRect(0,0,w,h);
background();
endLP = findNearestLP(relX, relY);
context.fillStyle = "red";
drawLine(startLP, endLP);
}
}
if(action == "release" || (action == "out" && draw))
{
draw = false;
/*If a correct word is highlighted, store the start and end coordinates
else clear*/
endLP = findNearestLP(relX, relY);
//Get the letters that are highlighted by the line
if(endLP != null)
{
var word = getWord();//returns the word that was made from the start to end point by adding the characters that were highlighted
if(word != null && matchWords(word)/*This string will be replaced by a word from the list of word search targets*/)
{
lines[lines.length] = [startLP,endLP];
}
}
context.clearRect(0,0,w,h);//clears the board of any drawn lines
background();//if the line highlighted a word from the list, this method should redraw that line
if(words.length == 0)
alert("Congratulations! You win!");
}
}
Related
Why isn't my condition working?
In javascript, I'm making an HTML canvas game, and in that game I have an object type/constructor called gamePiece. gamePiece has a function called checkCollision: this.checkCollision = function(piece){ var collisionX = piece.x >= this.x && piece.x <= (this.x + this.width); var collisionY = piece.y <= this.y && piece.y <= (this.y - this.height); if(collisionX || collisionY){ return true; } else { return false; } } which is called by update() function update(){ context.clearRect(0, 0, game.width, game.height); for(var i = 0; i < gamePieces.length; i++){ gamePieces[i].update(); for(var mi = 0; mi < gamePieces.length; mi++){ gamePieces[i].checkCollision(gamePieces[mi]); if(gamePieces[i].checkCollision(gamePieces[mi]) == true){ gamePieces[i].collisionFunction(); } } } } setInterval(function(){update();}, 1); I have another object that is supposed to give a speed boost upon colliding with another game piece, and it logs every time it gives a speed boost. var speedBooster = new gamePiece(25,25,"red",300,300,0); speedBooster.collisionFunction = function(){ for(var whichpiece = 0; whichpiece < gamePieces.length; whichpiece++){ if(speedBooster.checkCollision(gamePieces[whichpiece]) == true && gamePieces[whichpiece] != this){ gamePieces[whichpiece].speed += 10; console.log("gamePieces[" + whichpiece + "] has been given a speed boost."); } } } But it gives a speed boost whenever a piece is behind it, and I put the "piece.x >= this.x &&" there for a reason. Why is JavaScript ignoring the condition I gave it?
Try var collisionX = piece.x >= this.x && piece.x <= (this.x + this.width); var collisionY = piece.y >= this.y && piece.y <= (this.y + this.height); if(collisionX && collisionY){ return true; } else { return false; } To test if two objects overlap. Where the object has x,y as the top left and w,h as width and height //Returns true if any part of box1 touches box2 function areaTouching(box1,box2){ return ! (box1.x > box2.x + box2.w || box1.x + box1.w < box2.x || box1.y > box2.y + box2.h || box1.y + box1.h < box2.y) }
Is this a New or Faulty collision system?
I am making a top down 2D game with box collisions. Essentially this code just iterates through all pixels left to check for "Collision". If none move box. I have not seen anyone else do this so I am wondering. What could be faulty with this system? if (keyLeft) { var canMove = true; for(var x = 0; x < 64; x++){ var tile = this._world.getTile(new Vector2(this._position.x - 2, this._position.y + x)); if(tile != undefined){ if(tile.material.isCollidable == true){ canMove = false; this._hsp = 0; } } } if (canMove == true){ this._position.x -= 2; } }
How to determine what JavaScript causes a browser crash?
I'm brand new to JavaScript, and have a crashing application. I have no idea what would cause the crash. Here is the code: <script> //constants var Col = 20, Rows = 20; var cellHeight = 25; var cellWidth = 25; var foodX; var score; var foodY; var Nothing = 0, Snake = 1, Food = 2; var Left = 37, Up = 38, Right = 39, Down = 40; var canvas = document.getElementById('snakeCanvas'); var context = canvas.getContext('2d'); var dead = "false"; var snakeDirection = null; var keystate; var snake = []; function start() //this is where we begin the long journey { init(); Tick(); } function init() { snake = [{ x: 5, y: 5 }]; snakeDirection = null; score = 0; document.getElementById("score").innerHTML = "Score: " + score; setFood(); keystate = null; } function Tick() // just liker a timer tick { document.addEventListener("keydown", function (evt) { keystate = evt.keyCode; // checks key presses }); //document.addEventListener("keyup", function (evt) { //delete keystate[evt.keyCode]; //}); update(); //after we check for a key press we update alllll the stuff setTimeout(Tick, 300); //} } function update() { checkKey(); // checks what key has been pressed for (var i = snake.length-1; i > 0; i--) { snake[i].y = snake[i-1].y; snake[i].x = snake[i-1].x } switch (snakeDirection) { // keys case "DOWN": snake[0].y++; break; case "UP": snake[0].y--; break; case "RIGHT": snake[0].x++; break; case "LEFT": snake[0].x--; break; } draw(); //draws all the stuff like food and snake checkCollisions(); // self explaintory name } function checkKey() //Change the direction of the snake cant go backwards too { if (keystate == Left && snakeDirection != "RIGHT" ) { snakeDirection = "LEFT"; } if (keystate == Up && snakeDirection != "DOWN") { snakeDirection = "UP"; } if (keystate == Right && snakeDirection != "LEFT") { snakeDirection = "RIGHT"; } if (keystate == Down && snakeDirection != "UP") { snakeDirection = "DOWN"; } } function setFood() { //WE ARE RUNNING OUT OF FOOD WE NEED NEW PROVISIONS var next = "true" do { foodX = Math.floor((Math.random() * Rows)); foodY = Math.floor((Math.random() * Col)); for (var i = 0; i < snake.length; i++) { // IT SUCKS WHEN I CANT EAT FOOD BECAUSE ITS ALREADY INSIDE OF ME if (snake[i].x == foodX && snake[i].y == foodY) { next = "false" } } } while (next == "false") draw(); // Pretty pictures } function checkCollisions() { for (var i = 1; i < snake.length; i++) { // STOP hitting yourself if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) { init(); } } if (snake[0].y < 0 || snake[0].y > Rows || snake[0].x < 0 || snake[0].x > Col) // you are forbidon to veture from the canvas { init(); } if (snake[0].x == foodX && snake[0].y == foodY) { //Yummy FOOD EAT EAT EAT score++; document.getElementById("score").innerHTML = "Score: " + score; setFood(); snake.push({ x: null, y: null }); // I got fatter } } function draw() { context.clearRect(0, 0, canvas.width, canvas.height); // clears canvas context.fillStyle = "#FF0000"; // pretty colour for the head of the snake context.fillRect(snake[0].x * cellWidth, snake[0].y * cellWidth, cellWidth, cellHeight); context.fillStyle = "#09F"; for (var i = 1; i < snake.length; i++) { context.fillRect(snake[i].x * cellWidth, snake[i].y * cellWidth, cellWidth, cellHeight); } context.fillStyle = "#F90"; // FOOD FOOD FOOD FOOD context.fillRect(foodX * cellWidth, foodY * cellWidth, cellWidth, cellHeight); } start(); // starts hence the name start </script>
OK not bad for a beginer if you wrote it all your self. Your problem is with the keydown event. You are creating a new handler each time you tick. This will lead to a crash. You only need to create the event handler once for the page, it will remain active until you leave the page. To fix your problem move adding the keyDown listener to just above the function Start, as shown below. var snake = []; document.addEventListener("keydown", function (evt) { keystate = evt.keyCode; // checks key presses }); function start(){ init(); Tick(); } Also just a because to me it looks weird. true and false are not strings you dont need to put quotes around them. Though using them as strings still works. You have function setFood() { //WE ARE RUNNING OUT OF FOOD WE NEED NEW PROVISIONS var next = "true" do { foodX = Math.floor((Math.random() * Rows)); foodY = Math.floor((Math.random() * Col)); for (var i = 0; i < snake.length; i++) { if (snake[i].x == foodX && snake[i].y == foodY) { next = "false" } } } while (next == "false") draw(); } would be better written as follows function setFood() { var next = true; // removed the qoutes do { foodX = Math.floor((Math.random() * Rows)); foodY = Math.floor((Math.random() * Col)); for (var i = 0; i < snake.length; i++) { if (snake[i].x == foodX && snake[i].y == foodY) { next = false; // removed the quotes. // no point continuing the for loop as you know you need to // reposition the food so use the break token break; // breaks out of the closest loop } } } while ( !next ) // removed next == "false" and replaced with // ! next. "!" means "Not". do while next not true // you have the draw here but you draw every tick so it would be best if // you removed it as the next draw is less than 1/3 of a second away anyways // draw(); // removed needless draw } Good work. Hope you get a good mark for it.
Snake Game in Javascript
I'm really new to Javascript, so I decided to create a simple SnakeGame to embed in HTML. However, my code for changing the snake's direction freezes up after a few turns. Note: I'm running this in an HTML Canvas. Source: var Canvas; var ctx; var fps = 60; var x = 0; var seconds = 0; var lastLoop; var thisLoop; var tempFPS = 0; var blockList = []; var DEFAULT_DIRECTION = "Right"; var pendingDirections = []; function update() { x += 1; thisLoop = new Date(); tempFPS = 1000 / (thisLoop - lastLoop); lastLoop = thisLoop; tempFPS = Math.round(tempFPS*10)/10; if (x==10){ document.getElementById("FPS").innerHTML = ("FPS: " + tempFPS); } //Rendering for (var i = 0; i<blockList.length; i++){ var block = blockList[i]; draw(block.x, block.y); } if (x==5){ x=0; seconds+=1; //Updates once per x frames moveBlocks(); } } function moveBlocks(){ if(blockList.length === 0){ return; } for (var j = 0; j<pendingDirections.length; j++){ if (b >= blockList.length -1){ pendingDirections.shift(); }else { //Iterates through each direction that is pending var b = pendingDirections[j].block; try{ blockList[b].direction = pendingDirections[j].direction; } catch(err){ alert(err); } pendingDirections[j].block++; } } for (var i = 0; i<blockList.length; i++){ var block = blockList[i]; clear(block.x, block.y); if (block.direction == "Down"){ block.y += BLOCK_SIZE; } else if (block.direction == "Up"){ block.y -= BLOCK_SIZE; } else if (block.direction == "Left"){ block.x -= BLOCK_SIZE; } else if (block.direction == "Right"){ block.x += BLOCK_SIZE; } else { alert(block.direction); } draw(block.x, block.y); } } function init(){ lastLoop = new Date(); window.setInterval(update, 1000/fps); Canvas = document.getElementById("Canvas"); ctx = Canvas.getContext("2d"); } //The width/height of each block var BLOCK_SIZE = 30; //Draws a block function draw(x, y) { ctx.fillStyle = "#000000"; ctx.fillRect(x,y,BLOCK_SIZE,BLOCK_SIZE); } function clear(x,y){ ctx.fillStyle = "#FFFFFF"; ctx.fillRect(x,y,BLOCK_SIZE,BLOCK_SIZE); } function processInput(key){ if (key == 110){ //n (new) newBlock(BLOCK_SIZE*4,0); newBlock(BLOCK_SIZE*3,0); newBlock(BLOCK_SIZE*2,0); newBlock(BLOCK_SIZE*1,0); newBlock(0,0); } else if (key == 119){ changeDirection("Up"); } else if (key == 115){ changeDirection("Down"); } else if (key == 97){ changeDirection("Left"); } else if (key == 100){ changeDirection("Right"); } else if (key==122){ var pDir = "Pending Directions: "; for (var i = 0; i<pendingDirections.length; i++){ pDir += pendingDirections[i].direction + ", "; } alert(pDir); } else if (key == 120){ var dir = "Directions: "; for (var j = 0; j<blockList.length; j++){ dir += blockList[j].direction + ", "; } alert(dir); } else { alert("KEY: " +key); } } function changeDirection(d){ var LD = blockList[0].direction; var valid = false; if (d == "Up"){ if(LD != "Down"){ valid = true; } } else if (d == "Down"){ if(LD != "Up"){ valid = true; } } else if (d == "Left"){ if(LD != "Right"){ valid = true; } } else if (d == "Right"){ if(LD != "Left"){ valid = true; } } if (d == LD) { valid = false;} if (valid){ var dir = {'direction' : d, 'block' : 0}; pendingDirections.unshift(dir); } } function newBlock(x, y){ var block = {'x': x, 'y' : y, 'direction' : DEFAULT_DIRECTION}; //This works: alert(block['x']); draw(x,y); blockList.push(block); } Thanks
As Evan said, the main issue is how you are handling pending directions. The issue occurs when you turn twice in rapid succession, which causes two pending directions to be added for the same block. If these aren't handled in the correct order, then the blocks may move in the wrong direction. On every update, only one pending direction for each block is needed, so I redesigned how this is handled to avoid multiple directions on one block during a single update. Here is the link to it: http://jsbin.com/EkOSOre/5/edit Notice, when a change in direction is made, the pending direction on the first block is updated, overwriting any existing pending direction. if (valid) { blockList[0].pendingDirection = direction; } Then, when an update occurs, the list of blocks is looped through, and the pending direction of the next block is set to be the current direction of the current block. if(!!nextBlock) { nextBlock.pendingDirection = block.direction; } If the current block has a pending direction, set the direction to the pending direction. if(block.pendingDirection !== null) { block.direction = block.pendingDirection; } Then update the block locations like normal. You also had various other issues, such as using a variable (b) before it was initialized, and how you caught the null/undefined error (you should just do a check for that situation and handle it appropriately), but this was the main issue with your algorithm. You'll also want to remove the old blocks when the user hits 'n', because the old one is left, increasing the speed and number of total blocks present. Good luck with the rest of the game, and good luck learning JavaScript.
JavaScript if parameter, Ignoring Transparency in images
I need to make if parameters optional. If that makes sense. If you notice both of my functions are 98% the same, I need to turn this difference into a parameter and it's just not clicking for me. getElement(x, y, class) where the new parameter class changes what I have marked as //HERE in my code //get the element under the mouse, ignoring all transparency. function getElement(x, y){ var elem = document.elementFromPoint(x, y); if($(elem).parents("#game").length != 1){ return -1; } var pixelAlpha = mousePixelOpacity(elem, x, y); var track = Array; for(var i = 0;i<50;i++){ if($(elem).attr("id") == "game"){ return -1;} if(pixelAlpha == 0){ /////////////////////////////////////////HERE track[i] = elem; for(var z = 0; z<i+1; z++){ //hide elements $(track[z]).hide(); } elem = document.elementFromPoint(x, y); //set the element right under the mouse. pixelAlpha = mousePixelOpacity(elem, x, y); for(var z = 0; z<i+1; z++){ //show all the recently hidden elements $(track[z]).show(); } } if(pixelAlpha != 0){ ///////////////////////////////////////// AND HERE return elem; } } return -1; } //get the tile under the mouse, even if it's behind an object function getTile(x, y){ var elem = document.elementFromPoint(x, y); if($(elem).parents("#game").length != 1 && $(elem).attr("id") != "tileHighlight"){ return -1; } var pixelAlpha = mousePixelOpacity(elem, x, y); var track = Array; for(var i = 0;i<50;i++){ if($(elem).attr("id") == "game"){ return -1;} if(pixelAlpha == 0 || $(elem).attr('class') != "tile"){ /////////HERE track[i] = elem; for(var z = 0; z<i+1; z++){ //hide elements $(track[z]).hide(); } elem = document.elementFromPoint(x, y); //set the element right under the mouse. pixelAlpha = mousePixelOpacity(elem, x, y); for(var z = 0; z<i+1; z++){ //show all the recently hidden elements $(track[z]).show(); } } if($(elem).attr('class') == "tile" && pixelAlpha != 0){ ///// AND HERE return elem; } } return -1; } I was thinking something like getElement(x, y, "title"); //(This can be right) OR (both of these can be right.) if( (pixelAlpha == 0) || (class="tile" && onlycountclassifIsaidsovar) ){} By the way I'm at http://untitled.servegame.com if you want to see this puppy in action. Thanks!
The only difference I see are three extra tests in getTile(). You could use a boolean third arg (eg, 'tile') which if set would apply those tests.