Wall detection on canvas JS - javascript

I made this script which moves a smaller div inside it's parent container, but I need it so that it doesn't go past the boundaries of the parent (represented by the 1px black line border). Could someone help me?
var guy = document.getElementById("guy");
var container = document.getElementById("container");
var guyLeft = 0;
var y = 0;
function anim(e) {
if (e.keyCode == 39) {
guyLeft += 2;
guy.style.left = guyLeft + "px";
}
else if (e.keyCode == 37) {
guyLeft -= 2;
guy.style.left = guyLeft + "px";
}
else if (e.keyCode == 40) {
y += 2;
guy.style.top = y + "px";
}
else if (e.keyCode == 38) {
y -= 2;
guy.style.top = y + "px";
}
}
document.onkeydown = anim;
#container {
height:300px;
width:300px;
outline: 2px solid black;
position:relative;
}
#guy {
position:absolute;
height:20px;
width:20px;
outline: 1px solid black;
background-color:red; left: 0;
}
<div id="container">
<div id="guy"></div>
</div>

You could use an IF statement to detect when the character is at the edge.
With your example your JavaScript would become:
var guy = document.getElementById("guy");
var container = document.getElementById("container");
var guyLeft = 0;
var y = 0;
function anim(e) {
if (e.keyCode == 39 && guyLeft <= 278) {
guyLeft += 2; guy.style.left = guyLeft + "px";
}
else if (e.keyCode == 37 && guyLeft >= 2) {
guyLeft -= 2; guy.style.left = guyLeft + "px";
}
else if (e.keyCode == 40 && y <= 278) {
y += 2; guy.style.top = y + "px";
}
else if (e.keyCode == 38 && y >= 2) {
y -= 2; guy.style.top = y + "px";
}
} document.onkeydown = anim;
Here's a working example:
var guy = document.getElementById("guy");
var container = document.getElementById("container");
var guyLeft = 0;
var y = 0;
function anim(e) {
if (e.keyCode == 39 && guyLeft <= 278) {
guyLeft += 2; guy.style.left = guyLeft + "px";
}
else if (e.keyCode == 37 && guyLeft >= 2) {
guyLeft -= 2; guy.style.left = guyLeft + "px";
}
else if (e.keyCode == 40 && y <= 278) {
y += 2; guy.style.top = y + "px";
}
else if (e.keyCode == 38 && y >= 2) {
y -= 2; guy.style.top = y + "px";
}
} document.onkeydown = anim;
#container {
height:300px;
width:300px;
outline: 2px solid black;
position:relative; }
#guy {
position:absolute;
height:20px;
width:20px;
outline: 1px solid black;
background-color:red; left: 0;
}
<div id="container"> <div id="guy"></div></div>

Related

How do I make css offset smoother with Javascript?

I have a Player controller using the position attributes and javascript but it is kinda snappy. What do I do?
I tried making an infinite loop then adding a time out to slow it down to the client's computers frames but it just outputted the same result, snappy. Also, I wrote this in Atom and I compiled it on the Google Chrome Web Browser.
Javascript
var player = document.getElementById('Player');
var ctx = player.getContext("2d");
var playerXPos = 0;
var playerYPos = 0;
var playerSpeed = 5;
ctx.fillStyle = "Red";
ctx.fillRect(0,0,50,50);
function animate(key) {
if (key.keyCode == 39) { //Right Arrow
playerXPos += playerSpeed;
player.style.left = playerXPos + "px";
}
if (key.keyCode == 37) { //Left Arrow
playerXPos -= playerSpeed;
player.style.left = playerXPos + "px";
}
if (key.keyCode == 38) { //Up Arrow
playerYPos -= playerSpeed;
player.style.top = playerYPos + "px";
}
if (key.keyCode == 40) { //Down Arrow
playerYPos += playerSpeed;
player.style.top = playerYPos + "px";
}
else {
return false;
}
}
document.onkeydown = animate;
HTML part (lol html is not a programming language but whatever)
<!DOCTYPE html>
<html>
<head>
<title>Player Test</title>
<style>
#Player {
position: relative;
}
</style>
<body>
<canvas id="Player"></canvas>
<script src="App/playerController.js" type="text/javascript"></script>
</body>
</html>
I want it to be smooth. I think I should use linear algebra but I am not that advance in javascript. I am actually a beginner.
You can use the CSS transition property to get a smoother effect. You need to set the initial top and left positions of your element in your CSS since these are the properties that you are changing with your JS. I've set the animation to last 0.5s, but this can be adjusted to be faster or slower as you'd prefer:
var player = document.getElementById('Player');
var ctx = player.getContext("2d");
var playerXPos = 0;
var playerYPos = 0;
var playerSpeed = 5;
ctx.fillStyle = "Red";
ctx.fillRect(0, 0, 50, 50);
function animate(key) {
if (key.keyCode == 39) { //Right Arrow
playerXPos += playerSpeed;
player.style.left = playerXPos + "px";
}
if (key.keyCode == 37) { //Left Arrow
playerXPos -= playerSpeed;
player.style.left = playerXPos + "px";
}
if (key.keyCode == 38) { //Up Arrow
playerYPos -= playerSpeed;
player.style.top = playerYPos + "px";
}
if (key.keyCode == 40) { //Down Arrow
playerYPos += playerSpeed;
player.style.top = playerYPos + "px";
} else {
return false;
}
}
document.onkeydown = animate;
#Player {
position: relative;
left: 0;
top: 0;
-webkit-transition: all 0.5s;
/* For Safari 3.1 to 6.0 */
transition: all 0.5s;
}
<canvas id="Player"></canvas>
You could try this:
var player = document.getElementById('Player');
var ctx = player.getContext("2d");
var playerXPos = 0;
var playerYPos = 0;
var playerXSpeed = 0;
var playerYSpeed = 0;
ctx.fillStyle = "Red";
ctx.fillRect(0,0,50,50);
setInterval(function(){
playerXPos += playerXSpeed;
player.style.left = playerXPos + "px";
playerYPos += playerYSpeed;
player.style.top = playerYPos + "px";
},5)
function animate(event) {
let x = event.which || event.keyCode
switch (x) {
case 39://Right Arrow
playerXSpeed = 1
break
case 37://Left Arrow
playerXSpeed = -1
break
case 38://Up Arrow
playerYSpeed = -1;
break
case 40://Down Arrow
playerYSpeed = 1;
break
}
}
document.onkeydown = animate;
document.onkeyup = function(){
let x = event.which || event.keyCode
switch (x) {
case 39://Right Arrow
case 37://Left Arrow
playerXSpeed = 0
break
case 38://Up Arrow
case 40://Down Arrow
playerYSpeed = 0;
break
}
}

