Detecting movement range with array - javascript

I have one dimension array like this:
0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1, 1,
0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 1, 1,
0, 1, 0, 1, 1, 1, 0,
0, 1, 0, 1, 0, 0, 0,
Legend: 0 = empty floor, 1 = wall that blocks the way, 2 = starting point, 3 = floor that is possible to reach from starting point.
Movement is possible horizontally, vertically, and diagonally, if value is 0.
Example map array is 7x7 and example movement range is 3, but these parameters might be even 15x9 with 6.
What I am trying to do is getting one dimension array that shows possible movement range from chosen point like this (example range is 3 steps, and diagonal can pass between walls if position has 0 as you can see in bottom left corner):
0, 0, 0, 0, 0, 0, 0,
3, 1, 1, 1, 1, 1, 0,
3, 3, 3, 3, 3, 1, 1,
3, 3, 3, 2, 3, 3, 3,
1, 3, 3, 3, 3, 1, 1,
3, 1, 3, 1, 1, 1, 0,
0, 1, 3, 1, 0, 0, 0,
That was easier version, because it would be good if range can be limited to specified shape that can be different in one dimension mask array like this example (0 = outside range):
0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 0,
0, 0, 1, 1, 1, 0, 0,
0, 0, 0, 1, 0, 0, 0,
In this case, result would be like this:
0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 0,
0, 3, 3, 3, 3, 1, 1,
3, 3, 3, 2, 3, 3, 3,
1, 3, 3, 3, 3, 1, 1,
0, 1, 3, 1, 1, 1, 0,
0, 1, 0, 1, 0, 0, 0,
Code:
<div id="results" style="font-family: monospace; font-weight: bold; font-size: 24pt; background-color: #000000; color: #FFFFFF;">
</div>
<script>
var map=[0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,0,1,1,1,0,0,1,0,1,0,0,0,];
var mask=[0,0,0,1,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,1,0,0,0,];
function path_create(map,width,height,point,range,mask)
{
// map = pure map with 0 as floor and 1 as wall
// width, height = size of map
// point = starting point to calculate movement range
// range = number of moves from starting point to each direction of horizontal, vertical, and diagonal
// mask = (optional) if possible to do, array mask (0 is not in range) can change range for diagonal moves with special shapes like circle or rhombus
var matrix=[];
return matrix;
// one dimension array where 0 is no range, and 1 is ok
}
function path_show(matrix,width,height)
{
var v="";
for(var i=0; i<matrix.length; i++)
{
if(i!=0 && i%7==0){v=v+"<br>";}
v=v+matrix[i]+" ";
}
document.getElementById('results').innerHTML=v;
}
path_show(path_create(map,7,7,25,3,mask));
//path_show(path_create(map,7,7,16,3,mask));
</script>

Basically you could integrate the mask array and exit early if a cell is found which should not be used.
This proposal uses matrices instead of linear arrays, because the check for adjacent cells is easier to address.
Then it takes an array for the possible directions and a check for early exit with
if (!mask[i][j] || array[i][j] === 1 || !steps || reach[i][j] >= steps) {
return;
}
where
!mask[i][j] checks the mask,
array[i][j] === 1 checks a wall,
!steps checks the leftover steps to go or
reach[i][j] >= steps where a cell's steps is greater than the leftover steps, this prevents to check already checked steps and prevents to take shorter leftover steps as posssible.
In all above cases the further processing stops.
function check(i, j, steps) {
var directions = [{ x: 0, y: -1 }, { x: -1, y: -1 }, { x: -1, y: 0 }, { x: -1, y: 1 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 1, y: 0 }, { x: 1, y: -1 }];
if (!mask[i][j] || array[i][j] === 1 || !steps || reach[i][j] >= steps) {
return;
}
reach[i][j] = steps;
directions.forEach(({ x, y }) => {
x += i;
y += j;
if (x < 0 || x >= width || y < 0 || y >= height) {
return;
}
check(x, y, steps - 1);
});
}
function out(array) {
document.getElementById('out').innerHTML += array.map(a => a.join(' ')).join('\n') + '\n\n';
}
function getMatrix(raw, width) {
var array = [],
i = 0;
while (i < raw.length) {
array.push(raw.slice(i, i += width));
}
return array;
}
var width = 7,
height = 7,
rawData = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0],
rawMask = [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0],
array = getMatrix(rawData, width),
mask = getMatrix(rawMask, width),
reach = Array.from({ length: height }, _ => Array.from({ length: width }).fill(0)),
max = 3,
result;
array[3][3] = 2;
check(3, 3, max + 1);
result = reach.map((a, i) => a.map((b, j) => b === max + 1 ? 2 : b ? 3 : array[i][j]));
out(array);
out(reach);
out(result);
<pre id="out"></pre>

