A canvas multiplayer game, I cannot press more than 4 buttons - javascript

So im trying to learn about canvas and canvas games and im currently (more or less) following the W3Schools tutorial on canvas games.
At some point in the tutorial i had the idea to make 2 players, which should both be controled on the same keyboard (NOT online multiplayer).
So i followed the logic given by the tutorial and found the key codes for both WASD and the arrows.
I understand 95% of my code, which means i did not just copy everything without understanding it. (I will comeback to this soon)
The problem with my code is that when i add another player to the system i can move them freely when i only control one player at a time, when i try to move both players at the same time, they cannot be moved freely and i can only press a total of 4 buttons at a time.
Tryout the snippet and play around with the cubes with WASD and the arrows to see what im talking about.
As i said there is a part i do not understand 100% which might be the place for this error? I marked it out anyway on the code snippet.
So all in all my question is: Why can i not move both players freely at the same time?
The whole code is as following and i marked the part i do not understand:
For best experience, use the full screen function
{
function startGame() {
myGameArea.start();
myStick = new component(100, 100, 200, 200, "red");
myStick2 = new component(100, 100, 600, 200, "green");
}
var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
var bodyID = document.getElementById("body");
this.canvas.width = bodyID.offsetWidth;
this.canvas.height = (bodyID.offsetHeight);
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[2]);
this.interval = setInterval(updateGameArea, (1000 / 60));
//The part i do not understand
window.addEventListener('keydown', function (e) {
myGameArea.keys = (myGameArea.keys || []);
myGameArea.keys[e.keyCode] = true;
});
window.addEventListener('keyup', function (e) {
myGameArea.keys[e.keyCode] = false;
});
//End
},
clear : function(){
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
};
function component(width, height, x, y, color, mLeft, mRight, mUpLeft, mUpRigth){
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.speedX = 0;
this.speedY = 0;
this.update = function(){
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
};
this.newPos = function(){
this.x += this.speedX;
this.y += this.speedY;
};
this.player1 = function(){
this.speedX = 0;
this.speedY = 0;
if (myGameArea.keys && myGameArea.keys[65]) {this.speedX = -2; } // Left
if (myGameArea.keys && myGameArea.keys[68]) {this.speedX = 2; } // Right
if (myGameArea.keys && myGameArea.keys[87]) {this.speedY = -2; } // Up
if (myGameArea.keys && myGameArea.keys[83]) {this.speedY = 2; } // Down
};
this.player2 = function(){
this.speedX = 0;
this.speedY = 0;
if (myGameArea.keys && myGameArea.keys[37]) {this.speedX = -2; } // Left
if (myGameArea.keys && myGameArea.keys[39]) {this.speedX = 2; } // Right
if (myGameArea.keys && myGameArea.keys[38]) {this.speedY = -2; } // Up
if (myGameArea.keys && myGameArea.keys[40]) {this.speedY = 2; } // Down
};
}
function updateGameArea(){
myGameArea.clear();
myStick.player1();
myStick.newPos();
myStick2.player2();
myStick2.newPos();
myStick.update();
myStick2.update();
}
}
.nm{
margin: 0;
padding: 0;
}
canvas{
display: block;
background-color: lightgray;
}
<html>
<head>
<meta charset="UTF-8">
<title>Canvas stick game!</title>
<link rel="stylesheet" href="css/standard.css">
</head>
<body id="body" onload="startGame()" class="nm" style="height: 100vh">
</body>
</html>
<script src="js/canvas.js"></script>

I know this is redundant (anyone who reads the comments below your question will figure this out), but just so it's here as an answer, your problem is a keyboard bug.
Because of the way (some) non-digital keyboards work, certain keyboard combinations do not work properly. (ie. Certain keys will cancel each other out.)
(This is just another reason to supply customizable game controls, customizable program shortcuts, etc. Another reason is the fact that people with DVORAK keyboards are likely to find your QWERTY optimized game controls to be cumbersome.)
Other:
PS: I cannot reproduce your problem, and if you tried it on another computer, it is likely that you could not reproduce it either.
PPS: For more information check out the following article: Keyboards are evil.