Uncaught TypeError: Cannot read property 'getContext' of null. In chrome app development

I am making an app for the Chrome Web Store. It is a clone of the Doodle Jump game. When I test and load it as an unpacked extension, this error keeps coming up.
Uncaught TypeError: Cannot read property 'getContext' of null
My code is here:
Javascript
function startGame() {
// RequestAnimFrame: a browser API for getting smooth animations
window.requestAnimFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var width = 422,
height = 552;
canvas.width = width;
canvas.height = height;
//Variables for game
var platforms = [],
image = document.getElementById("sprite"),
player, platformCount = 10,
position = 0,
gravity = 0.2,
animloop,
flag = 0,
menuloop, broken = 0,
dir, score = 0, firstRun = true;
//Base object
var Base = function() {
this.height = 5;
this.width = width;
//Sprite clipping
this.cx = 0;
this.cy = 614;
this.cwidth = 100;
this.cheight = 5;
this.moved = 0;
this.x = 0;
this.y = height - this.height;
this.draw = function() {
try {
ctx.drawImage(image, this.cx, this.cy, this.cwidth, this.cheight, this.x, this.y, this.width, this.height);
} catch (e) {}
};
};
var base = new Base();
//Player object
var Player = function() {
this.vy = 11;
this.vx = 0;
this.isMovingLeft = false;
this.isMovingRight = false;
this.isDead = false;
this.width = 55;
this.height = 40;
//Sprite clipping
this.cx = 0;
this.cy = 0;
this.cwidth = 110;
this.cheight = 80;
this.dir = "left";
this.x = width / 2 - this.width / 2;
this.y = height;
//Function to draw it
this.draw = function() {
try {
if (this.dir == "right") this.cy = 121;
else if (this.dir == "left") this.cy = 201;
else if (this.dir == "right_land") this.cy = 289;
else if (this.dir == "left_land") this.cy = 371;
ctx.drawImage(image, this.cx, this.cy, this.cwidth, this.cheight, this.x, this.y, this.width, this.height);
} catch (e) {}
};
this.jump = function() {
this.vy = -8;
document.getElementById('audio').innerHTML='<audio src="sounds/pup.mp3" preload="auto" autoplay autobuffer></audio>'
};
this.jumpHigh = function() {
this.vy = -16;
document.getElementById('audio').innerHTML='<audio src="sounds/high.mp3" preload="auto" autoplay autobuffer></audio>'
};
};
player = new Player();
//Platform class
function Platform() {
this.width = 70;
this.height = 17;
this.x = Math.random() * (width - this.width);
this.y = position;
position += (height / platformCount);
this.flag = 0;
this.state = 0;
//Sprite clipping
this.cx = 0;
this.cy = 0;
this.cwidth = 105;
this.cheight = 31;
//Function to draw it
this.draw = function() {
try {
if (this.type == 1) this.cy = 0;
else if (this.type == 2) this.cy = 61;
else if (this.type == 3 && this.flag === 0) this.cy = 31;
else if (this.type == 3 && this.flag == 1) this.cy = 1000;
else if (this.type == 4 && this.state === 0) this.cy = 90;
else if (this.type == 4 && this.state == 1) this.cy = 1000;
ctx.drawImage(image, this.cx, this.cy, this.cwidth, this.cheight, this.x, this.y, this.width, this.height);
} catch (e) {}
};
//Platform types
//1: Normal
//2: Moving
//3: Breakable (Go through)
//4: Vanishable
//Setting the probability of which type of platforms should be shown at what score
if (score >= 5000) this.types = [2, 3, 3, 3, 4, 4, 4, 4];
else if (score >= 2000 && score < 5000) this.types = [2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4];
else if (score >= 1000 && score < 2000) this.types = [2, 2, 2, 3, 3, 3, 3, 3];
else if (score >= 500 && score < 1000) this.types = [1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3];
else if (score >= 100 && score < 500) this.types = [1, 1, 1, 1, 2, 2];
else this.types = [1];
this.type = this.types[Math.floor(Math.random() * this.types.length)];
//We can't have two consecutive breakable platforms otherwise it will be impossible to reach another platform sometimes!
if (this.type == 3 && broken < 1) {
broken++;
} else if (this.type == 3 && broken >= 1) {
this.type = 1;
broken = 0;
}
this.moved = 0;
this.vx = 1;
}
for (var i = 0; i < platformCount; i++) {
platforms.push(new Platform());
}
//Broken platform object
var Platform_broken_substitute = function() {
this.height = 30;
this.width = 70;
this.x = 0;
this.y = 0;
//Sprite clipping
this.cx = 0;
this.cy = 554;
this.cwidth = 105;
this.cheight = 60;
this.appearance = false;
this.draw = function() {
try {
if (this.appearance === true) ctx.drawImage(image, this.cx, this.cy, this.cwidth, this.cheight, this.x, this.y, this.width, this.height);
else return;
} catch (e) {}
};
};
var platform_broken_substitute = new Platform_broken_substitute();
//Spring Class
var spring = function() {
this.x = 0;
this.y = 0;
this.width = 26;
this.height = 30;
//Sprite clipping
this.cx = 0;
this.cy = 0;
this.cwidth = 45;
this.cheight = 53;
this.state = 0;
this.draw = function() {
try {
if (this.state === 0) this.cy = 445;
else if (this.state == 1) this.cy = 501;
ctx.drawImage(image, this.cx, this.cy, this.cwidth, this.cheight, this.x, this.y, this.width, this.height);
} catch (e) {}
};
};
var Spring = new spring();
function init() {
//Variables for the game
var dir = "left",
jumpCount = 0;
firstRun = false;
//Function for clearing canvas in each consecutive frame
function paintCanvas() {
ctx.clearRect(0, 0, width, height);
}
//Player related calculations and functions
function playerCalc() {
if (dir == "left") {
player.dir = "left";
if (player.vy < -7 && player.vy > -15) player.dir = "left_land";
} else if (dir == "right") {
player.dir = "right";
if (player.vy < -7 && player.vy > -15) player.dir = "right_land";
}
//Adding keyboard controls
document.onkeydown = function(e) {
var key = e.keyCode;
if (key == 37) {
dir = "left";
player.isMovingLeft = true;
} else if (key == 39) {
dir = "right";
player.isMovingRight = true;
}
if(key == 32) {
if(firstRun === true)
init();
else
reset();
}
};
document.onkeyup = function(e) {
var key = e.keyCode;
if (key == 37) {
dir = "left";
player.isMovingLeft = false;
} else if (key == 39) {
dir = "right";
player.isMovingRight = false;
}
};
//Accelerations produces when the user hold the keys
if (player.isMovingLeft === true) {
player.x += player.vx;
player.vx -= 0.15;
} else {
player.x += player.vx;
if (player.vx < 0) player.vx += 0.1;
}
if (player.isMovingRight === true) {
player.x += player.vx;
player.vx += 0.15;
} else {
player.x += player.vx;
if (player.vx > 0) player.vx -= 0.1;
}
//Jump the player when it hits the base
if ((player.y + player.height) > base.y && base.y < height) player.jump();
//Gameover if it hits the bottom
if (base.y > height && (player.y + player.height) > height && player.isDead != "lol") {
player.isDead = true;
document.getElementById('audio').innerHTML='<audio src="sounds/gameover.mp3" preload="auto" autoplay autobuffer></audio>'
}
//Make the player move through walls
if (player.x > width) player.x = 0 - player.width;
else if (player.x < 0 - player.width) player.x = width;
//Movement of player affected by gravity
if (player.y >= (height / 2) - (player.height / 2)) {
player.y += player.vy;
player.vy += gravity;
}
//When the player reaches half height, move the platforms to create the illusion of scrolling and recreate the platforms that are out of viewport...
else {
platforms.forEach(function(p, i) {
if (player.vy < 0) {
p.y -= player.vy;
}
if (p.y > height) {
platforms[i] = new Platform();
platforms[i].y = p.y - height;
}
});
base.y -= player.vy;
player.vy += gravity;
if (player.vy >= 0) {
player.y += player.vy;
player.vy += gravity;
}
score++;
}
//Make the player jump when it collides with platforms
collides();
if (player.isDead === true) gameOver();
}
//Spring algorithms
function springCalc() {
var s = Spring;
var p = platforms[0];
if (p.type == 1 || p.type == 2) {
s.x = p.x + p.width / 2 - s.width / 2;
s.y = p.y - p.height - 10;
if (s.y > height / 1.1) s.state = 0;
s.draw();
} else {
s.x = 0 - s.width;
s.y = 0 - s.height;
}
}
//Platform's horizontal movement (and falling) algo
function platformCalc() {
var subs = platform_broken_substitute;
platforms.forEach(function(p, i) {
if (p.type == 2) {
if (p.x < 0 || p.x + p.width > width) p.vx *= -1;
p.x += p.vx;
}
if (p.flag == 1 && subs.appearance === false && jumpCount === 0) {
subs.x = p.x;
subs.y = p.y;
subs.appearance = true;
jumpCount++;
}
p.draw();
});
if (subs.appearance === true) {
subs.draw();
subs.y += 8;
}
if (subs.y > height) subs.appearance = false;
}
function collides() {
//Platforms
platforms.forEach(function(p, i) {
if (player.vy > 0 && p.state === 0 && (player.x + 15 < p.x + p.width) && (player.x + player.width - 15 > p.x) && (player.y + player.height > p.y) && (player.y + player.height < p.y + p.height)) {
if (p.type == 3 && p.flag === 0) {
p.flag = 1;
jumpCount = 0;
return;
} else if (p.type == 4 && p.state === 0) {
player.jump();
p.state = 1;
} else if (p.flag == 1) return;
else {
player.jump();
}
}
});
//Springs
var s = Spring;
if (player.vy > 0 && (s.state === 0) && (player.x + 15 < s.x + s.width) && (player.x + player.width - 15 > s.x) && (player.y + player.height > s.y) && (player.y + player.height < s.y + s.height)) {
s.state = 1;
player.jumpHigh();
}
}
function updateScore() {
var scoreText = document.getElementById("score");
scoreText.innerHTML = score;
}
function gameOver() {
platforms.forEach(function(p, i) {
p.y -= 12;
});
if(player.y > height/2 && flag === 0) {
player.y -= 8;
player.vy = 0;
}
else if(player.y < height / 2) flag = 1;
else if(player.y + player.height > height) {
showGoMenu();
hideScore();
player.isDead = "lol";
}
}
//Function to update everything
function update() {
paintCanvas();
platformCalc();
springCalc();
playerCalc();
player.draw();
base.draw();
updateScore();
}
menuLoop = function(){return;};
animloop = function() {
update();
requestAnimFrame(animloop);
};
animloop();
hideMenu();
showScore();
}
function reset() {
hideGoMenu();
showScore();
player.isDead = false;
flag = 0;
position = 0;
score = 0;
base = new Base();
player = new Player();
Spring = new spring();
platform_broken_substitute = new Platform_broken_substitute();
platforms = [];
for (var i = 0; i < platformCount; i++) {
platforms.push(new Platform());
}
}
//Hides the menu
function hideMenu() {
var menu = document.getElementById("mainMenu");
menu.style.zIndex = -1;
}
//Shows the game over menu
function showGoMenu() {
var menu = document.getElementById("gameOverMenu");
menu.style.zIndex = 1;
menu.style.visibility = "visible";
var scoreText = document.getElementById("go_score");
scoreText.innerHTML = "Ваш результат " + score + " очков!";
}
//Hides the game over menu
function hideGoMenu() {
var menu = document.getElementById("gameOverMenu");
menu.style.zIndex = -1;
menu.style.visibility = "hidden";
}
//Show ScoreBoard
function showScore() {
var menu = document.getElementById("scoreBoard");
menu.style.zIndex = 1;
}
//Hide ScoreBoard
function hideScore() {
var menu = document.getElementById("scoreBoard");
menu.style.zIndex = -1;
}
function playerJump() {
player.y += player.vy;
player.vy += gravity;
if (player.vy > 0 &&
(player.x + 15 < 260) &&
(player.x + player.width - 15 > 155) &&
(player.y + player.height > 475) &&
(player.y + player.height < 500))
player.jump();
if (dir == "left") {
player.dir = "left";
if (player.vy < -7 && player.vy > -15) player.dir = "left_land";
} else if (dir == "right") {
player.dir = "right";
if (player.vy < -7 && player.vy > -15) player.dir = "right_land";
}
//Adding keyboard controls
document.onkeydown = function(e) {
var key = e.keyCode;
if (key == 37) {
dir = "left";
player.isMovingLeft = true;
} else if (key == 39) {
dir = "right";
player.isMovingRight = true;
}
if(key == 32) {
if(firstRun === true) {
init();
firstRun = false;
}
else
reset();
}
};
document.onkeyup = function(e) {
var key = e.keyCode;
if (key == 37) {
dir = "left";
player.isMovingLeft = false;
} else if (key == 39) {
dir = "right";
player.isMovingRight = false;
}
};
//Accelerations produces when the user hold the keys
if (player.isMovingLeft === true) {
player.x += player.vx;
player.vx -= 0.15;
} else {
player.x += player.vx;
if (player.vx < 0) player.vx += 0.1;
}
if (player.isMovingRight === true) {
player.x += player.vx;
player.vx += 0.15;
} else {
player.x += player.vx;
if (player.vx > 0) player.vx -= 0.1;
}
//Jump the player when it hits the base
if ((player.y + player.height) > base.y && base.y < height) player.jump();
//Make the player move through walls
if (player.x > width) player.x = 0 - player.width;
else if (player.x < 0 - player.width) player.x = width;
player.draw();
}
function update() {
ctx.clearRect(0, 0, width, height);
playerJump();
}
menuLoop = function() {
update();
requestAnimFrame(menuLoop);
};
menuLoop();
}
document.addEventListener("DOMContentLoaded", startGame, false);
<!DOCTYPE HTML>
<html>
<head>
<title>Doodle Jump</title>
<style type="text/css">
#import url(Gloria%20Hallelujah);
*{box-sizing: border-box;}
body {
margin: 0; padding: 0;
font-family: 'Gloria Hallelujah', cursive;
}
.container {
height: 552px;
width: 422px;
position: relative;
margin: 20px auto;
overflow: hidden;
}
canvas {
height: 552px;
width: 422px;
display: block;
background: url(images/Y0BMP.png) top left;
}
#scoreBoard {
width: 420px;
height: 50px;
background: rgba(182, 200, 220, 0.7);
position: absolute;
top: -3px;
left: 0;
z-index: -1;
border-image: url(images/5BBsR.png) 100 5 round;
}
#scoreBoard p {
font-size: 20px;
padding: 0;
line-height: 47px;
margin: 0px 0 0 5px;
}
img {display: none}
#mainMenu, #gameOverMenu {
height: 100%;
width: 100%;
text-align: center;
position: absolute;
top: 0;
left: 0;
z-index: 2;
}
#gameOverMenu {
visibility: hidden;
}
h2, h3, h1 {font-weight: normal}
h1 {
font-size: 60px;
color: #5a5816;
transform: rotate(-10deg);
margin: 0px;
}
h3 {text-align: right; margin: -10px 20px 0 0; color: #5e96be}
h3 a {color: #5a5816}
.button {
width: 105px;
height: 31px;
background: url(images/2WEhF.png) 0 0 no-repeat;
display: block;
color: #000;
font-size: 12px;
line-height: 31px;
text-decoration: none;
position: absolute;
left: 50%;
bottom: 50px;
margin-left: -53px;
}
.info {position: absolute; right: 20px; bottom: 00px; margin: 0; color: green}
.info .key {
width: 16px;
height: 16px;
background: url(images/2WEhF.png) no-repeat;
text-indent: -9999px;
display: inline-block;
}
.info .key.left {background-position: -92px -621px;}
.info .key.right {background-position: -92px -641px;}
</style>
</head>
<body><div style="position:absolute;z-index:999;padding:10px;top:0;right:0;background:#000" id="sxz03">
<div style="float:right;padding-left:10px"><img onclick="document.getElementById('sxz03').style.display='none'" src="images/x.gif" width="15" alt="" /></div>
<br>
<div style="position:absolute;top:-100px"></div>
</div>
<div class="container">
<canvas id="canvas"></canvas>
<div id="mainMenu">
<h1>doodle jump</h1>
<h3>A HTML5 game</h3>
<a class="button" href="javascript:init()">Play</a>
</div>
<div id="gameOverMenu">
<h1>Game Over!</h1>
<h3 id="go_score">Your score was ...</h3>
<a class="button" href="javascript:reset()">Play Again</a>
</div>
<!-- Preloading image ;) -->
<img id="sprite" src="images/2WEhF.png"/>
<div id="scoreBoard">
<p id="score">0</p>
</div>
</div>
<div id="audio"></div>
<script src="jquery.min.js"></script>
<script src="game.js"></script>
</body>
</html>
So when I run this, the intro works, but when I click the play button, nothing happens. If you want to run this code, I will put a download link for the folder that I used in this question.

Move a div within another div with arrow keys

I am new in JavaScript, so I have some difficulties with it. I would like to move a small div within another bigger div with arrow keys - left, up, right and down. I have the code below but it does not work properly.
HTML and CSS
<div id="rectangle">
<div id="square"></div>
</div>
#rectangle {
width: 320px;
height: 200px;
border: 1px solid;
margin: auto;
position: relative;
}
#square {
width: 40px;
height: 40px;
background-color: deepskyblue;
position: absolute;
left: 0;
top:0;
}
Javascript
var rectangle = document.getElementById("rectangle");
var square = document.getElementById("square");
var currentPositionX = 0;
var currentPositionY = 0;
function moveSquare(event) {
if (event.keyCode == 37) {
currentPositionX -= 40;
square.style.left = currentPositionX + 'px';
if (currentPositionX <= 0) {
currentPositionX += 40;
}
}
if (event.keyCode == 38) {
currentPositionY -= 40;
square.style.top = currentPositionY + 'px';
if (currentPositionY <= 0) {
currentPositionY += 40;
}
}
if (event.keyCode == 39) {
currentPositionX += 40;
square.style.left = currentPositionX + 'px';
if (currentPositionX >= 280) {
currentPositionX -= 40;
}
}
if (event.keyCode == 40) {
currentPositionY += 40;
square.style.top = currentPositionY + 'px';
if (currentPositionY >= 160) {
currentPositionY -= 40;
}
}
}
document.onkeydown = moveSquare;
I have tested the code and found that when you are near the borders it's not working properly (Perhaps this was the original question?). On this i tested another simpler solution: I just changed your moveSquare function to only do work when you are inside your bounds (I mean, you shouldn't be always incrementing/decrementing position and then check if you are out of bounds to correct it).
function moveSquare(event) {
if (event.keyCode == 37) {
if (currentPositionX > 0) {
currentPositionX -= 40;
square.style.left = currentPositionX + 'px';
}
}
if (event.keyCode == 38) {
if (currentPositionY > 0) {
currentPositionY -= 40;
square.style.top = currentPositionY + 'px';
}
}
if (event.keyCode == 39) {
if (currentPositionX < 280) {
currentPositionX += 40;
square.style.left = currentPositionX + 'px';
}
}
if (event.keyCode == 40) {
if (currentPositionY < 160) {
currentPositionY += 40;
square.style.top = currentPositionY + 'px';
}
}
}
Here is the jsfiddle: https://jsfiddle.net/epL4a9Lq/
Please, next time be more specific on your error (i would comment this before answering but i have no reputation yet for that).
Hope this helps!