Related

How can I add delays in between each iteration of a While Loop for a Canvas Animation?

Im writing a Tomb of the Mask game clone in Canvas for an assignment, and I wanted to create a small animation so that when my character "Snaps" to the next wall, it doesnt just "teleport" like it does now.
See here for a live review: https://codepen.io/SkylerSpark/pen/GRpdzBZ
Currently I have a big keydown event and a switch case that detects for any of the 4 arrow keys, this is an example of one of the case statements:
case "ArrowLeft":
if (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
while (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
playerCoords[0]--;
}
}
Ill split up those map statements for a better understanding:
map[playerCoords[1]][playerCoords[0] - 1] != 1
map[] - Main Map Data (1s and 0s that determine the game layout)
playerCoords[0 / 1] - Location of the Player
map[ pc[1] ] (going to get the sub array of map[playerCoords[1]]) > [pc[0] - 1] (-1 to look for the block to the left of the player)
then Im selecting all of that into one statement and detecting if its equal to 1 (1 is a brick block) to see if the player should MOVE or NOT MOVE.
Anyways, I have an animationFrame running on my player location at ALL TIMES so that if any adjustments are made, it will show them.
The while loops I use to send the player to the opposite wall (rather than just moving 1 block to the left or right, it needs to work JUST like TotM) are just immediately sending the results. I want it to quickly move all those blocks 1 by 1 but barely noticable...
Is it possible I can add some kind of "delay" inside the while loops so it can move 1 block, then wait 10 milliseconds, and then the next, so on so fourth?
Full Game and Code:
View in FULL PAGE or it wont work correctly..
const cvs = document.querySelector(".bastione"),
ctx = cvs.getContext("2d");
const cvs2 = document.querySelector(".basPlayer"),
ctx2 = cvs2.getContext("2d");
ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.webkitImageSmoothingEnabled = false;
ctx2.imageSmoothingEnabled = ctx2.mozImageSmoothingEnabled = ctx2.webkitImageSmoothingEnabled = false;
function loadImage(src, callback) {
var img = new Image();
img.onload = callback;
img.setAttribute("crossorigin", "anonymous");
img.src = src;
return img;
}
function ran(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
const map = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 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, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 1, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
];
const drawEnvironment = {
init: () => {
drawEnvironment.renderBack();
},
renderBack: () => {
let cx = 0, cy = 0;
map.forEach(e => {
for (var i = 0; i < e.length; i++) {
if (e[i] == 1) {
let v = ran(0, 10);
if (v > 0 && v < 8) {
ctx.drawImage(spriteImage, 0, 0, 32, 32, cx, cy, 32, 32);
} else {
ctx.drawImage(spriteImage, 32, 0, 32, 32, cx, cy, 32, 32);
}
cx += 32;
} else if (e[i] == 2 || e[i] == 3) {
ctx.drawImage(spriteImage, 64, 64, 32, 32, cx, cy, 32, 32);
cx += 32;
} else {
let v = ran(0, 10);
if (v > 0 && v < 5) {
ctx.drawImage(spriteImage, 128, 64, 32, 32, cx, cy, 32, 32);
if (v == 10) {
ctx.drawImage(spriteImage, 128, 32, 32, 32, cx, cy, 32, 32);
}
} else {
ctx.drawImage(spriteImage, 128, 32, 32, 32, cx, cy, 32, 32);
}
cx += 32;
}
}
cx = 0;
cy += 32;
});
ctx.drawImage(spriteImage, 0, 0, 32, 32, 0, 0, 32, 32);
}
};
let playerCoords = [1, 1];
const drawPlayer = {
init: () => {
drawPlayer.playerLoc();
drawPlayer.playerMove();
},
playerLoc: () => {
ctx2.drawImage(spriteImage, 0, 64, 32, 32, playerCoords[0] * 32, playerCoords[1] * 32, 32, 32);
window.requestAnimationFrame(drawPlayer.playerLoc);
},
playerMove: () => {
document.addEventListener("keydown", function(event) {
ctx2.clearRect(0, 0, cvs2.width, cvs2.height);
event.preventDefault();
const key = event.key;
switch (key) {
case "ArrowLeft":
if (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
while (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
playerCoords[0]--;
}
}
break;
case "ArrowRight":
if (map[playerCoords[1]][playerCoords[0] + 1] != 1) {
while (map[playerCoords[1]][playerCoords[0] + 1] != 1) {
playerCoords[0]++;
}
}
break;
case "ArrowUp":
if (map[playerCoords[1] - 1][playerCoords[0]] != 1) {
while (map[playerCoords[1] - 1][playerCoords[0]] != 1) {
playerCoords[1]--;
}
}
break;
case "ArrowDown":
if (map[playerCoords[1] + 1][playerCoords[0]] != 1) {
while (map[playerCoords[1] + 1][playerCoords[0]] != 1) {
playerCoords[1]++;
}
}
break;
}
});
}
}
const spriteImage = loadImage(
"https://cdn.jsdelivr.net/gh/FunctFlow/Bastione-Game#1d0514c968a737061916ae5e160b20eaf3a6b8b4/Sprites/Bastione_Sprites.png",
() => {
drawEnvironment.init();
drawPlayer.init();
}
);
* {
box-sizing: border-box;
overflow: hidden;
}
body {
text-align: center;
background: black;
}
canvas {
display: inline-block;
}
.basPlayer {
position: absolute;
margin-left: -1024px;
}
<canvas class=bastione width=1024 height=512></canvas>
<canvas class=basPlayer width=1024 height=512></canvas>
View in FULL PAGE or it wont work correctly..
You can use setInterval to create a loop which runs at a specific frequency.
case "ArrowLeft":
if (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
let interval = setInterval(() => {
if (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
// Stop the interval, and do nothing.
clearInterval(interval)
return
}
playerCoords[0]--;
}, 100) // <- 100 here is delay in milliseconds
}
This code is now running asynchronously. This means that while we are waiting for the delay, other code can run. If we are not careful, this can introduce bugs. For example: You need to think about what would happen if the player presses arrowRight while the arrowLeft is still being processed. If you do not fix that case, the character will move both left and right at the same time, which would mean that he would never hit a wall and you would be stuck in an endless loop.
This is BIG TOPIC
The normal thing to do would be for each thing (player, monster, etc..) to give them some kind of update function
const allTheThings = [];
function loop() {
for (const thing of things) {
thing.update();
}
requestAnimationFrame(loop);
}
In each of those update functions you would do whatever is appropriate for that thing doing only what is need at this moment. So for example the player might have a update function like this
class Player() {
constructor() {
this.coords = [1, 1];
this.delta = [0, 0];
}
update() {
if (this.waiting) {
this.waiting -= 1;
} else if (this.moving) {
this.coords[0] = this.delta[0];
this.coords[1] = this.delta[1];
this.waiting = 10;
} else {
// check the keys
// and set this.delta and moving appropriately
}
}
}
Then you can make a player and add it to this array of allTheThings
const player = new Player();
allTheThings.push(player);
Note that is way over simplified. Most games don't directly update in an object like that. Instead, just like allTheThings calls update for each thing, a thing itself might have a list of subthings (components) each of which also has an update function. A thing, or a GameThing, is a collection of these components.
Further, there are all kinds of ways to help organize what those update functions do. In the example above there were 2 flags moving and waiting but as the game gets more and more complicated there get to be too many flags so people have come up with things like Finite State Machines and Coroutines and may other techniques to help make that stuff simpler.
Maybe not useful but here is an article that uses some of these techniques.
Thanks to ViktorW, gman and VLAZ for the help and answers, I came up with a solution though:
so if you put a "run object" into the project, and have 4 sub variables with true/false, and change them depending on which key you press, you can make it work perfectly.
I followed ViktorW's answer and applied this idea, plus I just made a simple interval instead of the logic Viktor used:
so outside of my movement function, I have the run variable
let run = {
l: true,
r: true,
u: true,
d: true
}
theres variables for the 4 directions (Left Right Up Down)
Then just add the logic into the movement function + the interval and ITS logic:
case "ArrowLeft":
if (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
if (run.l == true) { // Detect for direction
var lInterval = setInterval(() => {
if (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
playerCoords[0]--;
run.r = run.u = run.d = false; // Set all other directions false
} else {
clearInterval(lInterval);
run.r = run.u = run.d = true; // Set all other directions true when done moving
}
}, 10);
}
}
break;
This PERFECTLY prevents the opposing movement of of the block if you use an interval to achieve the animation.
Check it out live here, use arrow keys to control: Tomb of the Mask Clone