Not sure what the bug was, looked ok as it was but as there were several issues. Saddly W3Schools is not what I would consider a uptodate, but I can offer no alternative.
I made changes to improve the whole thing but you will still lose keys, I am not sure why as the standard says nothing about loss of keys (It says all keys MUST be reported)
When hitting 4 or more keys at the same time (within 1/60th second) none of them are reported. This is highly unlikely when two people are playing, but when one person is testing both direction pads it happens often. I am unaware of a solution.
The key event keyCode has depreciated and you should be using event.key which is a string but event.key is only partially supported.
{
// key maps
const KEY_UP = 38;
const KEY_DOWN = 40;
const KEY_LEFT = 37;
const KEY_RIGHT = 39;
const KEY_W = 87;
const KEY_S = 83;
const KEY_A = 65;
const KEY_D = 68;
const DIR_KEY_MAP2 = [KEY_W, KEY_S, KEY_A, KEY_D];
const DIR_KEY_MAP1 = [KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT];
const BLOCK_DEFAULT_FOR = [KEY_W, KEY_S, KEY_A, KEY_D, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT];
const keyEvents = function(event){ // keyboard event listener sets the key array to true if keydown false if not
if(!event.repeat){ // ignore repeating key events
myGameArea.keys[event.keyCode] = event.type === "keydown";
}
// block default action for keys in array BLOCK_DEFAULT_FOR
if(BLOCK_DEFAULT_FOR.indexOf(event.keyCode) > -1){
event.preventDefault();
}
}
function startGame() {
myGameArea.start();
myStick = new Component(100, 100, 200, 200, "red", DIR_KEY_MAP2);
myStick2 = new Component(100, 100, 600, 200, "green", DIR_KEY_MAP1);
}
var myGameArea = {
canvas : document.createElement("canvas"),
keys : [], // create the key array
start : function() {
var bodyID = document.getElementById("body");
this.canvas.width = bodyID.offsetWidth;
this.canvas.height = (bodyID.offsetHeight);
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[2]);
requestAnimationFrame(updateGameArea); // request the first animation frame don't use setInterval
window.addEventListener('resize', function () { // for stackoverflow
myGameArea.canvas.width = bodyID.offsetWidth;
myGameArea.canvas.height = bodyID.offsetHeight;
});
window.addEventListener('keydown', keyEvents); // this is called once for every key down
window.addEventListener('keyup', keyEvents); // this is called once for every key up.
},
clear : function () {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
};
function Component(width, height, x, y, color, keyMap){ // key map is the keys used to control
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.speedX = 0;
this.speedY = 0;
// get reference to context
var ctx = myGameArea.context;
// clear the keys
var i = 3;
while(i >= 0){ myGameArea.keys[keyMap[i--]] = false; }
this.update = function(){
this.userInput();
this.x += this.speedX;
this.y += this.speedY;
}
this.display = function(){
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
};
this.userInput = function(){ // keyMap is accessed via closure
this.speedY = this.speedX = 0;
if (myGameArea.keys[keyMap[2]]) {this.speedX = -2; } // Left
if (myGameArea.keys[keyMap[3]]) {this.speedX = 2; } // Right
if (myGameArea.keys[keyMap[0]]) {this.speedY = -2; } // Up
if (myGameArea.keys[keyMap[1]]) {this.speedY = 2; } // Down
};
}
function updateGameArea(){
myGameArea.clear();
myStick.update();
myStick2.update();
myStick.display();
myStick2.display();
requestAnimationFrame(updateGameArea); // request the next animation frame in 1/60th second
}
}
.nm{
margin: 0;
padding: 0;
}
canvas{
display: block;
background-color: lightgray;
}
<html>
<head>
<meta charset="UTF-8">
<title>Canvas stick game!</title>
<link rel="stylesheet" href="css/standard.css">
</head>
<body id="body" onload="startGame()" class="nm" style="height: 100vh">
</body>
</html>
<script src="js/canvas.js"></script>

Related

Game components won't draw after adding if/else statement