Why JS animation is stuck?

I want the box to traverse the inside of the container from left to right, then down, then right to left and finally back up to the original position. The examples I found all include extra array pos = [180,180]. I don't understand why do I need it when my IF conditions seem to cover all positions.
window.onload = function() {
var t = setInterval(slide, 5);
pos1 = [0, 0];
var box = document.getElementById('sqr');
function slide() {
if (pos1[0] < 180 && pos1[1] < 180) {
pos1[0]++;
box.style.left = pos1[0] + "px";
} else if (pos1[0] >= 180 && pos1[1] < 180) {
pos1[1]++;
box.style.top = pos1[1] + "px";
} else if (pos1[0] >= 180 && pos1[1] >= 180) {
pos1[0]--;
box.style.left = pos1[0] + "px";
} else if (pos1[0] <= 0 && pos1[1] >= 180) {
pos1[1]--;
box.style.top = pos1[1] + "px";
}
}
}
#contain {
position: relative;
width: 200px;
height: 200px;
background-color: pink;
}
#sqr {
position: absolute;
width: 20px;
height: 20px;
background-color: blue;
}
<div id="contain">
<div id="sqr"></div>
</div>
Once the box gets to the maximum X and Y positions, the program is still checking to make sure the box hasn't reached the maximum yet, which causes the all IF conditions to fail. You could do it by checking for Y=0 for the first "leg", X=MAX for the next, Y=MAX for the next, and then X=0 for the last, but instead of that, you can set a "state" which has 4 values to determine which "leg" of the animation is being run, and then just run it for 180 iterations each.
window.onload = function() {
var t = setInterval(slide, 5);
pos1 = [0, 0];
var box = document.getElementById('sqr');
state = 0;
iterations = 0;
function slide() {
if (iterations >= 180) {state = (state + 1) % 4; iterations = 0;}
if (state === 0) pos1[0]++;
else if (state == 1) pos1[1]++;
else if (state == 2) pos1[0]--;
else if (state == 3) pos1[1]--;
iterations++;
box.style.left = pos1[0] + "px";
box.style.top = pos1[1] + "px";
}
}
#contain {
position: relative;
width: 200px;
height: 200px;
background-color: pink;
}
#sqr {
position: absolute;
width: 20px;
height: 20px;
background-color: blue;
}
<div id="contain">
<div id="sqr"></div>
</div>
window.onload = function() {
var t = setInterval(slide, 5);
var box = document.getElementById('sqr');
var left = 0,
top = 0;
function slide() {
var pos1 = [parseInt(box.style.left || 0), parseInt(box.style.top || 0)]
console.log(pos1);
if (pos1[0] == 0 && pos1[1] == 0) { //Top left, go right
left = 1;
top = 0;
} else if (pos1[0] == 180 && pos1[1] == 0) { //Top right, go down
left = 0;
top = 1;
} else if (pos1[0] == 180 && pos1[1] == 180) { //Bottom right, go left
left = -1;
top = 0;
} else if (pos1[0] == 0 && pos1[1] == 180) { //Bottom left, go up
left = 0;
top = -1;
}
box.style.left = (parseInt(box.style.left || 0) + left) + "px";
box.style.top = (parseInt(box.style.top || 0) + top) + "px";
}
}
#contain {
position: relative;
width: 200px;
height: 200px;
background-color: pink;
}
#sqr {
position: absolute;
width: 20px;
height: 20px;
background-color: blue;
}
<div id="contain">
<div id="sqr"></div>
</div>
Here's my take on it. React according to the position of the element, when it reaches a corner, change directions. This makes things easier, since we do not rely on actual positions to know where to go next step...
It is because, when animation gets to pos1 = [180, 180], it executes:
else if (pos1[0] >= 180 && pos1[1] >= 180) {
pos1[0]--;
box.style.left = pos1[0] + "px";
}
And then pos1 = [179, 180], which is not covered by the code.
I suggest using something like that:
var direction = 0; //0 - right, 1 - down, 2 - left, 3 - up
function slide() {
if (pos1[0] < 180 && pos1[1] = 0) {
direction = 0;
} else if (pos1[0] = 180 && pos1[1] < 180) {
direction = 1;
} else if (pos1[0] > 0 && pos1[1] = 180) {
direction = 2;
} else if (pos1[0] = 0 && pos1[1] > 0) {
direction = 3;
}
switch(direction){
case 0:
pos1[0]++;
break;
case 1:
pos1[1]++;
break;
case 2:
pos1[0]--;
break;
case 3:
pos1[1]--;
break;
}
box.style.left = pos1[0] + "px";
box.style.top = pos1[1] + "px";
}
I'm not usually a fan of doing someone's homework for them, but I got intrigued as to what would make it work and couldn't help myself. shrugs
The if statements have been tailored to be more explicit with what they need. This of course was achieved by following the methods suggested to you by #j08691, by just adding a console.log(pos1); at the top of each if section.
This is just for reference, in fact, #Salketer has managed to post before me and it looks a lot cleaner than this. The real answer is in the comments by #j08691
window.onload = function() {
var t = setInterval(slide, 5),
pos1 = [0, 0],
box = document.getElementById('sqr');
function slide() {
if (pos1[0] < 180 && pos1[1] === 0) {
console.log(pos1);
pos1[0]++;
box.style.left = pos1[0] + "px";
} else if (pos1[0] === 180 && pos1[1] < 180) {
console.log(pos1);
pos1[1]++;
box.style.top = pos1[1] + "px";
} else if ((pos1[0] <= 180 && pos1[0] >= 1) && pos1[1] === 180) {
console.log(pos1);
pos1[0]--;
box.style.left = pos1[0] + "px";
} else if (pos1[0] === 0 && pos1[1] <= 180) {
console.log(pos1);
pos1[1]--;
box.style.top = pos1[1] + "px";
}
}
}
#contain {
position: relative;
width: 200px;
height: 200px;
background-color: pink;
}
#sqr {
position: absolute;
width: 20px;
height: 20px;
background-color: blue;
}
<div id="contain">
<div id="sqr"></div>
</div>

