Hey guy's I'm making a snake game in JS and right now all I'm trying to do is draw a snake in the center of the canvas. I've set the canvas dimensions to the board dimensions so everything scales right but nothing shows up. Any help would be appreciated:)
//declare global variables
const canvas = document.querySelector('#canvas');
//set canvas context
const ctx = canvas.getContext('2d');
//set canvas dimensions to board dimensions
canvas.width = 768;
canvas.height = 512;
//put canvas dimensions into variables
const cvsW = canvas.width;
const cvsH = canvas.height;
//create snake unit
const unit = 16;
//create snake and set starting position
let snake = [{
x : cvsW/2,
y : cvsH/2
}]
ctx.fillStyle = 'limegreen';
ctx.fillRect(snake.x, snake.y, unit, unit);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Snake</title>
<style>
body {
background-color: #333;
}
#canvas {
background-color: #4d4d4d;
display: block;
margin: auto;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
</style>
</head>
<body>
<canvas id="canvas" width="768" height="512"></canvas>
<script src="script.js"></script>
</body>
</html>
This is happening because your snake is an array of objects. You either need to turn this into a single object for your code to work or use an index to select the object inside.
ctx.fillRect(snake[0].x-unit/2, snake[0].y-unit/2, unit, unit);
Also, note that in order to correctly centre your snake you need to subtract the unit/2 from both x and y coordinates.
You can also remove the setting of the canvas dimensions within your code, as this is set when your define the height and width attributes on your canvas element.
See working example below:
//declare global variables
const canvas = document.querySelector('#canvas');
//set canvas context
const ctx = canvas.getContext('2d');
//put canvas dimensions into variables
const cvsW = canvas.width;
const cvsH = canvas.height;
//create snake unit
const unit = 16;
//create snake and set starting position
let snake = [{
x: cvsW / 2,
y: cvsH / 2
}];
ctx.fillStyle = 'lime';
ctx.fillRect(snake[0].x - unit / 2, snake[0].y - unit / 2, unit, unit);
body {
background-color: #333;
}
#canvas {
background-color: #4d4d4d;
display: block;
margin: auto;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
<canvas id="canvas" width="768" height="512"></canvas>
Related
I'm trying to make a little game to play around with canvas. I've decided to go for Flappy Bird as there are plenty of examples online. My goal was to make the game "responsive" for all devices. If you open it in a mobile or whichever screen, you should be able to play it normally.
For this purpose I drew a background image within the canvas that will adapt the height to the screen's height and repeat it through the x-axis.
The issue I'm having is that, when I animate the bird, this one leaves a trail behind it.
As like so:
An easy fix would be clearing the canvas as explained in this post:
Canvas image leaves weird trail when moving left
But it isn't possible because of the way I'm painting my background (I paint it once and repaint it if it resizes).
Question
How do I avoid this image trail with a static background? Is it maybe possible to have like a false static background that doesn't need to be repainted?
I obviously want to do it in the canvas, without needing to have an image in the html.
This is the code with the main functionality so that you can take a look:
const cvs = document.getElementById("canvas");
const ctx = cvs.getContext("2d");
cvs.style.height = "100vh";
cvs.style.width = "100vw";
var bird = new Image();
var bg = new Image();
bird.src = "https://lh3.googleusercontent.com/prntPuix7QxlhKXx6IBtTxv7PrdEJtmzFJ4mopScNS1_klze86BVNy3PqHbQn2UQ4JyJdix3XQ=w128-h128-e365";
bg.src = "https://opengameart.org/sites/default/files/bg_5.png";
let gravity = 1;
birdPositionY = 10;
birdPositionX = 50;
const draw = () => {
ctx.drawImage(bird, birdPositionX, birdPositionY);
birdPositionY += gravity;
birdPositionX += 3;
requestAnimationFrame(draw);
};
const resizeCanvas = () => {
const {
width,
height
} = cvs.getBoundingClientRect();
cvs.height = height;
cvs.width = width;
let x = 0,
y = 0;
while (x < cvs.width && y < cvs.height) {
ctx.drawImage(bg, x, y, 360, cvs.height);
x += bg.width;
}
};
bg.onload = () => {
resizeCanvas();
};
window.addEventListener("resize", resizeCanvas, false);
draw();
<!DOCTYPE html>
<html>
<head>
<title>Flappy Bird</title>
<meta name="viewport" content="width=device-width, user-scalable=no" />
<style>
html {
overflow: hidden;
}
body {
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="index.js"></script>
</body>
</html>
Hope it did make sense!
Thanks in advance!
Draw your background on a canvas that you will keep off-screen, then draw this canvas every frame on your visible one before you draw your moving parts.
const cvs = document.getElementById("canvas");
const ctx = cvs.getContext("2d");
// our backround canvas that we won't append in the doc ("off-screen")
const background = cvs.cloneNode();
const bg_ctx = background.getContext('2d');
cvs.style.height = "100vh";
cvs.style.width = "100vw";
var bird = new Image();
var bg = new Image();
bird.src = "https://lh3.googleusercontent.com/prntPuix7QxlhKXx6IBtTxv7PrdEJtmzFJ4mopScNS1_klze86BVNy3PqHbQn2UQ4JyJdix3XQ=w128-h128-e365";
bg.src = "https://opengameart.org/sites/default/files/bg_5.png";
let gravity = 1;
birdPositionY = 10;
birdPositionX = 50;
const draw = () => {
// first draw the background canvas
ctx.drawImage(background, 0,0);
// then your persona
ctx.drawImage(bird, birdPositionX, birdPositionY);
birdPositionY += gravity;
birdPositionX += 3;
requestAnimationFrame(draw);
};
const resizeCanvas = () => {
const {
width,
height
} = cvs.getBoundingClientRect();
// resize both canvases
cvs.height = background.height = height;
cvs.width = background.width = width;
let x = 0,
y = 0;
while (x < cvs.width && y < cvs.height) {
// draw on the off-screen canvas
bg_ctx.drawImage(bg, x, y, 360, cvs.height);
x += bg.width;
}
};
bg.onload = () => {
resizeCanvas();
draw();
};
window.addEventListener("resize", resizeCanvas, false);
<!DOCTYPE html>
<html>
<head>
<title>Flappy Bird</title>
<meta name="viewport" content="width=device-width, user-scalable=no" />
<style>
html {
overflow: hidden;
}
body {
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="index.js"></script>
</body>
</html>
I think you could just call resizeCanvas in your draw loop to redraw the background.
EDIT: Apparently that does not work. Another option is just drawing it in the draw loop like this:
const draw = () => {
ctx.clearRect(0, 0, cvs.width, cvs.height)
let x = 0, y = 0;
while (x < cvs.width && y < cvs.height) {
ctx.drawImage(bg, x, y, 360, cvs.height);
x += bg.width;
}
ctx.drawImage(bird, birdPositionX, birdPositionY);
birdPositionY += gravity;
birdPositionX += 3;
requestAnimationFrame(draw);
};
Edit: That did not work either. The other option would be to make two canvases and overlap them with one drawing the background and one drawing the bird.
I am trying to program a game in html and javascript, for this I decided to use a canvas, as it will make the development easier. However, when I try to implement another object into the canvas, the code seems to break, I have been fiddling with the code, in order to see the issue, however, I have had no success so far. The code can be found below:
<!DOCTYPE html>
<html>
<head>
<Title>Game Prototype 1</Title>
</head>
<body onload= "startGame()">
<canvas id="canvas"
style =
"border:1px solid #000000;
padding-left: 0;
padding-right: 0;
margin-top: 65px;
margin-left: auto;
margin-right: auto;
display: block;">
</canvas>
<script>
var character;
function startGame(){
gameArea.start();
character = new character(30, 30, "red", 10, 120);
}
var gameArea = {
canvas:
document.getElementById("canvas");
start : function() {
this.canvas.width = 1000;
this.canvas.height = 800;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas,document.body.childNodes[0]);
}
function character(width, height, color, x, y) {
this.width = width;
this.height = height;
this.x = x;
this.y = y;
ctx = gameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.width, this.height, this.x, this.y);
}
</script>
</body>
</html>
The output of the code is this:
At this point I am no longer sure whether the issue is which the character variable, and hence the character is not being displayed, or with the canvas, as the canvas has incorrect dimensions, I am not entirely sure why the canvas changes the dimensions (if the rectangle displayed on the screen is the canvas). This is what I mean by the code breaking.
You have some syntax errors in the gameArea object. (A } was missing, and in Javascript you use , instead of ; to separate members within an object.)
I recommend using the browser console to spot such issues. Here is your code with the object fixed:
<!DOCTYPE html>
<html>
<head>
<Title>Game Prototype 1</Title>
</head>
<body onload= "startGame()">
<canvas id="canvas"
style =
"border:1px solid #000000;
padding-left: 0;
padding-right: 0;
margin-top: 65px;
margin-left: auto;
margin-right: auto;
display: block;">
</canvas>
<script>
var character;
function startGame(){
gameArea.start();
character = new character(30, 30, "red", 10, 120);
}
var gameArea = {
canvas: document.getElementById("canvas"),
start: function() {
this.canvas.width = 1000;
this.canvas.height = 800;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas,document.body.childNodes[0]);
}
}
function character(width, height, color, x, y) {
this.width = width;
this.height = height;
this.x = x;
this.y = y;
ctx = gameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.width, this.height, this.x, this.y);
}
</script>
</body>
</html>
Did this solve your problem or were you expecting something else to happen?
I would like the rectangle to move on the canvas and not copy every time.
It draws it but then the rectangle stays there.
I am a beginner with the canvas so if it is an epic fail then be prepared.
The codepen is at LINK.
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var boxWidth = 50;
var boxHeight = 50;
var bX = WIDTH / 2 - boxWidth / 2;
var bY = HEIGHT / 2 - boxHeight / 2;
function render() {
ctx.fillStyle = "white";
ctx.rect(bX, bY, boxWidth, boxHeight);
ctx.fill();
}
function control() {
bX++;
}
function main() {
control();
render();
}
main();
var run = setInterval(main, 10)
canvas {
width: 400px;
height: 400px;
background-color: black;
}
div {
text-align: center;
}
<div>
<canvas width="400px" height="400px" background-color="black" id="canvas"></canvas>
</div>
Repaint your canvas before drawing the rectangle each time - think about it. Its all done in layers.
The rectangle is "staying there" because you aren't replacing the rectangle your drew last time.
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var boxWidth = 50;
var boxHeight = 50;
var bX = WIDTH / 2 - boxWidth / 2;
var bY = HEIGHT / 2 - boxHeight / 2;
function render() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); //use clear rect instead
ctx.fillStyle = "white";
ctx.fillRect(bX, bY, boxWidth, boxHeight); //use fillRect instead of fill()
}
function control() {
bX++;
}
(function main() {
control();
render();
requestAnimationFrame(()=>main());
})()
canvas {
width: 400px;
height: 400px;
background-color: black;
}
div {
text-align: center;
}
<html>
<head></head>
<body>
<div>
<canvas width="400px" height="400px" background-color="black" id="canvas"></canvas>
</div>
</body>
</html>
Also have a look at the requestAnimationFrame() method as opposed to setInterval - it syncs up with the window's javascript timer and is less likely to cause problems.
I want to use putImageData to paint on canvas. But for some reason the painted pixels are blurry and I don't know why. Here is a minimal example:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<style>
html, body, canvas { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; }
</style>
</head>
<body oncontextmenu="return false;">
<canvas id='canvas'></canvas>
<script type='text/javascript'>
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
let uarr = new Uint8ClampedArray(4);
uarr[0] = 0;
uarr[1] = 0;
uarr[2] = 0;
uarr[3] = 255;
let imgData = new ImageData(uarr , 1, 1);
ctx.putImageData(imgData, 20, 20);
</script>
</body>
</html>
This should paint one black pixel at the 20,20 coordinates, but for some reason it look blurry.
How can I force to draw sharp edges?
You need to set the canvas width and height attributes, as your CSS is stretching a default size canvas to the size of the screen, like stretching a too small image. After that ctx.imageSmoothingEnabled should work fine.
var canvas = document.getElementById('canvas');
// This will stretch correctly
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
var uarr = new Uint8ClampedArray(4);
uarr[0] = 0;
uarr[1] = 0;
uarr[2] = 0;
uarr[3] = 255;
var imgData = new ImageData(uarr , 1, 1);
ctx.putImageData(imgData, 20, 20);
html, body, canvas { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; }
<canvas id='canvas'></canvas>
As #Kaido mentions in the comments below, imageSmoothingEnabled does not really work that way. Check his fiddle for a little bit more of an in-depth look at what it can be used for.
I am trying to display simple text on the screen via update() and setInterval() functions, but it's not working. Note that I create a canvas on which I want the text to be displayed. Please , check the code below.
var canvas, canvasContext;
window.onload = function() {
canvas = document.getElementById('theCanvas');
canvasContext = canvas.getContext('2d');
var fps = 30;
setInterval(update, 1000 / fps);
}
function update() {
drawText();
}
function drawText() {
canvasContext.fillStyle = 'black';
canvasContext.font = 'bold 40px Monospace';
canvasContext.fillText('POC ......', 150, 150);
}
#canvasHolder {
position: absolute;
left: 0px;
top: 0px;
}
#theCanvas {
background-color: lightgreen;
width: 100pc;
height: 100pc;
}
<div id="canvasHolder">
<canvas id="theCanvas"></canvas>
</div>
The issue is that the canvas is being stretched to fill the element. Both the canvas itself and the canvas element have a size. If you don't specify the size of the canvas (you haven't), it defaults to 300x150. When the size of the element and the canvas don't match, the canvas content is scaled to fit into the element.. So the text is being drawn, it's just really big; scroll to the right and down and you'll find it.
My guess is that you meant for the canvas and the element to be the same size. If so, you need to set the size of the canvas. You can do that with width and height attributes on the canvas element, but those values will be in pixels, not picas as in your CSS. So we do it dynamically by getting the computed width and height of the element (because they'll come back to us in pixels), and then setting those values for the width and height attributes of the canvas:
// Set the canvas size to match the element size
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
Updated snippet:
var canvas, canvasContext;
window.onload = function() {
canvas = document.getElementById('theCanvas');
// Set the canvas size to match the element size
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
canvasContext = canvas.getContext('2d');
canvasContext.canvas.width = canvas.width;
canvasContext.canvas.height = canvas.height;
var fps = 30;
setInterval(update, 1000 / fps);
};
function update() {
drawText();
}
function drawText() {
canvasContext.fillStyle = 'black';
canvasContext.font = 'bold 40px Monospace';
canvasContext.fillText('POC ......', 150, 150);
}
#canvasHolder {
position: absolute;
left: 0px;
top: 0px;
}
#theCanvas {
background-color: lightgreen;
width: 100pc;
height: 100pc;
}
<div id="canvasHolder">
<canvas id="theCanvas"></canvas>
</div>
Note that right now, you're just redrawing it at the same location every time, but I assume moving it was your next task...
The text is already rendered but if you want to animate your text then you have to change your draw function because each time you are drawing your text in the same position -
function drawText() {
canvasContext.fillStyle = 'black';
canvasContext.font = 'bold 40px Monospace';
canvasContext.fillText('POC ......', 150, 150); //here you have given the fixed position.
}
and you have to clear your canvas before rendering your text in new position-
please refer -
How to clear the canvas for redrawing