My game components won't draw onto the canvas after adding if/else statement.
The statement only checks if the game piece hit the game obstacle.
I tried changing attributes and rewrite some functions but it seems the problem hasn't been fixed.
Whenever I remove the if/else function, the components draw.
Here is part of the code that holds that if/else function:
if(gamePieceBorder.crashGame(gameObstacle) || gamePieceRed.crashGame(gameObstacle))
{
gameArea.stop();
}
else
{
obstacle.update();
gamePieceBorder.pos();
gamePieceBorder.move();
gamePieceBorder.update();
gamePieceRed.pos();
gamePieceRed.move();
gamePieceRed.update();
gameArea.clear();
}
For me not pasting an entire code, here is the pastebin link to the code: https://pastebin.com/HuiR7r7D
How can I get the components to draw? If someone fixes the code, what was the issue? I am not an expert at javascript but only a beginner.
There are several problems:
window.EventListener should be window.addEventListener
keyup and keydown should have no upper case letters
gameObstacle in that if is undefined (should be obstacle probably)
clear method should be called before drawing, not after it
Here is the corrected script: https://pastebin.com/bXpQ2qvB
//-----------------------------------------Variables
var gamePieceRed;
var gamePieceBorder;
var gameObstacle;
//-----------------------------------------
//-----------------------------------------Main game function
function startGame()
{
gamePieceRed = new component(22, 22, "rgb(255, 132, 156)", 10, 120);
gamePieceBorder = new component(24, 24, "black", 9, 119);
obstacle = new component(10, 200, "rgb(64, 0 ,12)", 300, 120)
gameArea.start();
}
//-----------------------------------------
//-----------------------------------------Creating game area and applying controls
var gameArea =
{
canvas : document.createElement("canvas"), start : function()
{
this.canvas.width = 510;
this.canvas.height = 280;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.interval = setInterval(gameUpdate, 20);
window.addEventListener("keydown", function (e)
{
gameArea.keys = (gameArea.keys || []);
gameArea.keys[e.keyCode] = true;
}, true)
window.addEventListener("keyup", function (e)
{
gameArea.keys[e.keyCode] = false;
}, true)
},
clear : function()
{
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
stop : function()
{
clearInterval(this.interval);
},
keyboard: function() {
if (this.keys) {
if (this.keys[37]) {gamePieceBorder.speedX = gamePieceRed.speedX = -2;}
else if (this.keys[39]) {gamePieceBorder.speedX = gamePieceRed.speedX = 2;}
else {gamePieceBorder.speedX = gamePieceRed.speedX = 0;}
if (this.keys[38]) {gamePieceBorder.speedY = gamePieceRed.speedY = -2;}
else if (this.keys[40]) {gamePieceBorder.speedY = gamePieceRed.speedY = 2;}
else {gamePieceBorder.speedY = gamePieceRed.speedY = 0;}
}
}
}
//-----------------------------------------
//-----------------------------------------Game component
function component(width, height, color, x, y)
{
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.speedX = 0;
this.speedY = 0;
this.update = function()
{
ctx = gameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height)
}
this.move = function()
{
this.x += this.speedX;
this.y += this.speedY;
}
this.crashGame = function(obj)
{
var left = this.x;
var right = this.x + (this.width);
var top = this.y;
var bottom = this.y + (this.height);
var otherLeft = obj.x;
var otherRight = obj.x + (obj.width);
var otherTop = obj.y;
var otherBottom = obj.y + (obj.height);
var crash = true;
if (bottom < otherTop || top > otherBottom || right < otherLeft || left > otherRight)
{
crash = false;
}
return crash;
}
}
//-----------------------------------------
//-----------------------------------------Game area updater
function gameUpdate()
{
if(gamePieceBorder.crashGame(obstacle) || gamePieceRed.crashGame(obstacle))
{
gameArea.stop();
}
else
{
gameArea.clear();
obstacle.update();
gameArea.keyboard();
gamePieceBorder.move();
gamePieceBorder.update();
gamePieceRed.move();
gamePieceRed.update();
}
}
//-----------------------------------------
<html>
<style>
canvas
{
border: 1px solid #d3d3d3;
background-image: url("https://ak0.picdn.net/shutterstock/videos/22492090/thumb/1.jpg");
}
</style>
<body onload = "startGame()">
</body>
</html>

HTML Graphics - HTML Game: Collusion control (destroy and score)

