I am having an issue redrawing some circles onto my html canvas - javascript

I am using a basic html canvas to create a simple game using javascript, but I am having an issue while trying to redraw my window. I have created a redraw_window function that I call whenever i want a new frame to show on the screen, but when I try to call the redraw_window function from this location on line 151, it does not work. The screen does not update. Any ideas why?
const c = canvas.getContext("2d")
canvas.width = innerWidth
canvas.height = innerHeight
const random = (min, max) => Math.floor(Math.random() * (max - min)) + min;
class Light {
constructor(pos,vel){
this.pos = pos
this.vel = vel
}
move(){
if (this.pos == 0){
this.vel = 1
}
if(this.pos == 9){
this.vel = -1
}
this.pos += this.vel
}
}
function is_not_in(val,list){
for (var i = 0; i < list.length; i++){
if (list[i] == val){
return false
}
}
return true
}
function clear_window(){
c.clearRect(0,0,canvas.width,canvas.height)
}
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
function redraw_window(pos){
c.font = "80px serif"
//let board = [1,1,1,1,1,1,1,1,1,1]
positions = [canvas.width/2-450,canvas.width/2-350,canvas.width/2-250,canvas.width/2-150,canvas.width/2-50,canvas.width/2+50,canvas.width/2+150,canvas.width/2+250,canvas.width/2+350,canvas.width/2+450,]
//board[light.pos] = 0
clear_window()
c.fillStyle = "black"
//c.fillText(board.toString(),canvas.width/2-c.measureText(board.toString()).width/2,canvas.height/2)
//c.fillText("Troy likes big hairy black balls",canvas.width/2-c.measureText("Troy likes big hairy black balls").width/2,canvas.height/2+80)
for (var i = 0; i < positions.length; i++) {
let radius = 20
if (pos != i){
c.beginPath()
c.arc(positions[i],canvas.height/2,radius,0,Math.PI * 2,false)
c.fillStyle = "black"
c.fill()
}
if (pos == i){
c.beginPath()
c.arc(positions[i],canvas.height/2,radius,0,Math.PI * 2,false)
c.fillStyle = "green"
c.fill()
}
}
}
function choose(choices) {
var index = Math.floor(Math.random() * choices.length);
return choices[index];
}
function win_animation(){
redraw_window(null)
sleep(300)
redraw_window(light.pos)
sleep(300)
redraw_window(null)
sleep(300)
redraw_window(light.pos)
}
var light = new Light(random(0,9),choose([-1,1]))
function main(){
var FPS = 60
var SPEED = 0.5
var COOLDOWN = FPS * 0.25
var SCORE_TO_WIN = 5
var right_player_points = 0
var left_player_points = 0
var frame = 0
var right_player_cooldown = 0
var left_player_cooldown = 0
var keys_pressed = []
var right_player_key = "ShiftRight"
var left_player_key = "ShiftLeft"
var right_player_cooldown = 0
var left_player_cooldown = 0
var playing = true
//handles cooldown and keys pressed list
window.addEventListener("keydown",(event)=>{
if (event.code == right_player_key && is_not_in(right_player_key,keys_pressed)){
if (right_player_cooldown == 0){
right_player_cooldown = COOLDOWN
keys_pressed.push(event.code)
}
}
if (event.code == left_player_key && is_not_in(left_player_key,keys_pressed)){
if (left_player_cooldown == 0){
left_player_cooldown = COOLDOWN
keys_pressed.push(event.code)
}
}
})
//runs fps times per second
var run = setInterval(run,1000 / FPS)
function run(){
frame += 1
if (left_player_cooldown > 0){
left_player_cooldown -= 1
}
if (right_player_cooldown > 0){
right_player_cooldown -= 1
}
//runs SPEED times per second
if (frame >= FPS * SPEED){
frame = 0
if (!is_not_in(right_player_key,keys_pressed) && light.pos == 9){
right_player_points += 1
**VVV THIS IS WHERE I AM HAVING MY ISSUE VVV**
redraw_window(null);
alert("right player:" + right_player_points.toString())
if (right_player_points >= SCORE_TO_WIN){
alert("right player Wins")
right_player_points = 0
left_player_points = 0
light = new Light(random(0,9),choose([-1,1]))
}
}
if (!is_not_in(left_player_key,keys_pressed) && light.pos == 0){
left_player_points += 1
light = new Light(random(0,9),choose([-1,1]))
alert("left player:" + left_player_points.toString())
if (left_player_points >= SCORE_TO_WIN){
alert("left player wins")
left_player_points = 0
right_player_points = 0
}
}
light.move()
redraw_window(light.pos)
keys_pressed = []
}
}
}
main() ```

The main issue I can see here is that you have a variable and a function both named run. The setInterval call is being overwritten with the function definition. I recommend you rename the variable run to runInterval.
Change
var run = setInterval(run, 1000 / FPS)
to
var runInterval = setInterval(run, 1000 / FPS)
See if that helps.

Related

Make movement more smooth on Java Script Game | JavaScript Canvas

I am working on very simple java script game. there's a falling random object (trash) and another object for catch the falling object (trash bin). everything seems fine but i wanted to make the movement of the trash bin more smooth. Do you have any idea to fix this? Thanks in Advance
this is my code
window.onload = function(){
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var canvasBack = document.getElementById("backgroundCanvas");
var contextBack = canvasBack.getContext("2d");
var timer;
//mengatur hiscore
var hiscore = 0;
//Background image, musik and arrays musik.
var background = new Image();
background.src = 'assets/bgw2.jpg';
var catchSounds = [];
var catchSoundCounter = 0;
for(var i = 0; i < 5; i++)
{
var catchSound = new Audio();
catchSound.src = 'Audio/bleep.wav';
catchSounds.push(catchSound);
}
var music = new Audio();
music.src = 'Audio/MarimbaBoy.wav';
music.loop = true;
var smashSounds = [];
var smashCounter = 0;
for(var i = 0; i < 5; i++)
{
var smash = new Audio();
smash.src = 'Audio/smash.mp3';
smashSounds.push(smash);
}
var player;
var fruits = [];
var numberOfFruits = 15;
//Player constructor
function Player()
{
this.gameOver = false;
this.score = 0;
this.fruitsCollected = 0;
this.fruitsMissed = 0;
this.playerWidth = 150;
this.playerHeight = 90;
this.playerSpeed = 10;
this.x = canvas.width / 2;
this.y = canvas.height - this.playerHeight;
this.playerImage = new Image();
this.playerImage.src = 'assets/binn.png';
//Draws the player
this.render = function()
{
context.drawImage(this.playerImage, this.x, this.y);
}
//Moves the player left
this.moveLeft = function()
{
if(this.x > 0)
{
this.x -= this.playerSpeed;
}
}
//Moves the player right
this.moveRight = function()
{
if(this.x < canvas.width - this.playerWidth)
{
this.x += this.playerSpeed;
}
}
}
//Fruit constructor
function Fruit()
{
this.fruitNumber = Math.floor(Math.random() * 5);
this.fruitType = "";
this.fruitScore = 0;
this.fruitWidth = 50;
this.fruitHeight = 50;
this.fruitImage = new Image();
this.fruitSpeed = Math.floor(Math.random() * 2 + 1);
this.x = Math.random() * (canvas.width - this.fruitWidth);
this.y = Math.random() * -canvas.height - this.fruitHeight;
//Creates a different kind of fruit depending on the fruit number
//which is generated randomly
this.chooseFruit = function()
{
if(this.fruitNumber == 0)
{
this.fruitType = "pisang";
this.fruitScore = 5 * this.fruitSpeed;
this.fruitImage.src = 'assets/pisang.png';
}
else if(this.fruitNumber == 1)
{
this.fruitType = "rubbish";
this.fruitScore = 10 * this.fruitSpeed;
this.fruitImage.src = 'assets/rubbish.png';
}
else if(this.fruitNumber == 2)
{
this.fruitType = "botol";
this.fruitScore = 15 * this.fruitSpeed;
this.fruitImage.src = 'assets/botol.png';
}
else if(this.fruitNumber == 3)
{
this.fruitType = "coke";
this.fruitScore = 20 * this.fruitSpeed;
this.fruitImage.src = 'assets/coke.png';
}
else if(this.fruitNumber == 4)
{
this.fruitType = "apple";
this.fruitScore = 25 * this.fruitSpeed;
this.fruitImage.src = 'assets/apple.png';
}
else if(this.fruitNumber == 5)
{
this.fruitType = "papikra";
this.fruitScore = 30 * this.fruitSpeed;
this.fruitImage.src = 'assets/papikra.png';
}
}
//Makes the fruit descend.
//While falling checks if the fruit has been caught by the player
//Or if it hit the floor.
this.fall = function()
{
if(this.y < canvas.height - this.fruitHeight)
{
this.y += this.fruitSpeed;
}
else
{
smashSounds[smashCounter].play();
if(smashCounter == 4)
{
smashCounter = 0;
}
else
{
smashCounter++;
}
player.fruitsMissed += 1;
this.changeState();
this.chooseFruit();
}
this.checkIfCaught();
}
//Checks if the fruit has been caught by the player
//If it is caught, the player score and fruit counter is increased, and
//the current fruit changes its state and becomes a different fruit.
this.checkIfCaught = function()
{
if(this.y >= player.y)
{
if((this.x > player.x && this.x < (player.x + player.playerWidth)) ||
(this.x + this.fruitWidth > player.x && this.x + this.fruitWidth < (player.x + player.playerWidth)))
{
catchSounds[catchSoundCounter].play();
if(catchSoundCounter == 4)
{
catchSoundCounter = 0;
}
else
{
catchSoundCounter++;
}
player.score += this.fruitScore;
player.fruitsCollected += 1;
this.changeState();
this.chooseFruit();
}
}
}
//Randomly updates the fruit speed, fruit number, which defines the type of fruit
//And also changes its x and y position on the canvas.
this.changeState = function()
{
this.fruitNumber = Math.floor(Math.random() * 5);
this.fruitSpeed = Math.floor(Math.random() * 2 + 1);
this.x = Math.random() * (canvas.width - this.fruitWidth);
this.y = Math.random() * -canvas.height - this.fruitHeight;
}
//Draws the fruit.
this.render = function()
{
context.drawImage(this.fruitImage, this.x, this.y);
}
}
//Adds controls. Left arrow to move left, right arrow to move right.
//ENTER to restart only works at the game over screen.
window.addEventListener("keydown", function(e){
e.preventDefault();
if(e.keyCode == 37)
{
player.moveLeft();
}
else if(e.keyCode == 39)
{
player.moveRight();
}
else if(e.keyCode == 13 && player.gameOver == true)
{
main();
window.clearTimeout(timer);
}
});
main();
//Fills an array of fruits, creates a player and starts the game
function main()
{
contextBack.font = "bold 20px Dominique";
contextBack.fillStyle = "WHITE";
player = new Player();
fruits = [];
for(var i = 0; i < numberOfFruits; i++)
{
var fruit = new Fruit();
fruit.chooseFruit();
fruits.push(fruit);
}
startGame();
}
function startGame()
{
updateGame();
window.requestAnimationFrame(drawGame);
}
//Checks for gameOver and makes each fruit in the array fall down.
function updateGame()
{
music.play();
if(player.fruitsMissed >= 10)
{
player.gameOver = true;
}
for(var j = 0; j < fruits.length; j++)
{
fruits[j].fall();
}
timer = window.setTimeout(updateGame, 30);
}
//Draws the player and fruits on the screen as well as info in the HUD.
function drawGame()
{
if(player.gameOver == false)
{
context.clearRect(0, 0, canvas.width, canvas.height);
contextBack.clearRect(0, 0, canvasBack.width, canvasBack.height);
contextBack.drawImage(background, 0, 0);
player.render();
for(var j = 0; j < fruits.length; j++)
{
fruits[j].render();
}
contextBack.fillText("SCORE: " + player.score, 30, 50);
contextBack.fillText("HIGHEST SCORE: " + hiscore, 140, 50);
contextBack.fillText("FRUIT CAUGHT: " + player.fruitsCollected, 320, 50);
contextBack.fillText("FRUIT MISSED: " + player.fruitsMissed, 490, 50);
}
else
{
//Different screen for game over.
for(var i = 0; i < numberOfFruits; i++)
{
console.log("Speed was" + fruits[fruits.length - 1].fruitSpeed);
fruits.pop();
}
if(hiscore < player.score)
{
hiscore = player.score;
contextBack.fillText("NEW HI SCORE: " + hiscore, (canvas.width / 2) - 80, canvas.height / 2);
}
contextBack.fillText("PRESS ENTER TO RESTART", (canvas.width / 2) - 100, canvas.height / 2 + 50);
context.clearRect(0, 0, canvas.width, canvas.height);
}
window.requestAnimationFrame(drawGame);
}
}
First of all, just use one requestAnimationFrame and no other setTimeout code. These can start to run out of sync with each other and it's hard to pause them all when the player wants to pause the game.
Instead, use a counter instead of timeout.
let fruitCounter = 0
function drawGame() {
// all your draw code here
...
// every 60 frames drop a new fruit
fruitCounter++
if(fruitCounter > 60){
fruitCounter = 0
dropNewFruit()
}
// request the new frame unless its game over
if(!gameOver) {
requestAnimationFrame(drawGame)
}
}
Also, if you use keyboard events like this, it will always be choppy because there is a delay when typing on the keyboard (just press a letter for a long time in a text field, you will see that it takes a while until more letters appear)
You can fix this by setting a variable once a key gets pressed.
window.addEventListener("keydown", function(e){
if(e.keyCode == 37){
moveLeft = 1
}
}
window.addEventListener("keyup", function(e){
if(e.keyCode == 37){
moveLeft = 0
}
}
Then, you can use that variable in your animation code
function drawGame() {
// all your draw code here
player.x += moveLeft
// request the new frame unless its game over
if(!gameOver) {
requestAnimationFrame(drawGame)
}
}
These are just a few tips! This has always worked for me when I build a javascript game.

Calculate the score for Memory Game based on number of turns , time and matches

I'm developing a memory game and i need to calculate the fair score for the game, based on:
number of tries,
time and
number of matches
So, i tried using a function to calculate the score, but when i tried to display the score in the winning screen, the score do not appear. Help me out with this
the variables are
var matches = 0;
var moves = 0;
var counter = document.querySelector(".moves");
To check for the matches:
for (i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function (e) {
var turnable = e.target.dataset.turnable;
//first click
if (!wait && lastKnownButtonId == undefined && lastKnownButtonNumber == undefined && turnable == 'true') {
e.target.dataset.turnable = 'false';
e.target.innerHTML = getgImage(event.target.dataset.number);
e.target.style.backgroundColor = 'yellow';
lastKnownButtonId = e.target.id;
lastKnownButtonNumber = e.target.dataset.number;
}
//second click
else if (!wait && lastKnownButtonId != undefined && lastKnownButtonNumber != undefined && turnable == 'true' && e.target.id != lastKnownButtonId) {
e.target.dataset.turnable = 'false';
e.target.innerHTML = getgImage(event.target.dataset.number);
//match
if (e.target.dataset.number == lastKnownButtonNumber) {
e.target.style.backgroundColor = '#00FF7F';
document.getElementById(lastKnownButtonId).style.backgroundColor = '#00FF7F';
lastKnownButtonId = undefined;
lastKnownButtonNumber = undefined;
matches++;
if (matches == 8) {
document.getElementById("finalMove").innerHTML = moves;
showWinScreen();
//clearTimeout(timeoutHandle);
}
}
//no match
else {
document.getElementById(lastKnownButtonId).style.backgroundColor = 'red';
e.target.style.backgroundColor = 'red';
wait = true;
setTimeout(() => {
e.target.dataset.turnable = 'true';
e.target.style.backgroundColor = 'white'
e.target.innerHTML = getgImage(0);
var tempLastClickedButton = document.getElementById(lastKnownButtonId);
tempLastClickedButton.dataset.turnable = 'true';
tempLastClickedButton.style.backgroundColor = 'white';
tempLastClickedButton.innerHTML = getgImage(0);
lastKnownButtonId = undefined;
lastKnownButtonNumber = undefined;
wait = false;
}, 1000);
}
moveCounter();
}
});
}
i have inserted a function to calculate the score
function calcScore(){
var tilesbonus = (16 - matches) * 20; // 20 points for each successful tile
var timebonus = (60 - finaltime) * 8; // 8 points for each second
var triesbonus = (48 - moves) * 10; // (deduct) 10 points for each try
if (tilesbonus <0) { tilesbonus = 0; }
if (timebonus <0) { timebonus = 0; }
if (triesbonus <0) { triesbonus = 0; }
var totalscore= tilesbonus + timebonus + triesbonus;
return totalscore;
}
The function for timer:
window.onload = function() {
var timeoutHandle;
function countdown(minutes, seconds) {
function tick() {
var timecounter = document.getElementById("timer");
timecounter.innerHTML = minutes.toString() + ":" + (seconds < 10 ? "0" : "") + String(seconds);
seconds--;
if (seconds >= 0) {
timeoutHandle = setTimeout(tick, 1000);
} else {
if (minutes >= 1) {
setTimeout(function () {
countdown(minutes - 1, 59);
}, 1000);
}
}
if (seconds==0 && minutes ==0){
alert("Game over");
//reset();
}
if (matches==8){
var totalscore = calcScore();
clearTimeout(timeoutHandle);
var finaltime= timecounter.innerHTML;
document.getElementById("seconds").innerHTML= finaltime;
document.getElementById("score").innerHTML=totalscore;
}
}
tick();
}
countdown(1, 00); }
the Move counter:
function moveCounter(){
moves++;
counter.innerHTML = moves;
}
the calscore() function is called when the game ends
if (matches==8){
calcScore();
clearTimeout(timeoutHandle);
var finaltime= timecounter.innerHTML;
document.getElementById("seconds").innerHTML= finaltime;
document.getElementById("score").innerHTML=totalscore;
document.getElementById("finalMove").innerHTML = moves;
}
The html code where the score should appear is :
<p><font size= "5">Your score: <span id=score> </span></font></p>
It's because you try to use a variables defined in a fonction from the global scope
here's an explanation of javascript scopes
basically if you declare a variable inside a fonction you can't use it outside of it
corrected and commented your code about this specific problem
var matches = 8
var finaltime = 42
var moves = 13
function calcScore(){
var tilesbonus = (16 - matches) * 20; // 20 points for each successful tile
var timebonus = (60 - finaltime) * 8; // 8 points for each second
var triesbonus = (48 - moves) * 10; // (deduct) 10 points for each try
if (tilesbonus <0) { tilesbonus = 0; }
if (timebonus <0) { timebonus = 0; }
if (triesbonus <0) { triesbonus = 0; }
var totalscore= tilesbonus + timebonus + triesbonus; // you defined the variable here
return totalscore;
} // totalscore is destroyed after the end of the function
if (matches==8){
var totalscore = calcScore(); // I modified this line and now it works
// I declare a new variable which contains the value returned by calcScore
clearTimeout(null); // i don't have the timeout var so I can't clear it
var finaltime= timecounter.innerHTML;
document.getElementById("seconds").innerHTML= finaltime;
document.getElementById("score").innerHTML=totalscore; // finally use the variable
}
<p><font size= "5">Your score: <span id=score> </span></font></p>
<p><font size= "5">finalTime: <span id=seconds> </span></font></p>
<span id=timecounter>42</span>

Control ball speed in pong

I have this code below, and I'm having a hard time solving this one.
On dotime function, i have the ball speed:
/* HERE */
function dotime() {
move1();
if (myform != null) {
myform.text3.value = display1();
myform.score.value = "" + score;
}
/* ---Ball Speed--- */
if (!oops_flag) timerID = setTimeout("dotime()", 190);
/* ---trying to make ball speed faster--- */
if (score == 1) {
timerID = setTimeout("dotime()", 100 - 30);
}
timerRunning = true;
}
I tried to make the ball move faster but when i do the second "if", the ball just flying too fast.
Thanks in advance,
fufle.
full code:
var crlf = "\r\n";
var x = 0;
var y = 0;
var dx = 1;
var dy = 1;
var s = "";
var u = 0;
var oops_flag = false;
var score = 0;
function move1() {
x += dx;
if (x > 61) {
x -= 2 * Math.abs(dx);
if (dx > 0) dx = -dx;
}
if (x < 0) {
x += 2 * Math.abs(dx);
if (dx < 0) dx = -dx;
}
y += dy;
if (y > 24) {
y -= 2 * Math.abs(dy);
if (dy > 0) dy = -dy;
if (Math.abs(x - 2 * u - 1) > 2) {
oops_flag = true;
} else {
score += 1;
}
}
if (y < 0) {
y += 2 * Math.abs(dy);
if (dy < 0) dy = -dy;
}
}
function display1() {
var s1 = ""
var i, j;
if (oops_flag) return " Unlucky, Play again?"
for (j = 0; j < 25; j++) {
for (i = 0; i < 62; i++) {
/* BALL */
if (j == y && i == x) s1 += "🔴";
else s1 += " ";
}
s1 += crlf;
}
/* DEFENDER */
var s2 = "";
for (i = 0; i < 31; i++) {
if (u == i) s2 += "â–„â–„â–„â–„â–„";
else s2 += " ";
}
return (s1 + s2);
}
var timerID = null;
var timerRunning = false;
var myform;
function stopclock() {
if (timerRunning) clearTimeout(timerID);
timerRunning = false;
}
function startclock(form) {
myform = form;
oops_flag = false;
score = 0;
if (navigator.userAgent.indexOf("Mac") > 2) crlf = "\n";
stopclock();
dotime();
// var id= setInterval(frameElement,10000);
}
/* HERE */
function dotime() {
move1();
if (myform != null) {
myform.text3.value = display1();
myform.score.value = "" + score;
}
if (!oops_flag) timerID = setTimeout("dotime()", 100);
if (score == 1) {
timerID = setTimeout("dotime()", 100 - 30);
}
timerRunning = true;
}
Looks like you have two timers running so you need to make it so only one will run.
if (!oops_flag) {
var speed = 100;
if (score===1) speed = 70;
timerID = setTimeout(dotime, speed);
}
or with a ternary operator
if (!oops_flag) {
var speed = (score===1) ? 70 : 100;
timerID = setTimeout(dotime, speed);
}

Drawing with sugar

I want to create a drawing in sugar thing, much the same as you would throw some sugar on a table and using your fingers to "erase" the sugar particles to form an image.
Does anyone know of a js type tool I can use to make this happen?
I suppose I can take a photo of a desk, and a photo of desk with some sugar on it, and then just erase the top layer, but I'm worried that this won't give a real effect.
I'm currently thinking of having a photo of desk, and then using JS to generate a lot of "sugary" particles, which I can then erase. This sounds incredibly hard to do though. Is it? Can someone point me in a good direction?
Sand or is it sugar?
An interesting problem that I had to give a little time.
This works by creating several buffers to hold grains of sand (sugar) and give them life when they need to move.
There is no way that Javascript could do a whole screen of a million plus grains so this demo cheats by only updating a very few and prioritising for new movement rather than allow older moving grains to hog CPU time.
The arrays active, sandStatus, holds the sand gains. active has the pixel address as a 32Bit int and sandStatus has age. The Array sand holds the amount of sand at each pixel and is used to calculate the shadow effect (shadow could be much better using a webGL shader) and to work out which direction sand should slide if disturbed or dropped to the surface.
the var activeMax holds the max number of active sand grains. Increase for a better effect, decrease if the sim runs to slow.
To drop sand use the right mouse button. Hold at one spot to make a pile. Left button pushes the sand about. When you hit a bigger pile the machine may lag (depending on CPU power and browser (best in firefox)).
The push function checks the sand array for any sand. If found it pushes the sand away from the center and piles it up around the edge. Some sand will fall back.
The function sprinkle adds grains of sand (one are a time by pixel coordinate or by index). The function push does the sand drawing FX. update moves the sand grains by checking surrounding pixels heights and moving grains down hill. renderPix handles rendering grains, creating the shadows and deactivating sand grains. The Array shadowChange holds the index of pixels that have had changes so that the shadows can be updated.
Bottom half of the demo is just boilerplate for mouse and canvas setup. All the code in regard to the answer is in the first half.
"use strict";
var activeMax = 2280; // this is the number of sand grains that are processed at
// at time. Increase for better looking effect. decrease
// if the machine is not keeping up with the load
var cw;
var ch;
var w; //
var h;
var canvasBuf = document.createElement("canvas");
var ctxB
var globalTime; // global to this
var pixels
var sand;
var sandToFall;
var sandToFallCount = 36000;
var shadow; // shadow pixels
var activeMax = 2280;
var active; // index of pixel for active grain
var sandStatus; // status of active grain
var shadowChange; // holds index of pixels that have a shadow change
var pixels;
var buf;
var grain = 0xFFFFFFFF;
var shadowGrain = 0x00000000;
var ready = false;
var sandReady = 0;
var nextActive = 0;
var nextActiveShadow = 0;
var onResize = function(){
cw = canvas.width;
ch = canvas.height;
w = cw; //
h = ch;
pixels = w*h;
canvasBuf.width = w;
canvasBuf.height = h;
ctxB = canvasBuf.getContext("2d");
sand = new Uint8ClampedArray(pixels);
shadow = new Uint8ClampedArray(pixels); // shadow pixels
sandToFall = new Uint32Array(sandToFallCount);
activeMax = 2280;
active = new Uint32Array(activeMax); // index of pixel for active grain
sandStatus = new Uint16Array(activeMax); // status of active grain
shadowChange= new Uint32Array(activeMax); // holds index of pixels that have a shadow change
sandStatus.fill(0); // clear
active.fill(0);
shadowChange.fill(0);
ctxB.clearRect(0,0,w,h);
ctxB.fillStyle = "white";
ctxB.font = "84px arial";
ctxB.textAlign = "center";
ctxB.globalAlpha = 0.01;
for(var i = 0; i < 12; i ++){
ctxB.fillText("Sand Doodler!",w/2 + (Math.random()-0.5)*5,h/2 + (Math.random()-0.5)*5);
}
ctxB.globalAlpha = 1;
pixels = ctxB.getImageData(0,0,w,h);
buf = new Uint32Array(pixels.data.buffer);
for(i = 0; i < buf.length; i += 3){
if(buf[i] !== 0){
var c = buf[i] >>> 24;
buf[i] = 0;
while(c > 0){
var ind = Math.floor(Math.random()*sandToFallCount);
if(sandToFall[ind] === 0){
sandToFall[ind] = i;
}
c = c >>> 1;
}
}
}
buf.fill(0);
offsets = [1,w-1,w,w+1,-w-1,-w,-w+1,-1];
shadowOffsets = [-w-1,-w,-1];
ready = true;
sandReady=0;
}
function sprinkle(x,y){
var ind;
if(y === undefined){
ind = x;
}else{
ind = x + y*w;
}
var alreadyExists = active.indexOf(ind);
var ac = nextActive;
if(alreadyExists > -1){
sand[ind] += 1;
shadow[ind] = 0;
sandStatus[alreadyExists] = 66;
}else{
active[nextActive] = ind;
sandStatus[nextActive] = 66;
shadowChange[nextActiveShadow];
nextActiveShadow = (nextActiveShadow+1)%activeMax;
nextActive = (nextActive +1)%activeMax;
sand[ind] += 1;
shadow[ind] = 0;
}
return ac;
}
var offsets = [1,w-1,w,w+1,-w-1,-w,-w+1,-1];
var offsetCount = 8;
function update(){
var min,max,minDir,maxDir,dir,start,jj,j,ind,level,i,l1;
for( i = 0; i <activeMax; i ++){
if(sandStatus[i] !== 0){
ind = active[i];
level = sand[ind];
if(level === 1){
sandStatus[i] = 1; // deactive is cant move (level ground)
}else{
min = level;
var d;
minDir = offsets[Math.floor(Math.random()*16)];
dir = null;
start = Math.floor(Math.random()*16); // start at a random direction
for(j=0;j < offsetCount; j++){
jj = offsets[(j + start)%offsetCount];
l1 = sand[ind+jj];
if(l1 < min){
min = l1;
minDir = jj;
d = (j + start)%offsetCount;
}
}
dir = null;
if(min >= level - 1){ // nowhere to move
sandStatus[i] = 1;
}else
if(min < level-1){ // move to lowest
dir = minDir
}
if(dir !== null){
var lv = level-min;
while(lv > 2){
active[i] = ind + dir;
if(sand[ind] > 1){
sand[ind] -= 2;
sprinkle(ind)
}else{
sand[ind] -=1;
}
ind = ind+dir;
sand[active[i]] += 1;
if(sand[active[i] + offsets[d]] >=level){
d+= Math.random()<0.5? 1 : offsetCount -1;
d %=offsetCount;
}
lv -= 1;
}
if(sand[ind]>0){
active[i] = ind + dir;
sand[ind] -= 1;
}
sand[active[i]] += 1;
}
}
}
}
}
var shadowOffsets = [-w-1,-w,-1];
var shadowCols = [0xFFf0f0f0,0xFFd0d0d0,0xFFb0b0b0,0xFF909090];
var shadowDist = [0xf0000000,0xd0000000,0xb0000000,0x90000000]; // shadow col no sand
// renders grains and adds shadows. Deactivates gains when they are done
function renderPix(){
var ac = 0;
for(var i = 0; i < activeMax; i ++){
if(sandStatus[i] !== 0){
ac += 1;
var ind = active[i];
buf[ind] = grain;
}
}
for(var i = 0; i < activeMax; i ++){
if(sandStatus[i] !== 0){
var ind = active[i];
var level = sand[ind];
var col =0;
if(sand[ind + shadowOffsets[0]] > level ){
col = 2;
}else
if(sand[ind + shadowOffsets[1]] > level ){
col =1;
}else
if(sand[ind + shadowOffsets[2]] > level ){
col = 1;
}
buf[ind] = grain; // add a sand grain to the image
shadow[ind] = col;
shadowChange[nextActiveShadow] = ind;
nextActiveShadow = (nextActiveShadow + 1)%activeMax;
var c = 4;
while(c > 0){
c-=1;
ind += w + 1;
var s = sand[ind];
var dif = level - s;
if(dif > 0){
c-= dif;
}
shadow[ind] += 1;
shadowChange[nextActiveShadow] = ind;
nextActiveShadow = (nextActiveShadow + 1)%activeMax;
}
sandStatus[i] -= 1;
if(sandStatus[i] === 1){
sandStatus[i] = 0;
active[i] = 0;
}
}
}
// add calculated shadows
for(var i = 0; i < activeMax; i ++){
if(shadowChange[i] !== 0){
var ind = shadowChange[i];
var s = shadow[ind] <4 ? shadow[ind]-1:3;
if(sand[ind] > 0){
buf[ind]=shadowCols[s];
}else{
buf[ind]=shadowDist[s];
}
shadowChange[i] = 0;
}
}
}
// push sand about
function push(x,y,radius){
var iyy,iny
var rr = radius * radius ;
x = Math.floor(x);
y = Math.floor(y);
for(var iy = -radius + 1; iy < radius; iy ++){
iyy = iy * iy;
iny = (y+iy) * w;
for(var ix = -radius + 1; ix < radius; ix ++){
if(ix*ix + iyy <= rr){ // is inside radius
var ind = (x + ix) + iny;
if(sand[ind] > 0){
var dir = Math.random() * Math.PI * 2;
dir = Math.atan2(iy,ix)
var r = radius + Math.random() * radius *0.2
var xx = Math.cos(dir) * r;
var yy = Math.sin(dir) * r;
buf[ind] = 0x000000;
sand[ind] = 0;
ind = Math.floor(xx + x) + Math.floor(yy + y) * w;
sprinkle(ind);
}else{
buf[ind] = 0;
}
}
}
}
}
function showHeight(){ // for debugging only
for(var i = 0; i < sand.length; i ++){
buf[i] = 0xff000000;
var k = sand[i];
buf[i] +=(k <<16) + (k<<8) + (k);
}
}
// main update function
function display(){
if(!ready){ // only when ready
return;
}
//ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.clearRect(0,0,cw,ch);
var mx = Math.floor((mouse.x/cw)*w); // canvas buf mouse pos
var my = Math.floor((mouse.y/ch)*h);
// drop sand
if(mouse.buttonRaw & 4){
for(var i = 0; i < 120; i ++){
var dir = Math.random()*Math.PI;
var dist = ((Math.random()+Math.random()+Math.random())/3-0.5) * 62;
var x = Math.cos(dir) * dist;
var y = Math.sin(dir) * dist;
x += mx;
y += my;
x = Math.floor(x); // floor
y = Math.floor(y); // floor
sprinkle(x,y);
}
}else{
// drop sand for intro FX
if(sandReady <sandToFallCount){
for(var i = 0; i < 120; i ++){
if(sandToFall[sandReady] !== 0){
sprinkle(sandToFall[sandReady] + offsets[Math.floor(Math.random()*8)%offsets.length]);
}
sandReady += 1;
}
}
}
// push sand about.
if(mouse.buttonRaw & 1){
push(((mouse.x/cw)*w),((mouse.y/ch)*h),32); // scale mouse to canvasBuf size
}
update();
renderPix();
//showHeight();
ctxB.putImageData(pixels,0,0);
ctx.drawImage(canvasBuf,0,0,cw,ch);
}
//==================================================================================================
// The following code is support code that provides me with a standard interface to various forums.
// It provides a mouse interface, a full screen canvas, and some global often used variable
// like canvas, ctx, mouse, w, h (width and height), globalTime
// This code is not intended to be part of the answer unless specified and has been formated to reduce
// display size. It should not be used as an example of how to write a canvas interface.
// By Blindman67
const U = undefined;const RESIZE_DEBOUNCE_TIME = 100;
var canvas,ctx,mouse,createCanvas,resizeCanvas,setGlobals,globalTime=0,resizeCount = 0;
createCanvas = function () { var c,cs; cs = (c = document.createElement("canvas")).style; cs.position = "absolute"; cs.top = cs.left = "0px"; cs.zIndex = 1000; document.body.appendChild(c); return c;}
resizeCanvas = function () {
if (canvas === U) { canvas = createCanvas(); } canvas.width = window.innerWidth; canvas.height = window.innerHeight; ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") { setGlobals(); } if (typeof onResize === "function"){ resizeCount += 1; setTimeout(debounceResize,RESIZE_DEBOUNCE_TIME);}
}
function debounceResize(){ resizeCount -= 1; if(resizeCount <= 0){ onResize();}}
setGlobals = function(){ cw = w = canvas.width; ch = h = canvas.height; mouse.updateBounds(); }
mouse = (function(){
function preventDefault(e) { e.preventDefault(); }
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false, buttonRaw : 0, over : false, bm : [1, 2, 4, 6, 5, 3],
active : false,bounds : null, crashRecover : null, mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
var m = mouse;
function mouseMove(e) {
var t = e.type;
m.x = e.clientX - m.bounds.left; m.y = e.clientY - m.bounds.top;
m.alt = e.altKey; m.shift = e.shiftKey; m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1]; }
else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2]; }
else if (t === "mouseout") { m.buttonRaw = 0; m.over = false; }
else if (t === "mouseover") { m.over = true; }
else if (t === "mousewheel") { m.w = e.wheelDelta; }
else if (t === "DOMMouseScroll") { m.w = -e.detail; }
if (m.callbacks) { m.callbacks.forEach(c => c(e)); }
if((m.buttonRaw & 2) && m.crashRecover !== null){ if(typeof m.crashRecover === "function"){ setTimeout(m.crashRecover,0);}}
e.preventDefault();
}
m.updateBounds = function(){
if(m.active){
m.bounds = m.element.getBoundingClientRect();
}
}
m.addCallback = function (callback) {
if (typeof callback === "function") {
if (m.callbacks === U) { m.callbacks = [callback]; }
else { m.callbacks.push(callback); }
} else { throw new TypeError("mouse.addCallback argument must be a function"); }
}
m.start = function (element, blockContextMenu) {
if (m.element !== U) { m.removeMouse(); }
m.element = element === U ? document : element;
m.blockContextMenu = blockContextMenu === U ? false : blockContextMenu;
m.mouseEvents.forEach( n => { m.element.addEventListener(n, mouseMove); } );
if (m.blockContextMenu === true) { m.element.addEventListener("contextmenu", preventDefault, false); }
m.active = true;
m.updateBounds();
}
m.remove = function () {
if (m.element !== U) {
m.mouseEvents.forEach(n => { m.element.removeEventListener(n, mouseMove); } );
if (m.contextMenuBlocked === true) { m.element.removeEventListener("contextmenu", preventDefault);}
m.element = m.callbacks = m.contextMenuBlocked = U;
m.active = false;
}
}
return mouse;
})();
function main(timer){ // Main update loop
globalTime = timer;
display(); // call demo code
requestAnimationFrame(main);
}
resizeCanvas();
mouse.start(canvas,true);
window.addEventListener("resize",resizeCanvas);
requestAnimationFrame(main);
body {
background:#49D;
}
.help {
text-align : center;
font-family : Arial,"Helvetica Neue",Helvetica,sans-serif;
font-size : 18px;
}
<div class="help">Right mouse to drop sand, left button to push it around.</div>

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.

Categories

Resources