How to translate multidimensional array from Javascript to C?

I am currently working on translating code from JavaScript to C using the Raylib framework for game coding practice, and I came across the following code:
area: {
columns:8,
map:[ 0, 0, 0, 0, 0, 0, 3, 0,
0, 0, 5, 0, 0, 0, 0, 4,
0, 7, 0, 0, 6, 2, 0, 4,
0, 1, 1, 1, 1, 1, 1, 1,
7, 0, 0, 0, 0, 0,13, 0,
0,11,12, 9,10, 0, 0, 7,
0, 1, 1, 1, 1, 1, 1, 1,
12, 0,14,14,14, 0, 8, 0 ]
}
Since I work in C, I figured this is self explanatory because it looks like a multidimensional array. I found the column variable strange, so I ignored it (I'll come back to that later). Then, later in the JavaScript code, I see a function with the following:
var value = area.map[row * area.columns + column];
So, this is an object with two members; an int named 'column' and a multidimensional array named 'map'; I can make a struct. But, why does area.map[] only have one bracket, wasn't this supposed to be a multidimensional array? Which elements of the array are being manipulated here? Where's the other bracket? How would I translate this into C code to read the array?
Not sure if it's what you're after, but here is a translation into C
struct Area
{
unsigned int columns;
unsigned int map[];
};
struct Area area = {
.columns = 8,
.map = { 0, 0, 0, 0, 0, 0, 3, 0,
0, 0, 5, 0, 0, 0, 0, 4,
0, 7, 0, 0, 6, 2, 0, 4,
0, 1, 1, 1, 1, 1, 1, 1,
7, 0, 0, 0, 0, 0,13, 0,
0,11,12, 9,10, 0, 0, 7,
0, 1, 1, 1, 1, 1, 1, 1,
12, 0,14,14,14, 0, 8, 0 }
};
unsigned int value = area.map[row * area.columns + column];

How to display arrays as visuals in Javascript (e.g. for a maze)

So, I am making a maze, in javascript. I do not know how to display an array (such as 1's and 0's) graphically. The best way is to show you the code:
var maze= [
[0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0],
[0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,1],
[0,0,1,1,1,0,0,1,1,0,0,1,0,0,0,0,1,0,0,1],
[0,0,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,0,0,1],
[0,0,1,0,1,1,0,1,0,0,0,1,1,1,0,0,1,0,0,0],
[0,0,1,1,0,1,1,0,0,0,0,0,0,1,0,0,1,1,1,1],
[0,0,0,1,1,0,1,1,1,1,1,1,0,0,0,0,0,1,0,1],
[0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,1],
];
With the 0's representing walls, and the 1's representing empty space, and 2 meaning the end of the maze, how can I display this as a maze using only javascript? Shall I make each 0 an individual rectangle? How can I do this?
Please do not make fun as I am just starting out.
Here is the code for reference (this is on "coding with chrome", as it was the easiest to use, as I did not have to import anything).
//PART 1 : THE CHARACTER
//Where is the character???
charX=10;
charY=10;
//Draw char
function drawChar(){
draw.circle(charX, charY, 5, "black");
}
//Loop happens at 40 milliseconds
setInterval (loop, 40);
//loop that clears screen
function loop(){
draw.rectangle(0,0, window.innerWidth, window.innerHeight,"white");
drawChar();
}
//Move Character
document.addEventListener("keydown", moveChar);
function moveChar (e) {
if(e.keyCode ==37){
//Left arrow
charX=charX-50;
}
if(e.keyCode== 38){
//Up arrow
charY=charY-50;
}
if(e.keyCode == 39){
//right arrow
charX=charX+50;
}
if(e.keyCode == 40){
//down arrow
charY=charY +50;
}
//PART 1 DONE :-)
//PART 2: Walls
//map of maze
var maze= [
[0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0],
[0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,1],
[0,0,1,1,1,0,0,1,1,0,0,1,0,0,0,0,1,0,0,1],
[0,0,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,0,0,1],
[0,0,1,0,1,1,0,1,0,0,0,1,1,1,0,0,1,0,0,0],
[0,0,1,1,0,1,1,0,0,0,0,0,0,1,0,0,1,1,1,1],
[0,0,0,1,1,0,1,1,1,1,1,1,0,0,0,0,0,1,0,1],
[0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,1],
];
//How can we display this graphically, with the 0 being a wall, and 1 being an empty space?
span {
white-space: pre;
display: inline-block;
width: 24px;
}
<script>
var maze = [
[0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1],
[0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1],
[0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1],
[0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0],
[0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1],
[0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1],
];
maze.forEach(function(arr, index) {
arr.forEach(function(path, i) {
var span = document.createElement("span");
if (path === 0) {
span.textContent = " ";
span.style.backgroundColor = "#000";
}
if (path === 1) {
span.textContent = " ";
span.style.backgroundColor = "yellow";
}
if (path === 2) {
span.textContent = "end";
span.style.backgroundColor = "green";
span.style.color = "gold";
}
document.body.appendChild(span)
});
document.body.appendChild(document.createElement("br"))
})
</script>

How to dynamically fill in an array of values based on a padded array?

Been going at this for a while, so I decided to step out of nodejs and onto jsfiddle to see if you guys can shed some light.
Here is the code:
inventory = [ // 50 Slot inventory 10 HORIZONTAL / 5 SLOTS VERTICAL
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
]
items = {
"1": {
name: "Short Sword",
slot_position: 5,
slot_size: [1, 3]
},
"2": {
name: "Heavy Leather Boots",
slot_position: 1,
slot_size: [2, 2]
},
"3": {
name: "Potion 1",
slot_position: 26,
slot_size: [1, 1]
}
}
for (i in items) {
inventory[items[i].slot_position] = 1; // Fill in the used inventory slots to 1 (The easy part)
if (items[i].slot_size) {
if (items[i].slot_size[0] > 1) {
/*
The X slot_size has to be greater than '1' because we already filled in their current positon.
Now I need to fill in the inventory slots based on their item_slot_size [X,Y] values... (I'm stuck here)
*/
}
}
}
console.log(inventory);
console.log(inventory.length); // 50 is correct.
And the jsfiddle: http://jsfiddle.net/68w1w0s8/8/
At line 42, I am stuck here because I need to dynamically fill the slots in the inventory to 1 based on the item slot_size dimensions.
For example, the short sword has a slot_size of [1,3] (3 squares down), how do I then dynamically fill in the appropriate values for that in my inventory array?
An example how my slot_size array is used is best left to be seen by my diagram:
First of all your inventory should be a matrix (collection of collections)
inventory = [ // 50 Slot inventory 10 HORIZONTAL / 5 SLOTS VERTICAL (HARDCODE)
[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]
]
then you can iterate using your slot size. Also, slot position should be stored in coordinates:
items = {
"1": {
name: "Short Sword",
slot_position: [5,0],
slot_size: [1, 3]
},
"2": {
name: "Heavy Leather Boots",
slot_position: [1,0],
slot_size: [2, 2]
},
"3": {
name: "Potion 1",
slot_position: [6,2],
slot_size: [1, 1]
}
}
for (i in items) {
var item = items[i];
if (item.slot_size) {
for (var x = 0; x < item.slot_size[0]; x++) {
for (y = 0; y < item.slot_size[1]; y++) {
inventory[y+item.slot_position[1]][x+item.slot_position[0]] = item;
}
}
}
}
JSfiddle
This is the first thing I thought of:
// slot_to_coords(n) returns (n%10, n/10)
// coords_to_lot(x, y) returns (y*10 + x)
coords = slot_to_coords(items[i].slot_position);
for (int x = 0; x < items[i].slot_size[0]; x++) {
for (int y = 0; y < items[i].slot_size[1]; y++) {
slot = coords_to_slot(x+coords[0], y+coords[1]);
inventory[slot] = 1;
}
}
Is this similar to what you are trying to do? Make sure you get all edge cases.

Collision detection in javascript canvas tanks game

Here is the code, how would i go on about making the bullets fired (size 6x6 pixels) collide with the blocks currently on the map? I've looked at all sort of collision code but I can't seem to apply it to this particular code because of how the map is setup i'm guessing.
Desperately need a solution!
// inner variables
var canvas, context; // canvas and context objects
var imgBrick, imgSteel, imgWater, imgForest, imgTank; // images
var aMap; // map array
var oTank; // tank object
var bullets = [];
var iCellSize = 24; // cell wide
var iXCnt = 26; // amount of X cells
var iYCnt = 26; // amount of Y cells
// objects :
function Tank(x, y, w, h, image) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.i = 0;
this.image = image;
}
function Bullet(dir, bullX, bullY, bulltype) {
this.direct = dir;
this.bullX = bullX;
this.bullY = bullY;
this.bulltype = bulltype;
}
// functions
function clear() { // clear canvas function
context.clearRect(0, 0, canvas.width, canvas.height);
}
// Firing bullets and directions
function movebullets()
{
for (var i = 0; i < bullets.length; i++) {
if (bullets[i].direct==2)
{
bullets[i].bullY-=10;
}
else if (bullets[i].direct==3)
{
bullets[i].bullY+=10;
}
else if (bullets[i].direct==0)
{
bullets[i].bullX+=10;
}
else if (bullets[i].direct==1)
{
bullets[i].bullX-=10;
}
}
}
/*
function bulletCollision(){
var remove = false;
for(var i = 0; i < bullets.length; i++){
for(var j = 0; j < asteroids.length; j++){
if(bullets[i].y <= (asteroids[j].y + enemies[j].h) && bullets[i].x >= enemies[j].x && bullets[i].x <= (enemies[j].x + enemies[j].w)){
remove = true;
enemies[j].dead = true;
//score++;
//explosions.push(new Explosion((asteroids[j].x + (asteroids[j].w / 2)), (asteroids[j].y + (asteroids[j].h / 2 )), asteroids[j].w));
enemies.splice(j,1);
}
}
*/
function drawScene() { // main drawScene function
clear(); // clear canvas
// fill background
context.fillStyle = '#111';
context.fillRect(0, 0, canvas.width, canvas.height);
// save current context
context.save();
// walk through our array
for (var y = 0; y < iYCnt; y++) {
for (var x = 0; x < iXCnt; x++) {
switch (aMap[y][x]) {
case 0: // skip
break;
case 1: // draw brick block
context.drawImage(imgBrick, 0, 0, iCellSize, iCellSize, x*iCellSize, y*iCellSize, iCellSize, iCellSize);
break;
case 2: // draw steel block
context.drawImage(imgSteel, 0, 0, iCellSize, iCellSize, x*iCellSize, y*iCellSize, iCellSize, iCellSize);
break;
case 3: // draw forest block
context.drawImage(imgForest, 0, 0, iCellSize, iCellSize, x*iCellSize, y*iCellSize, iCellSize, iCellSize);
break;
case 4: // draw water block
context.drawImage(imgWater, 0, 0, iCellSize, iCellSize, x*iCellSize, y*iCellSize, iCellSize, iCellSize);
break;
}
}
}
// restore current context
context.restore();
// draw tank + bullets
context.drawImage(oTank.image, oTank.i*oTank.w, 0, oTank.w, oTank.h, oTank.x, oTank.y, oTank.w, oTank.h);
for (var i = 0; i < bullets.length; i++) {
context.drawImage(imgBullet, bullets[i].bullX, bullets[i].bullY);
}
}
// -------------------------------------------------------------
// initialization
$(function(){
canvas = document.getElementById('scene');
canvas.width = iXCnt * iCellSize;
canvas.height = iYCnt * iCellSize;
context = canvas.getContext('2d');
// main scene Map array
aMap = ([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 2, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 2, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 4, 4, 4, 4, 1, 1, 3, 3, 3, 3, 3, 3, 4, 4, 1, 1, 0, 0, 2, 2, 0, 0],
[0, 0, 0, 0, 4, 4, 4, 4, 1, 1, 3, 3, 3, 3, 3, 3, 4, 4, 1, 1, 0, 0, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 4, 4, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 4, 4, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 2, 2, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 2, 2, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[3, 3, 3, 3, 1, 1, 0, 0, 4, 4, 4, 4, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0],
[3, 3, 3, 3, 1, 1, 0, 0, 4, 4, 4, 4, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0],
[3, 3, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 3, 3],
[3, 3, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 3, 3],
[0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 2, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 2, 2, 0, 0, 3, 3, 4, 4, 0, 0, 1, 1, 0, 0],
[2, 2, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 2, 2, 0, 0, 3, 3, 4, 4, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 2, 2, 0, 0, 3, 3, 4, 4, 0, 0, 2, 3, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 2, 2, 0, 0, 3, 3, 4, 4, 0, 0, 2, 3, 0, 0],
[0, 0, 0, 0, 0, 0, 2, 2, 3, 3, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0],
[0, 0, 0, 0, 0, 0, 2, 2, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 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, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0]
]);
// load images
imgBrick = new Image();
imgBrick.src = 'images/brick.png';
imgSteel = new Image();
imgSteel.src = 'images/steel.png';
imgWater = new Image();
imgWater.src = 'images/water.png';
imgForest = new Image();
imgForest.src = 'images/forest.png';
imgBullet = new Image();
imgBullet.src = 'images/bullet.png';
imgTank = new Image();
imgTank.src = 'images/tank.png';
oTank = new Tank(iCellSize*9, iCellSize*24, 48, 48, imgTank);
document.addEventListener('mousedown', function (event) {
bullets.push(new Bullet(oTank.i, oTank.x+24, oTank.y+24, 1));
(function(){
var sound = new Audio('bullet_shot.ogg');
sound.volume = 0.9;
sound.addEventListener('ended', function() { // loop the sound
}, false);
sound.play();
})();
});
$(window).keydown(function(event){ // keyboard alerts
switch (event.keyCode) {
case 87: // W key
oTank.i = 2;
// checking collisions
var iCurCelX = (2 * oTank.x) / 48;
var iCurCelY = (2 * oTank.y) / 48;
if (iCurCelY) {
var iTest1 = aMap[iCurCelY-1][iCurCelX];
var iTest2 = aMap[iCurCelY-1][iCurCelX+1];
if ((iTest1 == 0 || iTest1 == 3) && (iTest2 == 0 || iTest2 == 3)) {
oTank.y-=24;
if (oTank.y < 0) {
oTank.y = 0;
}
}
}
break;
case 83: // S key
oTank.i = 3;
// checking collisions
var iCurCelX = (2 * oTank.x) / 48;
var iCurCelY = (2 * oTank.y) / 48;
if (iCurCelY+2 < iYCnt) {
var iTest1 = aMap[iCurCelY+2][iCurCelX];
var iTest2 = aMap[iCurCelY+2][iCurCelX+1];
if ((iTest1 == 0 || iTest1 == 3) && (iTest2 == 0 || iTest2 == 3)) {
oTank.y+=24;
if (oTank.y > 576) { //iCellSize * (iYCnt-2)
oTank.y = 576;
}
}
}
break;
case 65: // A key
oTank.i = 1;
// checking collisions
var iCurCelX = (2 * oTank.x) / 48;
var iCurCelY = (2 * oTank.y) / 48;
var iTest1 = aMap[iCurCelY][iCurCelX-1];
var iTest2 = aMap[iCurCelY+1][iCurCelX-1];
if ((iTest1 == 0 || iTest1 == 3) && (iTest2 == 0 || iTest2 == 3)) {
oTank.x-=24;
if (oTank.x < 0) {
oTank.x = 0;
}
}
break;
case 68: // D key
oTank.i = 0;
// checking collisions
var iCurCelX = (2 * oTank.x) / 48;
var iCurCelY = (2 * oTank.y) / 48;
var iTest1 = aMap[iCurCelY][iCurCelX+2];
var iTest2 = aMap[iCurCelY+1][iCurCelX+2];
if ((iTest1 == 0 || iTest1 == 3) && (iTest2 == 0 || iTest2 == 3)) {
oTank.x+=24;
if (oTank.x > 576) { //iCellSize * (iXCnt-2)
oTank.x = 576;
}
}
break;
}
});
setInterval(drawScene, 40); // loop drawScene
setInterval(movebullets, 40);
});
you need to set the bullet size like this inside the for
for (var i = 0; i < bullets.length; i++) {
//6px width and height
context.drawImage(imgBullet, bullets[i].bullX, bullets[i].bullY,6,6);
}
and inside movebullets() you can call bulletCollision like this
function movebullets()
{
for (var i = 0; i < bullets.length; i++) {
if (bullets[i].direct==2)
{
bullets[i].bullY-=10;
}
else if (bullets[i].direct==3)
{
bullets[i].bullY+=10;
}
else if (bullets[i].direct==0)
{
bullets[i].bullX+=10;
}
else if (bullets[i].direct==1)
{
bullets[i].bullX-=10;
}
bulletCollision(i);
}
}
function bulletCollision(i){
//if the aMap value in the bullet position is not 0
if(aMap[parseInt(bullets[i].bullY/iCellSize)][parseInt(bullets[i].bullX/iCellSize)]!=0){
alert("Collision");//do something
bullets.splice(i, 1);//remove the current bullet
}
}

Categories