I was putting in practice the HTML Game for the first time.
I managed to get through with my game just having the https://www.w3schools.com/graphics/game_intro.asp as my only guide.
The game is very simple: Random objects (green squares) fall from above. The player (red square) moves right and left. The main goal here in the game is for the player to collect/collide with as many as falling objects. Every time the collision happens with 1 object, the player earns 1 point.
Now, how can i destroy the object(green square) that collides with the player and set 1 point at a time?
Here is the code:
<!DOCTYPE html>
<html>
<head>
<title>Food Game v1</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
canvas {
border:1px solid #d3d3d3;
background-image: linear-gradient(#FFFDF0, white);
}
/* buttons align in center*/
.center {
margin: auto;
width: 90%;
padding: 10px;
}
</style>
</head>
<body onload="startGame()">
<script>
var myGamePiece;
var myFallenObj_ = []; //array of fallen objects
var myScore;
function startGame() {
myGamePiece = new component(30, 30, "red", 130, 300);
myScore = new component("10px", "Consolas", "black", 100, 345,"text");
myGameArea.start();
}
var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 300;
this.canvas.height = 350;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.frameNo = 0; //if we use an array
this.interval = setInterval(updateGameArea, 20);
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
stop: function(){
clearInterval(this.interval);
}
}
function component(width, height, color, x, y, type) {
this.type = type;
this.width = width;
this.height = height;
this.score = 0;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
if(this.type == "text"){ //check on text object
ctx.font = this.width + " " + this.height;
ctx.fillStyle = color;
ctx.fillText(this.text, this.x, this.y);
}else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
this.crashWith = function(otherobj){
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if((myright < otherleft) || (myleft > otherright) || (mybottom < othertop) || (mytop > otherbottom)) {
crash = false;
}
return crash;
}
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.newPos();
myGamePiece.update();
var width_, minWidth_, maxWidth_;
var x,y;
for (i=0;i<myFallenObj_.length; i++){
if(myGamePiece.crashWith(myFallenObj_[i])){ //if collision happens
myScore.score ++;
//destroy(myFallenObj_[i]);
}
}
myScore.text = "SCORE: " + myScore.score;
//check myGamePiece not go over the left border
if(myGamePiece.x <= 0)
myGamePiece.x = 1;
//check myGamePiece not go over the right border
if(myGamePiece.x > 265)
myGamePiece.x = 260;
myScore.update();
myGameArea.frameNo +=1;
if (myGameArea.frameNo == 1 || everyinterval(150)){
//x = 10;
minWidth_=0;
maxWidth_=300;
width_ = Math.floor(Math.random()*(maxWidth_-minWidth_+1)+minWidth_);
y = - 50;
myFallenObj_.push(new component (30,30,"green", width_, y));
}
for (i=0; i<myFallenObj_.length; i++){
myFallenObj_[i].x +=0;
myFallenObj_[i].y +=1;
myFallenObj_[i].update();
}
}
function everyinterval(n){
if((myGameArea.frameNo /n) % 1 == 0)
return true;
return false;
}
function moveleft() {
myGamePiece.speedX = - 2;
}
function moveright() {
myGamePiece.speedX = 2;
}
function clearmove() {
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
}
</script>
<div class="center">
<button onmousedown="moveleft()" onmouseup="clearmove()" ontouchstart="moveleft()">LEFT</button>
<button onmousedown="moveright()" onmouseup="clearmove()" ontouchstart="moveright()">RIGHT</button><br><br>
</div>
</body>
</html>
What i've done so far is to apply that collision and count the score, but the score counts for as long as the red and green keep touching themselves repeatedly (which is wrong).
Any suggestions are more than welcome, thx!
Add scorable attribute to your component:
function component(...) {
...
this.scorable = true;
...
}
Then use the attribute to flag if it still scorable, if not skip:
for (i=0;i<myFallenObj_.length; i++){
if(myGamePiece.crashWith(myFallenObj_[i]) && myFallenObj_[i].scorable){ //if collision happens
myScore.score ++;
myFallenObj_[i].scorable = false;
//destroy(myFallenObj_[i]);
}
}
Thanks to #ACD i solved half of my problem concerning the points' count.
The other half of my answer was how to handle my (green) component: get the point when the green component crashes on to the red one and then disappears.
Well, i couldn't handle properly the destroy attribute of my object (in this case my component). Too much JS with Unity, i suppose :p . I don't know if there is such an attribute for the HTML Canvas' philosophy. To "apply" in a way the destroy attribute to my problem here (as to make my green component to disappear from the canvas) i had to create an "illusion" that came to my mind at first. Basically, playing with the component's positioning around the canvas.
Mainly, i had to set a variable var garbagePOS = 2000; //any possible measure out of my canvas size.
And then i set the position x of my crashed green component into its new positioning equals 2000 (visually, out of my content) myFallenObj_[i].x = garbagePOS; And just like that it creates the illusion. Probably, not an ideal solution. However it works fine for me at the moment. Any more suggestions for the above?

how to fix the bliking of the balls in the canvas?