forEach inside Ticker makes Canvas slow

I'm creating a HTML5 minigame that uses collision-detection and I've recently discovered that it has a speed problem:
I think the reason of this problem is that...
Inside the 60fps ticker there are two forEach loops, one for the rects array and other for the lasers array. So, when there's 5 rects and 5 lasers in the canvas, it'll loop 5 times in the first forEach and five times in the second at each frame, and each forEach function has lots of ifs in it, making the game slow. How can I change that to something less CPU-intensive?
If you know a bigger speed problem in this minigame, feel free to help me to solve it too.
Here's my entire code:
I highly recommend you to see the JSFiddle instead of the code below, since there's more than 400 lines.
<!DOCTYPE html>
<html>
<head>
<title>VelJS α</title>
<!-- This app was coded by Tiago Marinho -->
<!-- Do not leech it! -->
<link rel="shortcut icon" href="http://i.imgur.com/Jja8mvg.png">
<!-- EaselJS: -->
<script src="http://static.tumblr.com/uzcr0ts/uzIn1l1v2/easeljs-0.7.1.min.js"></script>
<script src="http://pastebin.com/raw.php?i=W4S2mtCp"></script>
<!-- jQuery: -->
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script>
(function () {
// Primary vars (stage, circle, rects):
var stage,
circle, // Hero!
rects = [], // Platforms
lasers = [];
// Velocity vars:
var xvel = 0, // X Velocity
yvel = 0, // Y Velocity
xvelpast = 0,
yvelpast = 0;
// Keyvars (up, left, right, down):
var up = false, // W or arrow up
left = false, // A or arrow left
right = false, // D or arrow right
down = false; // S or arrow down
// Other vars (maxvel, col, pause):
var maxvel = 256, // Maximum velocity
col = false, // Collision detection helper (returns true if collided side-to-side)
pause = false;
// Volatility vars (rmdir, pastrmdir):
var rmdir = 0,
pastrmdir = 0;
// Main part (aka creating stage, creating circle, etc):
function init() {
stage = new createjs.Stage("canvas");
// Creating circle:
var circle = new createjs.Shape();
circle.radius = 11;
circle.graphics.beginFill("#fff").beginStroke("white").drawCircle(circle.radius - 0.5, circle.radius - 0.5, circle.radius);
circle.width = circle.radius * 2;
circle.height = circle.radius * 2;
stage.addChild(circle);
setTimeout(function () {
// newobj(W, H, X, Y)
newobj("laser", 3, 244, stage.canvas.width / 2 - 125, stage.canvas.height / 4 * 3 - 247);
newobj("rect", 125, 3, stage.canvas.width / 2 - 125, stage.canvas.height / 4 * 3 - 250);
}, 250); // Wait until first tick finishes and stage is resized to 100%, then calculate the middle of canvas.
// User Input (Redirect input to Input Handler):
// Keydown:
document.addEventListener("keydown", function (evt) {
if (evt.keyCode == 87 || evt.keyCode == 38) { // up
up = true;
}
if (evt.keyCode == 65 || evt.keyCode == 37) { // left
left = true;
}
if (evt.keyCode == 68 || evt.keyCode == 39) { // right
right = true;
}
if (evt.keyCode == 83 || evt.keyCode == 40) { // down
down = true;
}
if (evt.keyCode == 8 || evt.keyCode == 80) { // del/p
if (pause == false) {
xvelpast = xvel;
yvelpast = yvel;
pause = true;
var fadestep = 0;
for (var i = 1; i > 0; i -= 0.1) {
i = parseFloat(i.toFixed(1));
fadestep++;
fadeFill("circle", i, fadestep);
rects.forEach(function (rect) {
fadeFill("rect", i, fadestep);
});
}
} else {
pause = false;
xvel = xvelpast;
yvel = yvelpast;
var fadestep = 0;
for (var i = 0; i <= 1; i += 0.1) {
i = parseFloat(i.toFixed(1));
fadestep++;
fadeFill("circle", i, fadestep);
rects.forEach(function (rect) {
fadeFill("rect", i, fadestep);
});
}
}
}
});
// Keyup:
document.addEventListener("keyup", function (evt) {
if (evt.keyCode == 87 || evt.keyCode == 38) { // up
up = false;
}
if (evt.keyCode == 65 || evt.keyCode == 37) { // left
left = false;
}
if (evt.keyCode == 68 || evt.keyCode == 39) { // right
right = false;
}
if (evt.keyCode == 83 || evt.keyCode == 40) { // down
down = false;
}
});
// Functions:
// Fade beginFill to a lower alpha:
function fadeFill(obj, i, t) {
setTimeout(function () {
if (obj == "circle") {
circle.graphics.clear().beginFill("rgba(255,255,255," + i + ")").beginStroke("white").drawCircle(circle.radius, circle.radius, circle.radius).endFill();
}
if (obj == "rect") {
for (var r = 0; r < rects.length; r++) {
rects[r].graphics.clear().beginFill("rgba(255,255,255," + i + ")").beginStroke("white").drawRect(0, 0, rects[r].width, rects[r].height).endFill();
}
}
}, t * 20);
};
// To create new rects:
function newobj(type, w, h, x, y) {
if (type == "rect") {
var rect = new createjs.Shape();
rect.graphics.beginFill("#fff").beginStroke("white").drawRect(0, 0, w, h);
rect.width = w + 1;
rect.height = h + 1;
rect.y = Math.round(y) + 0.5;
rect.x = Math.round(x) + 0.5;
stage.addChild(rect);
rects.push(rect);
}
if (type == "laser") {
var laser = new createjs.Shape();
if (w >= h) {
laser.graphics.beginFill("#c22").drawRect(0, 0, w, 1);
laser.width = w;
laser.height = 1;
} else {
laser.graphics.beginFill("#c22").drawRect(0, 0, 1, h);
laser.width = 1;
laser.height = h;
}
laser.shadow = new createjs.Shadow("#ff0000", 0, 0, 5);
laser.y = Math.round(y);
laser.x = Math.round(x);
stage.addChild(laser);
lasers.push(laser);
}
}
// Collision recoil:
function cls(clsdir) {
if (clsdir == "top") {
if (yvel <= 4) {
yvel = 0;
} else {
yvel = Math.round(yvel * -0.5);
}
}
if (clsdir == "left") {
if (xvel <= 4) {
xvel = 0;
} else {
xvel = Math.round(xvel * -0.5);
}
}
if (clsdir == "right") {
if (xvel >= -4) {
xvel = 0;
} else {
xvel = Math.round(xvel * -0.5);
}
}
if (clsdir == "bottom") {
if (yvel >= -4) {
yvel = 0;
} else {
yvel = Math.round(yvel * -0.5);
}
}
col = true;
}
// Die:
function die() {
circle.alpha = 1;
createjs.Tween.get(circle).to({
alpha: 0
}, 250).call(handleComplete);
function handleComplete() {
circle.x = stage.canvas.width / 2 - circle.radius;
circle.y = stage.canvas.height / 2 - circle.radius;
createjs.Tween.get(circle).to({
alpha: 1
}, 250);
yvel = 0;
xvel = 0;
yvelpast = 0;
xvelpast = 0;
}
yvel = yvel/2;
xvel = xvel/2;
}
// Set Intervals:
// Speed/Score:
setInterval(function () {
if (pause == false) {
speed = Math.abs(xvel) + Math.abs(yvel);
$(".speed").html("Speed: " + speed);
} else {
speed = Math.abs(xvelpast) + Math.abs(yvelpast);
$(".speed").html("Speed: " + speed + " (Paused)");
}
}, 175);
// Tick:
createjs.Ticker.on("tick", tick);
createjs.Ticker.setFPS(60);
function tick(event) {
// Input Handler:
if (up == true) {
yvel -= 2;
} else {
if (yvel < 0) {
yvel++;
}
}
if (left == true) {
xvel -= 2;
} else {
if (xvel < 0) {
xvel++;
}
}
if (right == true) {
xvel += 2;
} else {
if (xvel > 0) {
xvel--;
}
}
if (down == true) {
yvel += 2;
} else {
if (yvel > 0) {
yvel--;
}
}
// Volatility:
pastrmdir = rmdir;
rmdir = Math.floor((Math.random() * 20) + 1);
if (rmdir == 1 && pastrmdir != 4) {
yvel--;
}
if (rmdir == 2 && pastrmdir != 3) {
xvel--;
}
if (rmdir == 3 && pastrmdir != 2) {
xvel++;
}
if (rmdir == 4 && pastrmdir != 1) {
yvel++;
}
// Velocity limiter:
if (xvel > maxvel || xvel < maxvel * -1) {
(xvel > 0) ? xvel = maxvel : xvel = maxvel * -1;
}
if (yvel > maxvel || yvel < maxvel * -1) {
(yvel > 0) ? yvel = maxvel : yvel = maxvel * -1;
}
// Collision handler:
// xvel and yvel modifications must be before this!
rects.forEach(function (rect) { // Affect all rects
// Collision detection:
// (This MUST BE after every change in xvel/yvel)
// Next circle position calculation:
nextposx = circle.x + event.delta / 1000 * xvel * 30,
nextposy = circle.y + event.delta / 1000 * yvel * 30;
// Collision between objects (Rect and Circle):
if (nextposy + circle.height > rect.y && circle.y + circle.height < rect.y && circle.x + circle.width > rect.x && circle.x < rect.x + rect.width) {
cls("top");
}
if (nextposx + circle.width > rect.x && circle.x + circle.width < rect.x && circle.y + circle.height > rect.y && circle.y < rect.y + rect.height) {
cls("left");
}
if (nextposx < rect.x + rect.width && circle.x > rect.x + rect.width && circle.y + circle.height > rect.y && circle.y < rect.y + rect.height) {
cls("right");
}
if (nextposy < rect.y + rect.height && circle.y > rect.y + rect.height && circle.x + circle.width > rect.x && circle.x < rect.x + rect.width) {
cls("bottom");
}
rects.forEach(function (rect) {
// Check side-to-side collisions with other rects:
if (nextposy + circle.height > rect.y && circle.y + circle.height < rect.y && circle.x + circle.width > rect.x && circle.x < rect.x + rect.width) {
col = true;
}
if (nextposx + circle.width > rect.x && circle.x + circle.width < rect.x && circle.y + circle.height > rect.y && circle.y < rect.y + rect.height) {
col = true;
}
if (nextposx < rect.x + rect.width && circle.x > rect.x + rect.width && circle.y + circle.height > rect.y && circle.y < rect.y + rect.height) {
col = true;
}
if (nextposy < rect.y + rect.height && circle.y > rect.y + rect.height && circle.x + circle.width > rect.x && circle.x < rect.x + rect.width) {
col = true;
}
});
// Edge-to-edge collision between objects (Rect and Circle) - Note that this will not occur if a side-to-side collision occurred in the current frame!:
if (nextposy + circle.height > rect.y &&
nextposx + circle.width > rect.x &&
nextposx < rect.x + rect.width &&
nextposy < rect.y + rect.height &&
col == false) {
if (circle.y + circle.height < rect.y &&
circle.x + circle.width < rect.x) {
cls("top");
cls("left");
}
if (circle.y > rect.y + rect.height &&
circle.x + circle.width < rect.x) {
cls("bottom");
cls("left");
}
if (circle.y + circle.height < rect.y &&
circle.x > rect.x + rect.width) {
cls("top");
cls("right");
}
if (circle.y > rect.y + rect.height &&
circle.x > rect.x + rect.width) {
cls("bottom");
cls("right");
}
}
col = false;
// Stage collision:
if (nextposy < 0) { // Collided with TOP of stage. Trust me.
cls("bottom"); // Inverted clsdir is proposital!
}
if (nextposx < 0) {
cls("right");
}
if (nextposx + circle.width > stage.canvas.width) {
cls("left");
}
if (nextposy + circle.height > stage.canvas.height) {
cls("top");
}
});
// Laser collision handler:
lasers.forEach(function (laser) {
laser.alpha = Math.random() + 0.5;
nextposx = circle.x + event.delta / 1000 * xvel * 30,
nextposy = circle.y + event.delta / 1000 * yvel * 30;
if (nextposy + circle.height > laser.y && circle.y + circle.height < laser.y && circle.x + circle.width > laser.x && circle.x < laser.x + laser.width) {
circle.y = laser.y-circle.height;
die();
}
if (nextposx + circle.width > laser.x && circle.x + circle.width < laser.x && circle.y + circle.height > laser.y && circle.y < laser.y + laser.height) {
circle.x = laser.x-circle.width;
die();
}
if (nextposx < laser.x + laser.width && circle.x > laser.x + laser.width && circle.y + circle.height > laser.y && circle.y < laser.y + laser.height) {
circle.x = laser.x+laser.width;
die();
}
if (nextposy < laser.y + laser.height && circle.y > laser.y + laser.height && circle.x + circle.width > laser.x && circle.x < laser.x + laser.width) {
circle.y = laser.y+laser.height;
die();
}
});
// Velocity:
if (pause == true) {
xvel = 0;
yvel = 0;
}
circle.x += event.delta / 1000 * xvel * 20;
circle.y += event.delta / 1000 * yvel * 20;
// Stage.canvas 100% width and height:
stage.canvas.width = window.innerWidth;
stage.canvas.height = window.innerHeight;
// Update stage:
stage.update(event);
}
setTimeout(function () {
// Centre circle:
circle.x = stage.canvas.width / 2 - circle.radius;
circle.y = stage.canvas.height / 2 - circle.radius;
// Fade-in after loading:
$(".speed").css({
opacity: 1
});
$("canvas").css({
opacity: 1
});
}, 500);
}
$(function () {
init();
});
})();
</script>
<style>
* {
margin: 0;
}
html,
body {
-webkit-font-smoothing: antialiased;
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
font-weight: 300;
color: #fff;
background-color: #181818
}
.build {
position: absolute;
bottom: 5px;
right: 5px;
color: rgba(255, 255, 255, 0.05)
}
canvas {
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
filter: alpha(opacity=0);
opacity: 0;
position: absolute;
top: 0;
left: 0;
-moz-transition: 5s ease;
-o-transition: 5s ease;
-webkit-transition: 5s ease;
transition: 5s ease
}
.speed {
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
filter: alpha(opacity=0);
opacity: 0;
position: absolute;
top: 5px;
left: 5px;
color: #fff;
font-size: 16px;
-moz-transition: 5s ease;
-o-transition: 5s ease;
-webkit-transition: 5s ease;
transition: 5s ease
}
h2 {
text-align: center;
font-size: 22px;
font-weight: 700
}
p {
font-size: 16px;
margin: 0
}
</style>
</head>
<body>
<p class="speed"></p>
<p class="build">α256</p>
<canvas id="canvas">
<h2>Your browser doesn't support Canvas.</h2>
<p>Switch to <b>Chrome 33</b>, <b>Firefox 27</b> or <b>Safari 7</b>.</p>
</canvas>
</body>
</html>
JSFiddle
The game logic isn't really a problem, the reason it's slow is because you "create" a new canvas every tick by setting the width and height:
stage.canvas.width = window.innerWidth;
stage.canvas.height = window.innerHeight;
So, even if you set the canvas width and height to the same values they had, under the hood pretty much a new canvas is constructed. If you remove the lines above from the game loop it should run smoothly.
Just set the canvas width and height once and then listen for window resize and set it when the browser window changes size.
Running logic in a tick can be expensive, as is updating the canvas each frame. If you can, a lower framerate could be advisable - since it often isn't necessary to run at 60fps. If you want to keep refreshing the canvas at that rate, and you have any particularly expensive functions, such as pathfinding, collision, etc - you could always decouple that from your update loop, so it isn't running quite as often.
Note that stage updating can be very expensive, especially with vectors. If you can, find a solution that lets you cache vector content as bitmaps, and update the vector as little as possible. In your case, since you are just fading the fill - you might separate the fill from the outline, cache them separately, and then use alpha on the fill object. If you have a lot of shapes, you can reuse the caches between them, and you will see huge performance gains.
Best of luck!

Categories

Resources