Related
I've been looking for some time now how to detect collisions on a tilemap between my player and the box specified in my table, but all I found are advanced tutorials, I'm trying to do this as simply as possible so that I can understand how it works too.
In my table, I therefore seek to detect a collision only if the player walks on a box of value 1 (this would be a wall for example). Then the player will not be able to move on this place of my map.
My code:
// Initi
ctx = null;
var ctx = document.getElementById("canvas").getContext("2d");
// Map
var gameMap = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 0,
0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
0, 1, 0, 1, 0, 0, 0, 1, 1, 0,
0, 1, 0, 1, 0, 1, 0, 0, 1, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
0, 1, 0, 0, 0, 0, 0, 1, 0, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
var tileW = 40,
tileH = 40;
var mapW = 10,
mapH = 10;
window.onload = function() {
requestAnimationFrame(drawGame);
ctx.font = "bold 10pt sans-serif";
};
// Player
var x = 100;
var y = 100;
var radius = 10;
var upPressed = false;
var downPressed = false;
var leftPressed = false;
var rightPressed = false;
var speed = 1;
function drawPlayer() {
ctx.fillStyle = "green";
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2)
ctx.fill();
}
// Inputs
function inputs() {
if (upPressed) {
y = y - speed;
}
if (downPressed) {
y = y + speed;
}
if (leftPressed) {
x = x - speed;
}
if (rightPressed) {
x = x + speed;
}
}
document.body.addEventListener("keydown", keyDown)
document.body.addEventListener("keyup", keyUp)
function keyDown(event) {
if (event.keyCode == 38) {
upPressed = true;
}
if (event.keyCode == 40) {
downPressed = true;
}
if (event.keyCode == 37) {
leftPressed = true;
}
if (event.keyCode == 39) {
rightPressed = true;
}
if (event.keyCode == 65) {
speedCodePressed = true;
speed = 20;
}
if (event.keyCode == 32) {
shootPressed = true;
}
}
function keyUp(event) {
if (event.keyCode == 38) {
upPressed = false;
}
if (event.keyCode == 40) {
downPressed = false;
}
if (event.keyCode == 37) {
leftPressed = false;
}
if (event.keyCode == 39) {
rightPressed = false;
}
if (event.keyCode == 32) {
shootPressed = false;
}
}
// game map draw function
function drawMap() {
if (ctx == null) {
return;
}
for (var y = 0; y < mapH; ++y) {
for (var x = 0; x < mapW; ++x) {
switch (gameMap[((y * mapW) + x)]) {
case 0:
ctx.fillStyle = "#685b48";
break;
default:
ctx.fillStyle = "#5aa457";
}
ctx.fillRect(x * tileW, y * tileH, tileW, tileH);
}
}
}
// clear screen
function clearScreen() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// game loop
function drawGame() {
requestAnimationFrame(drawGame);
clearScreen();
drawMap();
drawPlayer();
inputs();
}
<canvas id="canvas"></canvas>
I won't go into too much detail, as I think it's pretty straightforward, but I'm a beginner and really have no idea.
See the changes below...
I added canvas.height = tileH * mapH same for width to match the real size of the game.
Created a new object var player = { x: 100, y: 100 , radius: 10, speed: 1 } you should keep everything related to the player in that object
I'm using Path2D to create the structure that we draw (walls) and a path that we use for the collisions
The collisions are detected with isPointInPath read more here: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInPath
I also changed the gameMap to a 2 dimensional array its makes everything easier now that we are using the Path2D, not really required but I like it better that way.
var canvas = document.getElementById("canvas")
var tileW = 40
var tileH = 40
var mapW = 10
var mapH = 10
var ctx = canvas.getContext("2d");
var upPressed = false;
var downPressed = false;
var leftPressed = false;
var rightPressed = false
var player = { x: 100, y: 100, radius: 10, speed: 1 }
var gameMap = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 1, 0, 0, 0, 1, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 0, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
var path = new Path2D()
var walls = new Path2D()
window.onload = function() {
canvas.height = tileH * mapH
canvas.width = tileW * mapW
for (var y = 0; y < mapH; ++y) {
for (var x = 0; x < mapW; ++x) {
if (gameMap[y][x]) {
path.rect(x * tileW- player.radius, y * tileH- player.radius, tileW + player.radius*2, tileH + player.radius*2)
walls.rect(x * tileW, y * tileH, tileW, tileH)
}
}
}
requestAnimationFrame(drawGame);
};
function drawPlayer() {
ctx.fillStyle = "green";
ctx.beginPath();
ctx.arc(player.x, player.y, player.radius, 0, Math.PI * 2)
ctx.fill();
}
function inputs() {
var newx = player.x
var newy = player.y
if (upPressed) newy = player.y - player.speed;
if (downPressed) newy = player.y + player.speed;
if (leftPressed) newx = player.x - player.speed;
if (rightPressed) newx = player.x + player.speed;
if (!ctx.isPointInPath(path, newx, newy)) {
player.x = newx;
player.y = newy;
}
}
document.body.addEventListener("keydown", keyDown)
document.body.addEventListener("keyup", keyUp)
function keyDown(event) {
if (event.keyCode == 38) upPressed = true;
if (event.keyCode == 40) downPressed = true;
if (event.keyCode == 37) leftPressed = true;
if (event.keyCode == 39) rightPressed = true;
}
function keyUp(event) {
if (event.keyCode == 38) upPressed = false;
if (event.keyCode == 40) downPressed = false;
if (event.keyCode == 37) leftPressed = false;
if (event.keyCode == 39) rightPressed = false;
}
function drawGame() {
ctx.fillStyle = "#685b48"
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#5aa457"
ctx.fill(walls)
//ctx.stroke(path);
drawPlayer();
inputs();
requestAnimationFrame(drawGame);
}
<canvas id="canvas"></canvas>
Solution:
Check if the new position is not 1 in the game map.
If it's 1 do nothing.
If it's not 1 assign position
Calculating position:
Math.floor(y / tileH) // y
Math.floor(x / tileW) // x
Actual code:
function inputs() {
let newX = x
let newY = y
if(upPressed) {
newY -= speed
}
if(downPressed) {
newY += speed
}
if(leftPressed) {
newX -= speed
}
if(rightPressed) {
newX += speed
}
if (gameMap[Math.floor(newY / tileH)][Math.floor(newX / tileW)] !== 1) {
x = newX
y = newY
}
}
One way to solve this is by changing your game map from a 1 dimensional array to a 2 dimensional array.
So instead of:
var gameMap = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 0,
0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
0, 1, 0, 1, 0, 0, 0, 1, 1, 0,
0, 1, 0, 1, 0, 1, 0, 0, 1, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
0, 1, 0, 0, 0, 0, 0, 1, 0, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
Make it:
let gameMap = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 0, 1, 0, 0, 0, 1, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 0, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
Or however you want to structure your game's map.
Then once you have this 2D array, keep track of the row and column index of where your player is currently located.
let player_index_x = 3;
let player_index_y = 5;
Update this index whenever the player changes locations; e.g if the player moves up 1, then you subtract 1 from the y index.
If the player moves right 1, add one to the x index.
Then collision detection becomes a lot more straightforward because before moving left, right, up, or down, you can check something like:
if(left_pressed)
{
// make sure that it is indeed possible to move left
if(player_index_x > 1)
{
if(gameMap[player_index_x - 1][player_index_y] == 1)
{
// collision detected, do not move, return if in function
}
else
{
// move player
player_index_x -= 1;
}
}
}
My Recommendations:
Be sure to check first whether or not the move is a valid one, so the player does not fall off the map
If a collision occurs, what should happen? If a collision doesn't occur, what should happen? I recommend writing down a list of your assumptions while coding and checking them as you go. Especially in collision detection, it can be very easy to have unintended bugs from unchecked assumptions.
Resources for Learning to do this:
How can I create a two dimensional array in JavaScript?
Now I have a simple code without a main game loop. I'm rendering the sprites on the canvas using the Loader constructor and its image.onload function (because without image.onload I won't see any sprites) Now I want to animate on of my sprites and for that I need to create a draw loop. Unfortunately, this is where my knowledge ends. I tried creating render function and just copy pasting my ship.drawimage(boat, boatPosX, boatPosY, 50, 50); methods what so ever, but it's not working because I need image.onload function which is inside Loader. And I can't put Loader constructor to my render() function because then var background = new Loader("ground.png");
var boat = new Loader("ship.png"); can't access the constructor variable to init new object.
So at this point I'm pretty lost how I should refactor my code better?
Here is the full code:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var mapArray = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 2, 2, 0],
[0, 0, 1, 1, 1, 0, 0, 2, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
var StyleSheet = function(image, width, height) {
this.image = image;
this.width = width;
this.height = height;
this.draw = function(image, sx, sy, swidth, sheight, x, y, width, height) {
context.drawImage(image, sx, sy, swidth, sheight,x, y, width, height);
}
this.drawimage = function(image, x, y, width, height) {
context.drawImage(image, x, y, width, height);
}
}
/* Initial Sprite Position */
var boatPosX = 230;
var boatPosY = 200;
var Loader = function(src) {
this.image = new Image();
this.image.src = src;
this.image.onload = function() {
var sprite = new StyleSheet(background, 36, 36);
var ship = new StyleSheet(boat, 90, 100);
for (let i = 0; i < mapArray.length; i++) {
for (let j = 0; j < mapArray[i].length; j++) {
if (mapArray[i][j] == 0) {
sprite.draw(background, 190, 230, 26, 26, i * sprite.width, j * sprite.height, sprite.width, sprite.height);
}
if (mapArray[i][j] == 1) {
sprite.draw(background, 30, 30, 26, 26, i * sprite.width, j * sprite.height, sprite.width, sprite.height);
}
if (mapArray[i][j] == 2) {
sprite.draw(background, 200, 20, 26, 26, i * sprite.width, j * sprite.height, sprite.width, sprite.height);
}
}
}
ship.drawimage(boat, boatPosX, boatPosY, 50, 50);
}
return this.image;
}
function render() {
}
setInterval(render, 10);
/* Sprite controls */
function move(e) {
if (e.keyCode == 39) {
boatPosX += 2;
console.log("works");
}
if (e.keyCode == 37) {
boatPosX -= 2;
}
}
document.onkeydown = move;
var background = new Loader("ground.png");
var boat = new Loader("ship.png");
console.log(background);
UPDATE:
So following my old questions, I decided to do some changes to my code so that I would be allowed to call requestAnimationFrame for my onload function and draw the sprite on the canvas constantly. For that I separated Loader constructor and my method onload by putting onload into a new function and assigning that function to Loader prototype. Then I do var background = new Loader("ground.png"); and background.render(); but I get Uncaught TypeError: background.render is not a function error. Not sure what I'm doing wrong?
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var mapArray = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 2, 2, 0],
[0, 0, 1, 1, 1, 0, 0, 2, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
var StyleSheet = function(image, width, height) {
this.image = image;
this.width = width;
this.height = height;
this.draw = function(image, sx, sy, swidth, sheight, x, y, width, height) {
context.drawImage(image, sx, sy, swidth, sheight,x, y, width, height);
}
this.drawimage = function(image, x, y, width, height) {
context.drawImage(image, x, y, width, height);
}
}
/* Initial Sprite Position */
var boatPosX = 230;
var boatPosY = 200;
var Loader = function(src) {
this.image = new Image();
this.image.src = src;
return this.image;
}
Loader.prototype.render = function() {
this.image.onload = function() {
var sprite = new StyleSheet(background, 36, 36);
var ship = new StyleSheet(boat, 90, 100);
for (let i = 0; i < mapArray.length; i++) {
for (let j = 0; j < mapArray[i].length; j++) {
if (mapArray[i][j] == 0) {
sprite.draw(background, 190, 230, 26, 26, i * sprite.width, j * sprite.height, sprite.width, sprite.height);
}
if (mapArray[i][j] == 1) {
sprite.draw(background, 30, 30, 26, 26, i * sprite.width, j * sprite.height, sprite.width, sprite.height);
}
if (mapArray[i][j] == 2) {
sprite.draw(background, 200, 20, 26, 26, i * sprite.width, j * sprite.height, sprite.width, sprite.height);
}
}
}
ship.drawimage(boat, boatPosX, boatPosY, 50, 50);
}
}
/* Sprite controls */
function move(e) {
if (e.keyCode == 39) {
boatPosX += 2;
console.log("works");
}
if (e.keyCode == 37) {
boatPosX -= 2;
}
}
document.onkeydown = move;
var background = new Loader("ground.png");
var boat = new Loader("ship.png");
background.render();
console.log(background);
Codepen example: https://codepen.io/Limpuls/pen/dejVpR
I made a fork of your Pen and refactored your code. Now you have a game loop using requestAnimationFrame: https://codepen.io/DonKarlssonSan/pen/rvrGvL/
Edit, adding what I changed:
I removed the Loader and instead just use plain Image and img.src = url which I then pass in as parameters to the constructor to StyleSheet.
I extracted the loop over mapArray into a render method which also functions as the main ("game") loop.
Code:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var mapArray = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 2, 2, 0],
[0, 0, 1, 1, 1, 0, 0, 2, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
var StyleSheet = function(image, width, height) {
this.image = image;
this.width = width;
this.height = height;
this.draw = function(image, sx, sy, swidth, sheight, x, y, width, height) {
context.drawImage(image, sx, sy, swidth, sheight, x, y, width, height);
};
this.drawimage = function(image, x, y, width, height) {
context.drawImage(image, x, y, width, height);
};
};
/* Initial Sprite Position */
var boatPosX = 230;
var boatPosY = 200;
function render() {
requestAnimationFrame(render);
for (let i = 0; i < mapArray.length; i++) {
for (let j = 0; j < mapArray[i].length; j++) {
if (mapArray[i][j] == 0) {
this.sprite.draw(
background,
190,
230,
26,
26,
i * this.sprite.width,
j * this.sprite.height,
this.sprite.width,
this.sprite.height
);
}
if (mapArray[i][j] == 1) {
this.sprite.draw(
background,
30,
30,
26,
26,
i * this.sprite.width,
j * this.sprite.height,
this.sprite.width,
this.sprite.height
);
}
if (mapArray[i][j] == 2) {
this.sprite.draw(
background,
200,
20,
26,
26,
i * this.sprite.width,
j * this.sprite.height,
this.sprite.width,
this.sprite.height
);
}
}
}
this.ship.drawimage(boat, boatPosX, boatPosY, 50, 50);
};
function move(e) {
if (e.keyCode == 39) {
boatPosX += 2;
console.log("right");
}
if (e.keyCode == 37) {
boatPosX -= 2;
console.log("left");
}
}
document.onkeydown = move;
var background = new Image();
background.src = "http://i67.tinypic.com/35lx8y0.png";
var sprite = new StyleSheet(background, 36, 36);
var boat = new Image();
boat.src = "http://i66.tinypic.com/b7b9tc.png";
var ship = new StyleSheet(boat, 90, 100);
render();
Tried changing the fillStyle color to many different ones, also different positions but nothing. No errors in console either. I already have tileset and sprites drawn on the canvas, does that has anything to do with it? I just need to print out a simple text on every character move on key press.
Here is the code:
function move(e) {
if (e.keyCode == 39) {
boatPosX += 5;
view.x -= 5
moveCount++;
context.fillStyle = "red";
context.fillText(theArray[0].question, 0, 0);
console.log(theArray[0].question);
}
The rest of the code:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var view = {x: 0, y: 0};
var questionsArray = [];
var moveCount = 0;
var mapArray = [
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 2, 2, 0],
[0, 0, 1, 1, 1, 0, 0, 2, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 2, 2, 0],
[0, 0, 1, 1, 1, 0, 0, 2, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
function isPositionWall(ptX, ptY) {
var gridX = Math.floor(ptX / 36)
var gridY = Math.floor(ptY / 36)
if(gridX < 0 || gridX >= mapArray[0].length)
return true;
if(gridY < 0 || gridY >= mapArray.length)
return true;
return mapArray[gridX][gridY];
}
var theArray = [];
var Question = function(question, answer1, answer2, correctAnswer) {
this.question = question;
this.answer1 = answer1;
this.answer2 = answer2;
this.correctAnswer = correctAnswer;
this.addToArray = function(){
theArray.push(this);
};
this.addToArray();
}
Question.prototype.checkAnswer = function() {
return answer1 || answer2 == correctAnswer;
}
var question1 = new Question("Taip ar ne?", "Taip", "Ne", "Taip");
var question2 = new Question("Jo ar ne?", "Ne", "Jo", "Jo");
var question3 = new Question("Aha ar ne?", "Aha", "Ne", "Ne");
var question4 = new Question("Ja ar ne?", "Taip", "Ne", "Taip");
var question5 = new Question("Jojo ar ne?", "Taip", "Ne", "Taip");
var question6 = new Question("Taip ar ne?", "Taip", "Ne", "Taip");
var question7 = new Question("Taip ar ne?", "Taip", "Ne", "Taip");
var StyleSheet = function(image, width, height, x, y) {
this.image = image;
this.width = width;
this.height = height;
this.x = x;
this.y = y
this.draw = function(image, sx, sy, swidth, sheight, x, y, width, height) {
context.drawImage(image, sx, sy, swidth, sheight, x, y, width, height);
};
this.drawimage = function(image, x, y, width, height) {
context.drawImage(image, x, y, width, height);
};
};
/* Initial Sprite Position */
var boatPosX = canvas.height/2 - 50;
var boatPosY = canvas.height/2 - 50;
function render(viewport) {
context.save();
context.translate(view.x, view.y);
requestAnimationFrame(render);
var oldPosX = boatPosX;
var oldPosY = boatPosY;
for (let i = 0; i < mapArray.length; i++) {
for (let j = 0; j < mapArray[i].length; j++) {
if (mapArray[i][j] == 0) {
this.sprite.draw(
background,
190,
230,
26,
26,
i * this.sprite.width,
j * this.sprite.height,
this.sprite.width,
this.sprite.height
);
}
if (mapArray[i][j] == 1) {
this.sprite.draw(
background,
30,
30,
26,
26,
i * this.sprite.width,
j * this.sprite.height,
this.sprite.width,
this.sprite.height
);
}
if (mapArray[i][j] == 2) {
this.sprite.draw(
background,
200,
20,
26,
26,
i * this.sprite.width,
j * this.sprite.height,
this.sprite.width,
this.sprite.height
);
}
}
}
this.ship.drawimage(boat, boatPosX, boatPosY, 50, 50);
//console.log(boatPosX + ship.width)
if(isPositionWall(boatPosX, boatPosY)) {
boatPosX = oldPosY;
console.log("collision");
}
context.restore();
};
function move(e) {
if (e.keyCode == 39) {
boatPosX += 5;
//canvas.width += 2;
view.x -= 5
moveCount++;
console.log(moveCount);
console.log("right");
context.fillStyle = "red";
context.fillText(theArray[0].question, 0, 0);
console.log(theArray[0].question);
}
if (e.keyCode == 37) {
boatPosX -= 5;
view.x += 5
moveCount++;
console.log(moveCount);
console.log("left");
}
if (e.keyCode == 38) {
boatPosY -= 5;
view.Y += 5
moveCount++;
console.log(moveCount);
console.log("up");
}
if (e.keyCode == 40) {
boatPosY += 5;
view.Y += 5
moveCount++;
console.log(moveCount);
console.log("down");
}
}
document.onkeydown = move;
var background = new Image();
background.src = "ground.png";
var sprite = new StyleSheet(background, 36, 36, 16, 16);
var boat = new Image();
boat.src = "ship.png";
var ship = new StyleSheet(boat, 90, 100, 16, 16);
console.log(Math.floor(boatPosX / 36));
console.log(mapArray[Math.floor(boatPosX / 36)]);
render();
Seems to be because you are trying to put the text at 0,0. By default the text will be drawn above the Y position of 0 (so off the top of the canvas which is why you don't see anything). If you made it 0,10 or 0,20 then you will probably see some text.
It is possible to change the text base line like this, so at 0,0 you will see something..
context.textBaseline = "top";
I'm working on a tetris game - still - and am trying to use requestAnimationFrame to draw my T piece on the black board.
This is the problem. the requestAnimationFrame draws the piece 2 times, then stops drawing even though the for loop is still running. That is, after two times, I only see the black background. When I comment out the black background the piece shows up/animates just fine.
I really am at a loss why this is happening.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
const T = [
[
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0]
],
[
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 1, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0]
],
[
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
],
[
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 1, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0]
],
]
var piece = T[0];
const player = {
position: {x: 5, y: -1},
piece: piece,
}
function colorPiece(piece, offset) {
for(y = 0; y < piece.length; y++) {
for(x = 0; x < piece.length; x++) {
if (piece[y][x] !== 0) {
ctx.fillStyle = "red";
ctx.fillRect(x + offset.x, y + offset.y, 1, 1);
}
}
}
}
function drawCanvas() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.scale(20, 20);
colorPiece(player.piece, player.position);
}
function update() {
drawCanvas();
requestAnimationFrame(update);
}
update();
OK - working version, with a fiddle here. A number of changes. The biggest are:
Don't use canvas.scale(), since it's cumulative per this (see "More Examples"). Instead, use 20*x and 20*y for blocks 20x20.
Edit Based on a further test, it looks like this was the most significant change.
Rename so that piece is not used as all of a variable, a field name in player, and a parameter of colorPiece
Move the ctx creation into update() (now called doUpdate()) per this fiddle example. Pass ctx as a parameter to other functions.
Move the red fillStyle assignment out of the loop, since you only need to do it once, and then you can draw all the rectangles without changing it.
In the loops:
for(var y = 0; y < thePiece.length; ++y) {
for(var x = 0; x < thePiece[y].length; ++x) { ... } }
Keep x and y in the local scope, using var.
When you are ready to go across a row, that's thePiece[y].length, i.e., the length of the row. Using thePiece.length there would have broken for non-square elements of T.
Added a <p id="log"/> and a javascript framenum so that I could see that doUpdate() was indeed being called.
If you haven't already, make sure to open the console while you're testing so you can see error messages. If drawCanvas causes an error, it will prevent requestAnimationFrame from being called again. I think that's why the fiddle I linked above calls requestAnimationFrame at the beginning rather than the end of the frame-draw function.
Hope this helps!
Code
const T = [
[
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0]
],
[
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 1, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0]
],
[
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
],
[
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 1, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0]
],
]
const player = {
position: {x: 5, y: -1},
piece: T[0]
}
function colorPiece(ctx, thePiece, offset) {
ctx.fillStyle = "red";
for(var y = 0; y < thePiece.length; ++y) {
for(var x = 0; x < thePiece[y].length; ++x) {
if (thePiece[y][x] !== 0) {
ctx.fillRect(20*(x + offset.x), 20*(y + offset.y), 20, 20);
}
}
}
}
function drawCanvas(ctx) {
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
colorPiece(ctx, player.piece, player.position);
}
var framenum=0;
function doUpdate(timestamp) {
document.getElementById("log").innerHTML = framenum.toString();
++framenum;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
drawCanvas(ctx);
window.requestAnimationFrame(doUpdate);
}
doUpdate();
I have been using a particle system to draw on a <canvas>, and now I'm trying to create an "image" out of particles from an array.
Unfortunately, I cannot seem to figure out how to position and color the particles according to how the array is structured.
The array I'm trying to make the image out of is the "defs" array. Hopefully I'm close to achieving my goal, but I need some help.
Here's what I've got.
0 = White
1 = Black
2 = Red
(Stackoverflow wouldn't let me enter in the Javascript for some reason, here is a link to the fiddle - http://jsfiddle.net/arcbpred/)
// Creating the Canvas
var canvas = document.createElement("canvas"),
c = canvas.getContext("2d"),
particles = {},
particleIndex = 0,
particleNum = 87;
document.body.appendChild(canvas);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.id = "canvas";
// Setting needed varialbes
var xmin = 1;
var xmax = +canvas.width;
var bgcolor = "#FFF";
// Setting color which is just one big square
c.fillStyle = bgcolor;
c.fillRect(0, 0, canvas.width, canvas.height);
var defs = {
[0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0], [0, 1, 2, 2, 2, 1, 0, 1, 2, 2, 2, 1, 0], [1, 2, 3, 3, 2, 2, 1, 2, 2, 2, 2, 2, 1], [1, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], [0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0], [0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0], [0, 0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0, 0], [0, 0, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
};
function Particle() {
this.x = canvas.width / 2 - 15;
this.y = canvas.height / 2 / 2;
this.vx = 0;
this.vy = 0;
this.gravity = 0;
particleIndex++;
particles[particleIndex] = this;
this.id = particleIndex;
this.beat = 1;
this.size = 30;
this.color = "rgba(191,30,46,0)";
}
Particle.prototype.draw = function() {
this.x += this.vx;
this.y += this.vy;
this.vy += this.gravity;
c.fillStyle = this.color;
c.fillRect(this.x, this.y, this.size, this.size);
};
setInterval(function() {
c.fillStyle = bgcolor;
c.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < particleNum; i++) {
new Particle();
}
for (var i in particles) {
particles[i].draw();
}
}, 30);
body{
margin: 0;
padding: 0;
background-color: rgba(100,0,0,1);
overflow:hidden;
}
Thank you for any input.