An object type of ball is created with a new constructor function ball(color).
Its properties include all that need to paint it on a canvas and to cause him to move in a random direction.
When one ball is created orangeBall = new ball('orange'), it shows up well including its movement on the canvas. But when another one is added, both of them start to blink.
How to solv it ?
Thanks.
<!DOCTYPE html>
<html lang="he">
<head>
<meta charset="utf-8" />
<title>
כדורים קופצים רנדומלית בצבעים שונים
</title>
<style>
html,body {
margin: 0;
padding: 0;
background: black;
}
.container { width:900px; margin:0 auto; }
#canvas { background:#8613eb; border:1px solid #cbcbcb; }
</style>
<script>
var ctx;
var H = 800;
var W = 800;
window.onload = function () {
ctx = canvas.getContext("2d");
canvas.width=W;
canvas.height=H;
function ball(color) {
//life - the amount of time to show the ball in the screen
this.life = 60*1000, //1 minute
this.color = arguments.length==1 ? color: 'white';
this.x= Math.round(Math.random()*W);
this.y= Math.round(Math.random()*H);
this.radius= 10 + Math.round(Math.random()*50);// between 10-60
this.dx=1+ Math.round(Math.random()*5); //between 1-6
this.dy= 2+ Math.round(Math.random()*4); //between 2-6
this.startAngel= 0;
this.endAngel= 2*Math.PI; //360deg
this.speed= 3+Math.round(Math.random()*50) //3-50msec
this.show = function() {
//first clear the previous ball
ctx.clearRect(0,0,canvas.width,canvas.height);
var xClear = (this.x-this.radius) <=0 ? 0:(this.x - this.radius);
var yClear = (this.y-2*this.radius) <=0 ? 0:(this.y - 2*this.radius);
ctx.clearRect(xClear,yClear,canvas.width,canvas.height);
//lets stroke the ball
ctx.beginPath();
ctx.fillStyle = this.color;
this.x+=this.dx;
this.y+=this.dy;
if (this.x<0 || this.x>W) {
this.dx=-this.dx;
}
if (this.y<0 || this.y>H) {
this.dy=-this.dy;
}
ctx.arc(this.x,this.y,this.radius,this.startAngel,this.endAngel);
ctx.closePath();
ctx.fill();
this.life-=this.speed;
var _this = this;
// creating new property in the ball 'timePointer'
this.timePointer = this.life<=0 ?
clearInterval(this.timePointer):
setTimeout(function() {
_this.show();
},this.speed);
}
this.show();
};
orangeBall = new ball('orange');
blackBall = new ball('black');
// whiteBall = new ball('white');
// yellowgeBall = new ball('yellow');
// pinkBall = new ball('pink');
// blueBall = new ball('blue');
// greenBall = new ball('green');
};
</script>
</head>
<body>
<div class="container">
<canvas id="canvas">Your browser doesn't support this game.</canvas>
</div>
</body>
</html>
You are clearing your whole canvas after every ball draws itself with its show function. Instead of having the balls clear the canvas, have an interval that will call a function that will clear the canvas once and then iterates all the balls and draw them.
Animations are divided up into frames. A frame is approx 1/60th of a second and in that time all of the animation is drawn.
To help with animations the browser has a function that you use to call the function that renders your frame. requestAnimationFrame(yourfunction)
requestAnimationFrame will tell the browser that you are making changes to an animation. This stops the canvas being presented to the display until the next vertical display refresh.
Using setInterval or setTimeout
function animateSomething(){
// draw something
} // as soon as this exits the content of the canvas is moved to the display
// there is no way to know where the display hardware is writing pixels to
// display, could be halfway through drawing a ball
setInterval(animateSomething,1000 / 60);
If you do this for many objects each time you exit the pixels are move to the display without regard to what the display hardware is doing. This causes flicker, shearing and other problems.
Using requestAnimationFrame
function animateSomething(){
// draw something
requestAnimationFrame(animateSomething)
} // The content of the canvas does not move to the display until the
// display hardware is getting ready to scan the next display refresh
requestAnimationFrame(animateSomething);
The best way to handle animations is to do all the rendering from one function.
Below i have modified your code to remove the flicker using requestAnimationFrame I have added a mainLoop function that draws all the balls. I let the browser handle the timing.
var ctx;
var H = 800;
var W = 800;
window.onload = function() {
ctx = canvas.getContext("2d");
canvas.width = W;
canvas.height = H;
function ball(color) {
//life - the amount of time to show the ball in the screen
this.life = 60 * 1000; //1 minute
this.color = arguments.length == 1 ? color : 'white';
this.x = Math.round(Math.random() * W);
this.y = Math.round(Math.random() * H);
this.radius = 10 + Math.round(Math.random() * 50); // between 10-60
this.dx = 1 + Math.round(Math.random() * 5); //between 1-6
this.dy = 2 + Math.round(Math.random() * 4); //between 2-6
this.startAngel = 0;
this.endAngel = 2 * Math.PI; //360deg
this.speed = 3 + Math.round(Math.random() * 50) //3-50msec
this.show = function() {
//first clear the previous ball
ctx.beginPath();
ctx.fillStyle = this.color;
this.x += this.dx;
this.y += this.dy;
if (this.x < 0 || this.x > W) {
this.dx = -this.dx;
}
if (this.y < 0 || this.y > H) {
this.dy = -this.dy;
}
ctx.arc(this.x, this.y, this.radius, this.startAngel, this.endAngel);
ctx.closePath();
ctx.fill();
this.life -= this.speed;
}
};
orangeBall = new ball('orange');
blackBall = new ball('black');
whiteBall = new ball('white');
yellowgeBall = new ball('yellow');
pinkBall = new ball('pink');
blueBall = new ball('blue');
greenBall = new ball('green');
var balls = [orangeBall, blackBall, whiteBall, yellowgeBall, pinkBall, blueBall, greenBall];
function mainLoop(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(var i = 0; i < balls.length; i++){
if(balls[i].life > 0){
balls[i].show();
}
}
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
};
html,
body {
margin: 0;
padding: 0;
background: black;
}
.container {
width: 900px;
margin: 0 auto;
}
#canvas {
background: #8613eb;
border: 1px solid #cbcbcb;
}
<div class="container">
<canvas id="canvas">Your browser doesn't support this game.</canvas>
</div>
I changed the code to use prototype. Is that better ?
In addition it's not smooth enough.
Added also performance timeline for your help.
var ctx;
var H = window.innerHeight;
var W = window.innerWidth;
window.onload = function () {
ctx = canvas.getContext("2d");
canvas.width=W;
canvas.height=H;
function ball(color) {
//life - the amount of time to show the ball in the screen
this.life = 60*10, //duration of 600 X 16ms(fram per sec)
this.color = arguments.length==1 ? color: 'white';
this.x= Math.round(Math.random()*W);
this.y= Math.round(Math.random()*H);
this.radius= 10 + Math.round(Math.random()*50);// between 10-60
this.dx=1+ Math.round(Math.random()*5); //between 1-6
this.dy= 2+ Math.round(Math.random()*4); //between 2-6
this.startAngel= 0;
this.endAngel= 2*Math.PI; //360deg
this.start = null;
};
ball.prototype.show = function() {
//lets stroke the ball
ctx.beginPath();
ctx.fillStyle = this.color;
this.x+=this.dx;
this.y+=this.dy;
if (this.x<0 || this.x>W) {
this.dx=-this.dx;
}
if (this.y<0 || this.y>H) {
this.dy=-this.dy;
}
ctx.arc(this.x,this.y,this.radius,this.startAngel,this.endAngel);
ctx.closePath();
ctx.fill();
this.life--;
};
var arrBalls = [] ;
arrBalls.push(new ball('orange'));
arrBalls.push(new ball('black'));
arrBalls.push(new ball('white'));
arrBalls.push(new ball('pink'));
arrBalls.push(new ball('blue'));
arrBalls.push(new ball('green'));
// This loop shell be less then 10ms inorder not to maintain
// 60fps interval of screen hardware rendering
var balldeth = 0;
function paint() {
//clear the canvas once in xframes
ctx.clearRect(0,0,canvas.width,canvas.height);
//paint balls in the canvas
for (var i = 0; i < arrBalls.length; i++ ) {
arrBalls[i].life >= 0 ? arrBalls[i].show() : ++balldeth;
}
(balldeth == arrBalls.length) ? console.log("game over"):window.requestAnimationFrame(paint);
}
window.requestAnimationFrame(paint);
};
html,body {
margin: 0;
padding: 0;
background: black;
}
#canvas { background:#8613eb; border:1px solid #cbcbcb; }
<body>
<canvas id="canvas">Your browser doesn't support this game.</canvas>
</body>

