Related
I am trying to make a game in Javascript. The game board is intialized to a zero-filled 2-D array. However, when I am setting the value of a single point, the complete column is getting set with that value. I think this is some issue with the way I am initializing the Array.
Method 1
# initialization
gameState = Array(6).fill(Array(7).fill(0))
# later in the game
gameState[2][4] = 1
# results in complete 4th index column to be assigned the value 1, like so -
0: (7) [0, 0, 0, 0, 1, 0, 0]
1: (7) [0, 0, 0, 0, 1, 0, 0]
2: (7) [0, 0, 0, 0, 1, 0, 0]
3: (7) [0, 0, 0, 0, 1, 0, 0]
4: (7) [0, 0, 0, 0, 1, 0, 0]
5: (7) [0, 0, 0, 0, 1, 0, 0]
Method 2
# initialization
let gameState = [];
for (let i=0; i<MAX_ROWS; i++) {
let row = []
for (let j=0; j<MAX_COLUMNS; j++) {
row.push(0)
}
gameState.push(row);
}
# again similar assignment
gameState[2][4] = 1
# results in correct state of the array
0: (7) [0, 0, 0, 0, 0, 0, 0]
1: (7) [0, 0, 0, 0, 0, 0, 0]
2: (7) [0, 0, 0, 0, 1, 0, 0]
3: (7) [0, 0, 0, 0, 0, 0, 0]
4: (7) [0, 0, 0, 0, 0, 0, 0]
5: (7) [0, 0, 0, 0, 0, 0, 0]
Can someone please explain what I am doing wrong here?
Your problem is pretty simple.
Array(6).fill(Array(7).fill(0))
Let's explain what this does.
Array(6)
creates a holey array with space for 6 items.
.fill(...)
will fill up these 6 holes with what ever you put in as argument.
Now comes the issue..
In Javascript, the arguments are evaluated before the execution of the function is run.
This means (in this exact case where .fill(...) is only ran once) your code is exactly the same as:
const innerArray = [0,0,0,0,0,0,0];
gameState = Array(6).fill(innerArray);
This means it fills the outer array with exactly the same array instance 6 times.
What you want is to create separate arrays each time. Just do this instead:
gameState = [...Array(6)].map(() => [...Array(7)].map(() => 0))
I have searched this for weeks but just can't find the right tutorial.
Lets say we have a canvas that is 800x800.
<canvas id='draw' width=800 height=800></canvas>
And we have a tile map(0 will be square barriers and 1 will be air).
var tileMap = [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
]
How do I make the map scroll so that only 3x3 of the squares be seen whenever the player moves?
For example:
canvas screen--> [0,0,0]
[0,1,1] <-- just this part to be seen
[0,0,0]
when player moves:
canvas screen--> [0,0,0]
[1,1,1] <-- now this part will be seen
[0,0,0]
So how do I make the tile map move to give the illusion that the player is moving?
tileMap should not be modify instead you create some object that represents center of current view e.g. player and use it in your display function. Whenever you want to scroll you just move center of view.
var tileMap = [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
]
var hero = {
position: {
x: 0,
y: 1
}
};
const air = 1;
const barrier = 0;
display(hero,tileMap);
// move player instead of scrolling data
// tileMap is untouched
hero.position.x += 3;
display(hero,tileMap);
// use player position to display only portion of map
function display(player,map) {
var result = [
"",
"",
""
];
for(var y = 0, i = player.position.y - 1; y < 3; i++,y++) {
if (i >= 0 && i < map.length) {
for(var x = 0, j = player.position.x - 1; x < 3; j++,x++) {
if ( j >= 0 && j < map[i].length) {
result[y] += map[i][j] + ",";
}
else {
// outside map only ait
result[y] += air+ ",";
}
}
}
else {
// outside map only ait
result[y] += air +","+ air +","+air+",";
}
}
console.log(result);
}
You did not explained how is your array correlated with your canvas and animations - therefore a clear guess is that you're concerned about your Arrays only.
You need a viewport Array viewMap dictated by the camera cam position and size values.
In the example below it's anchor is left/top (you might want to change the logic later to use center/center instead, up to you).
on keyboard event, change the camera x y position and prevent going out of map boundaries
Populate your viewMap array and print it:
var tileMap = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 4, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 8, 8, 1, 0],
[0, 6, 1, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 9, 9, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
var cam = {
x: 0,
y: 0,
width: 3,
height: 3
};
// Create empty 2D viewMap of viewport
var viewMap = [];
for (var i = 0; i < cam.height; i++) viewMap[i] = new Array(cam.width);
function tileViewport() {
for (var y = 0; y < cam.height; y++)
for (var x = 0; x < cam.width; x++)
viewMap[y][x] = tileMap[y + cam.y][x + cam.x];
// PRINT
console.clear(); console.log(viewMap.map(a => a.join(" ")).join("\n"))
}
document.body.addEventListener("keydown", function(e) {
var key = e.which;
if( /^(37|38|39|40)$/.test(key) ) e.preventDefault(); // prevent browser default stuff
if (key === 38) --cam.y;
if (key === 40) ++cam.y;
if (key === 37) --cam.x;
if (key === 39) ++cam.x;
// Fix movement to tileMap area boundary
cam.y = Math.max(0, Math.min(cam.y, tileMap.length - cam.height));
cam.x = Math.max(0, Math.min(cam.x, tileMap[0].length - cam.width));
tileViewport();
});
// INITIALIZE
tileViewport();
Click here and user your keyboard arrows!
Now that the above works correctly you can:
Add logic for obstacles behavior by using the new viewMap Array
prefetch new tiles for your canvas,
animate the canvas depending on the movement
I'm creating an isometric tile map for a small www game, I have created an array to store map data, column and row numbers aswell as height and width of tile. Then I found function getTile on web which is on the end of this map array. It should be giving me value of the tile later on. It looks like this:
var map = {
cols: 13,
rows: 11,
twidth: 200,
theight: 100,
tiles: [
1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1,
2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1,
],
getTile: function(col, row) {
return this.tiles[row * map.cols + col]
}
};
Then I have a code to draw something on the canvas:
for (var c = 0; c < map.cols; c++) {
for (var r = 0; r < map.rows; r++) {
var tile = map.getTile(c, r);
var screenX = (c - r) * map.twidth/2;
var screenY = (c + r) * map.theight/2;
var screenX = screenX+1000;
if(tile == 1) {
var element = "area_grass_01";
}
if(tile == 2) {
var element = "area_road_01";
}
var img = document.getElementById(element);
ctxObj.drawImage(img, (tile - 1) * map.twidth, 0, map.twidth, map.theight, screenX, screenY, map.twidth, map.theight);
}
}
Now, when I run console_log or alert with tile variable in it when the loop is run. It shows all the numbers that are included in map.tiles one by one. However when I try to find out which image should be drawn like this:
if(tile == 1) {
var element = "area_grass_01";
}
if(tile == 2) {
var element = "area_road_01";
}
var img = document.getElementById(element);
It only draws the title with value 1. Rest stays undrawn. Like this:
Map render
Now I want to ask you how do I actually set the image according to the array number I've put inside map.tiles?
And another question that I have to anyone familiar with this. If I have a tile width 200 and height 100, how do I draw let's say a building which is much higher? Do I find the height of that drawing and set it higher by the drawing size - tile height (which is 100), or do you have any other advice on how to draw higher elements? And do I still use the same drawing technique for this:
var screenX = (c - r) * map.twidth/2;
var screenY = (c + r) * map.theight/2;
But with adjustment of map.theight in screenY?
You are using an overload of drawImage of which the second and third parameters are the source X and Y values. You only need to use these if you are making use of a spritesheet which it doesn't seem like you are. Try replacing the second parameter of that function call with just 0.
Take a look at the sx, sy, swidth and sheight parameters here for more information.
I need to draw a 3d house model (walls only) from a 2d path or array (explained later) I receive from FabricJS editor I've built. The type of data sent from 2d to 3d views doesn't matter.
My first (and only quite close to what I want to get) attempt was to create the array of 1s and zeros based on the room I want to draw, and then render it in ThreeJS as one cuboid per 'grid'. I based this approach on this ThreeJS game demo. So if the array look like this:
var map = [ //1 2 3 4 5 6 7 8
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1,],
[1, 1, 0, 0, 0, 0, 0, 1, 1, 1,], // 1
[1, 1, 0, 0, 1, 0, 0, 0, 0, 1,], // 2
[1, 0, 0, 0, 1, 1, 0, 0, 0, 1,], // 3
[1, 0, 0, 1, 1, 1, 1, 0, 0, 1,], // 4
[1, 0, 0, 0, 1, 1, 0, 0, 1, 1,], // 5
[1, 1, 1, 0, 0, 0, 0, 1, 1, 1,], // 6
[1, 1, 1, 0, 0, 1, 0, 0, 1, 1,], // 7
[1, 1, 1, 1, 1, 1, 0, 0, 1, 1,], // 8
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1,],
];
I iterate through the array and render one block for every 1, and calculate it's position from indexes from the 2d 'map' (my array).
var UNITSIZE = 250, units = mapW;
for (var i = 0; i < mapW; i++) {
for (var j = 0, m = map[i].length; j < m; j++) {
if (map[i][j]) {
var wall = new t.Mesh(cube, material);
wall.position.x = (i - units/2) * UNITSIZE;
wall.position.y = WALLHEIGHT/2;
wall.position.z = (j - units/2) * UNITSIZE;
scene.add(wall);
}
}
}
It worked great till I wanted to place other models (.obj, but it doesn't matter. Let's call them furniture) near the walls. Each piece of furniture has it's (x=0, y=0, z=0) point in the center of the model, and since walls are cubes (with the same coord system, with 0 point in the center), furniture are rendered in the center of the wall (when we place it in the corner, only 1/4 of the model is visible). This is more/less how it looks like:
(black - how the walls should look like, blue - each cuboid of the wall, red - piece of furniture)
Thats why I would like to render walls as planes, probably from a 2d closed patch (I can export it from Fabric without a problem). I don't need walls to be thick nor to be visible "from behind", when camera moves through the wall. Any clues on how to achieve something like this?
"Help me StackOverflow, your my only hope."
You can manually populate the vertex and face arrays of a THREE.js mesh, so if you can export the closed path you need for example as an array of coordinates, you can iterate over it, and push needed information to your wall object.
Something like this
var coordArray = [...]; //Array of corner points of a closed shape from your source. Here assumed to be THREE.Vector2() for simplicity.
var walls = new THREE.Geometry();
for(var i = 0; i < coordArray.length(); i++){ //iterate over the coordinate array, pushing vertices to the geometry
var coordinates = coordArray[i];
walls.vertices.push(new THREE.Vector3(coordinates.x, coordinates.y, 0)); //vertex at floor level
walls.vertices.push(new THREE.Vector3(coordinates.x, coordinates.y, 10)); //vertex at the top part of the wall, directly above the last
}
var previousVertexIndex = walls.vertices.length - 2; // index of the vertex at the bottom of the wall, in the segment we are creating faces for
for(var i = 0; i < walls.vertices.length; i += 2){
walls.faces.push(new THREE.Face3(i, i + 1, previousVertexIndex));
walls.faces.push(new THREE.Face3(i + 1, previousVertexIndex + 1, previousVertexIndex));
previousVertexIndex = i;
}
walls.computeVertexNormals();
walls.computeFaceNormals();
scene.add(new THREE.Mesh(walls, new THREE.MeshLambertMaterial());
I have an array that looks like this
board = [
[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]
];
Is there any way, just by using 2 for loops, like this
for(var i = 0; i < 7; i++){
for(var j = 0; j < 7; j++){
//actions here
}
}
to place 6 values of 1, first value of 1 to replace the first 0 in the array and the next 5 to be placed using the same rules for placing a queen on a chessboard. The result should look like this
board = [
[1, 0, 0, 0, 0 ,0, 0, 0],
[0, 0, 1, 0, 0 ,0, 0, 0],
[0, 0, 0, 0, 1 ,0, 0, 0],
[0, 1, 0, 0, 0 ,0, 0, 0],
[0, 0, 0, 1, 0 ,0, 0, 0],
[0, 0, 0, 0, 0 ,0, 1, 0],
[0, 0, 0, 0, 0 ,0, 0, 0],
[0, 0, 0, 0, 0 ,0, 0, 0]
];
I'm new to javascript and I have no clue on how to start this. Could someone please give me some hints.
board[xCord][yCord] = ValueToInsert;
I may have misread your question, so here is some more info.
You can "push" another array into a 1d array.
myArr.push([]);
Then you push the value like this:
myArr[Xcord].push(YValue);
You need to make a function to check whether placing a queen on xy coordinates is valid. The function would check whether there is a queen in that row, column or diagonals. You can also make those three sub-functions of the main function.
Then, as you go through the loop, use that function for each field and if it returns true, place the queen. If false, move on.
Of course, there will be many solutions in the end, so you might want to keep/store all of them in an array.
Also, think of ways to optimize the algorithm: for example, if you set a queen in a row, there's no need to check any other fields in that row, etc.
And when you decide to really go advanced with your skills, try using recursive functions to generate the solutions.