How do I increase the score value of my game?

I am making a game in which the player catches falling objects. When an object is caught, the score should increase by one, but it doesn't. I have a collision function already in which the objects disappear when the player catches them. The score is already displayed on the screen. Here is my code:
(() => {
// In general, don't touch anything except for sections explicitly marked as such.
// Look for the exclamations points (!!!!!) for such markers.
let canvas = document.getElementById("game");
let game = canvas.getContext("2d");
let lastTimestamp = 0;
let playerScore = 0;
const FRAME_RATE = 60;
const FRAME_DURATION = 1000 / FRAME_RATE;
// !!!!! Change/add to this as needed. What other objects or variables will you need for your game idea?
// A score? Different kinds of fallers? Player statistics? It's all up to you!
let fallers = [];
// Check out that cool ES6 feature: default parameter values!
const DEFAULT_DESCENT = 0.0002; // This is per millisecond.
let Faller = function (x, y, width, height, dx = 0, dy = 0, ax = 0, ay = DEFAULT_DESCENT) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
// Velocity.
this.dx = dx;
this.dy = dy;
// Acceleration.
this.ax = ax;
this.ay = ay;
};
Faller.prototype.draw = function () {
game.fillStyle = "blue";
game.fillRect(this.x, this.y, this.width, this.height);
};
Faller.prototype.move = function (millisecondsElapsed) {
//Good old Newtonian physics.
this.x += this.dx * millisecondsElapsed;
this.y += this.dy * millisecondsElapsed;
this.dx += this.ax * millisecondsElapsed;
this.dy += this.ay * millisecondsElapsed;
};
const DEFAULT_PLAYER_WIDTH = 45;
const DEFAULT_PLAYER_HEIGHT = 20;
const DEFAULT_PLAYER_Y = canvas.height - DEFAULT_PLAYER_HEIGHT;
let Player = function (x, y = DEFAULT_PLAYER_Y, width = DEFAULT_PLAYER_WIDTH, height = DEFAULT_PLAYER_HEIGHT) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
};
Player.prototype.draw = function () {
game.fillStyle = "aqua";
game.beginPath();
game.moveTo(this.x - this.width / 2, this.y + this.height);
game.lineTo(this.x - 2, this.y);
game.lineTo(this.x, this.y - 20);
game.lineTo(this.x + 2, this.y);
game.lineTo(this.x + this.width / 2, this.y + this.height);
game.closePath();
game.fill();
};
let player = new Player(canvas.width / 2);
// !!!!! You can treat this function like Khan Academy’s `draw`---just precede all
// drawing instructions with `game.`
let draw = (millisecondsElapsed) => {
game.clearRect(0, 0, canvas.width, canvas.height);
fallers.forEach((faller) => {
faller.draw();
//COLLISION FUNCTION!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
let playerScore = document.getElementById("playerScore");
if (player.x < faller.x + faller.width && player.x + player.width > faller.x &&
player.y < faller.y + faller.height && player.y + player.height > faller.y) {
game.clearRect(0, 0, canvas.width, canvas.height);
};
playerScore += 1;
faller.move(millisecondsElapsed);
});
player.draw();
// Remove fallers that have hit the ground. You might have other reasons to remove fallers.
fallers = fallers.filter((faller) => {
return faller.y < canvas.height;
});
};
// !!!!! This section is modifiable to a degree. It is responsible for generating falling objects at random.
// You don't want to completely eliminate this code, but you may want to revise it to modify the rate/range
// of objects that get generated.
const MIN_WIDTH = 10;
const WIDTH_RANGE = 20;
const MIN_HEIGHT = 10;
const HEIGHT_RANGE = 20;
const MILLISECONDS_BETWEEN_FALLERS = 800;
let fallerGenerator;
let startFallerGenerator = () => {
fallerGenerator = setInterval(() => {
// !!!!! This code looks really repetitive! Hmmmm, what to do...
let fallerWidth = Math.floor(Math.random() * WIDTH_RANGE) + MIN_WIDTH;
fallers.push(new Faller(
Math.floor(Math.random() * (canvas.width - fallerWidth)), 0,
fallerWidth, Math.floor(Math.random() * HEIGHT_RANGE) + MIN_HEIGHT
));
}, MILLISECONDS_BETWEEN_FALLERS);
};
let stopFallerGenerator = () => clearInterval(fallerGenerator);
// !!!!! This section is also modifiable to a degree: it is responsible for moving the "player" around based on
// mouse movement.
let setPlayerPositionBasedOnMouse = (event) => {
player.x = event.clientX / document.body.clientWidth * canvas.width;
};
document.body.addEventListener("mouseenter", setPlayerPositionBasedOnMouse);
document.body.addEventListener("mousemove", setPlayerPositionBasedOnMouse);
// OK, back to the no-touch zone (unless you _really_ know what you’re doing).
let running = false;
let nextFrame = (timestamp) => {
if (!lastTimestamp) {
lastTimestamp = timestamp;
}
if (timestamp - lastTimestamp < FRAME_DURATION) {
if (running) {
window.requestAnimationFrame(nextFrame);
}
return;
}
draw(timestamp - lastTimestamp);
lastTimestamp = timestamp;
if (running) {
window.requestAnimationFrame(nextFrame);
}
};
document.getElementById("start-button").addEventListener("click", () => {
running = true;
lastTimestamp = 0;
startFallerGenerator();
window.requestAnimationFrame(nextFrame);
});
document.getElementById("stop-button").addEventListener("click", () => {
stopFallerGenerator();
running = false;
});
})();
canvas {
border: solid 1px gray;
display: block;
margin-left: auto;
margin-right: auto;
width: 1024px;
}
p {
text-align: center;
}
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Undefined</title>
<link rel="stylesheet" href="falling.css">
</head>
<body>
<h1>Not finished</h1>
<p>
<b>Score:</b> <span id="playerScore">0</span>
<!-- This starter code only contains the area in which the game graphics get drawn.
Feel free to add more elements for displays like scores, lives left, player
statistics, instructions, etc. -->
<canvas id="game" width="1024" height="536">
Sorry, but you need a web browser that supports the
<code>canvas</code> element.
</canvas>
<p>
<button id="start-button">Start</button>
<button id="stop-button">Stop</button>
</p>
<script src="falling.js"></script>
</body>
</html>
You start the game with:
let playerScore = 0;
But in your collision code you have:
let playerScore = document.getElementById("playerScore");
and
playerScore += 1;
Aside from having two variables with the same name (albeit in different scopes), you end up calling += 1 on the HTML element, and never actually write the value of the score to the document for display.

I can't get mouse input in my HTML5 game

I'm trying to get mouse input in my game; any help would be appreciated.
I call the event listeners in my init() function, and I have both my mouseMoved() and mouseClicked() functions. But I just haven't been able to get any response.
(I was asked to make a jsFiddle for this project, so here it is. It's not rendering the images, for some reason. But once there's input, there should be text on the top left the shows the mouse coordinates. Also, when you click on the canvas, you should get an alert.)
var canvasBg = document.getElementById('canvasBg');
var ctxBg = canvasBg.getContext('2d');
var canvasEntities = document.getElementById('entities');
var entitiesCtx = canvasEntities.getContext('2d');
var isPlaying = false;
var player;
var enemy;
var mouseX, mouseY;
var playerImg = new Image();
playerImg.src = 'http://placekitten.com/g/50/50';
var enemyImg = new Image();
enemyImg.src = 'http://placehold.it/50x50';
window.onload = init;
var requestAnimFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
// main functions
function init() {
console.debug('init()');
player = new Entity(250, // xpos
225, // ypos
0, // xd
0, // yd
3, // speed
50, // width
50, // height
playerImg, // imgSrc
true); // player?
enemy = new Entity(500,225,0,0,1,25,25,enemyImg,false);
canvasBg.addEventListener('mousemove', mouseMoved, false);
canvasBg.addEventListener('click', mouseClicked, false);
startLoop();
}
function loop() {
// console.debug('game loop');
if(isPlaying){
update();
draw();
requestAnimFrame(loop);
}
}
function startLoop() {
isPlaying = true;
loop();
}
function stopLoop() {
isPlaying = false;
}
function clearAllCtx() {
ctxBg.clearRect(0, 0, canvasBg.width, canvasBg.height);
Entity.clearCtx();
}
function draw(){
clearAllCtx();
player.draw();
enemy.draw();
}
function update(){
player.update();
}
// end of main functions
// input handling
function mouseMoved(e) {
mouseX = e.layerX - canvasBg.offsetLeft;
mouseY = e.layerY - canvasBg.offsetTop;
document.getElementById('mouseCoors').innerHTML = 'X: ' + mouseX + ' Y: ' + mouseY;
}
function mouseClicked(e) {
alert('You clicked the mouse!');
}
// end of input handling
// Entity functions
function Entity(xpos, ypos, xd, yd, speed, width, height, imagesrc, player) {
this.xpos = xpos;
this.ypos = ypos;
this.xd = xd;
this.yd = yd;
this.speed = speed;
this.width = width;
this.height = height;
this.imagesrc = imagesrc;
this.player = player;
}
Entity.clearCtx = function(){
entitiesCtx.clearRect(0,0,canvasBg.width,canvasBg.height);
};
Entity.prototype.draw = function () {
entitiesCtx.drawImage(this.imagesrc, this.xpos, this.ypos);
};
Entity.prototype.update = function () {
this.xpos += this.xd;
this.ypos -= this.yd;
};
// end of Entity functions
So theres a few things going on, first the fiddle loading was set incorrectly, once I changed it to no wrap in body everything works.
The actual issue you're having is due to the background canvas being under the entities canvas, so it cant get any of the mouse events.
One solution would be to use pointer-events and set it to none like so pointer-events: none on the entities canvas.
Live Demo
#entities {
margin: -500px auto;
pointer-events: none;
}
Another option if you need wider browser support is to have the entities canvas capture the mouse events instead.
Live Demo of Option 2

Categories

Resources