Canvas Maze character's distance from walls - javascript

I am working on a 2D maze game with a torch effect in canvas without the use of any raytracing. Everything is working great, however the torch effect's algorithm is causing immense lags in several browsers and computers. (It is weird as well, that the game runs smoother on older computers. The funniest is, IExplorer runs the game without any lags, while mozzila dies on every move..)
My general idea for solving this problem was, to get how far the character is from the walls (4 functions) and make the rest of the maze grey.
Here is an example with the Northern wall detection:
http://webprogramozas.inf.elte.hu/~ce0ta3/beadando/maze_example.png
And an example how it is working at the moment and what I would like to achieve without lag issues.
http://webprogramozas.inf.elte.hu/~ce0ta3/beadando/ce0ta3_html5_maze.html
As I mentioned above, the algorithm that tracks the character's distance from the walls is causing incredible lags.
//Get the character's X,Y position as parameter
function distanceFromNorth (posX,posY)
{
distNorth = 0;
var l = false;
//Start getting charSize x 1 px lines from the character position towards the up, until we reach the max viewDistance or we find a black pixel in the charSize x 1 line.
for (var i = posY; i > posY - viewDistance && !l; i--)
{
var mazeWallData = context.getImageData(posX, i, charSize, 1);
var data = mazeWallData.data;
//Check if there are any black pixels in the line
for (var j = 0; j < 4 * charSize && !l; j += 4)
{
l = (data[j] === 0 && data[j + 1] === 0 && data[j + 2] === 0);
}
distNorth++;
}
return distNorth;
}
I am fairly sure, that the ctx.getImageData() is the most costly method in this linear search and if I only requested this method once for a charSize x viewDistance rectangle, and then check for black pixels in that huge array, then the lag could be reduced greatly. However, I still want to keep searching in lines, because finding only one black pixel will return false distNorth value.
I would be grateful if anyone could convert my code into the form I mentioned in the previous paragraph.

Assuming the image data isnt changing then you can precompute all the pixel values that have black pixel. Then use simple binary search on it to get the any black pixels in the given range.
Algorithm : -
cols[];
rows[];
for(int i=0;i<height;i++) {
for(int j=0;j<width;j++) {
if(pixel(j,i)==black) {
row[i].add(j);
col[j].add(i);
}
}
}
for query on (x,y) :
distance = binarysearch(col[x],y,y-distance) - y

Related

How do I generate a random X value for each "projectile" in my falling objects game using Javascript?

I am coding a game that is currently in its very early stages for a project to try to learn more about coding. In my game, objects generate randomly (green squares), and the player (red square), avoids them. I am having trouble trying to get the green squares to generate from a random position on the x-axis. I already have a formula to generate a random number for X, but after it selects a number randomly, all the "projectiles" generate there, rather than all generating from a different area. How would I get all the "projectiles" to generate from different positions on the x-axis randomly?
var randomX = Math.floor(Math.random() * 480) + 15;
function updateGameArea() {
var x, y;
for (i = 0; i < projectiles.length; i += 1) {
if (player.crashWith(projectiles[i])) {
gameArea.stop();
return;
}
}
gameArea.clear();
gameArea.frameNo += 1;
if (gameArea.frameNo == 1 || everyinterval(150)) {
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
for (i = 0; i < projectiles.length; i += 1) {
projectiles[i].y += -1; // the shape is using its coordinates to build downward from its y position
projectiles[i].update();
}
player.newPos();
player.update();
}
function everyinterval(n) {
if ((gameArea.frameNo / n) % 1 == 0) {return true;}
return false;
Expected: Green squares generate in random positions on the x- axis every 3 seconds and move upwards
Actual: Green squares all generate from the same random position on the X-axis.
You should reset X every time you're adding a new projectile:
if (gameArea.frameNo == 1 || everyinterval(150)) {
randomX = Math.floor(Math.random() * 480) + 15;
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
Otherwise, the randomX value stays constant as the value originally evaluated on line 1 when the interpreter reached it.
Here's your problem:
var randomX = Math.floor(Math.random() * 480) + 15;
// Generates a random number and stores it to randomX
// Called using 'randomX'
You need to turn it into a function if you want it to run each time:
var randomX = function() { Math.floor(Math.random() * 480) + 15 };
// Returns a new number each time
// Called using 'randomX()'
Both shivashriganesh mahato and natelouisdev have, essentially responded to how to fix the issue but since you are learning coding here is a tip. When you code, the code will run in a particular order. If you want something to be reassigned repeatedly, in this case a randomized number being used, and you want it to occur only after an event, you need to make sure that it gets trigger within each event.
natelouisdev has a good approach because, by using it as a function, you can call your randomizer more cleanly in your code and make it reassign the value of x each time.
Since you are building a game, it is also a good idea to compartmentalize your code. It'll make it easier to keep your ideas in order for each event trigger.
Example:
function gameLoss(){} - Define event return upon game loss. You can
then create editable rules to reason for loss without having to edit
the loss
function gameActive(){} - Defines what is normal gameplay. everything that occurs during normal gameplay should be managed here.
function gameArea(){} - Defines game canvas that function more for UI than for gameplay (scores, lifes, size of screen, etc)
Had you created individual functions you'd realize you only need a randomized 'x' value upon regular play thus you'd assign it within the gameActive() function and not as a global variable. Then you'd call the gameActive() function as many times as needed within a time interval to ensure a unique value is created each time.
-Side note: Don't litter unnecessary global variables. It'll make a mess off of your code when debugging. -

Drawing any function's graph using Javascript

I'm taking a javascript class for my bachelor in CS, and the IDE we're using (it's an educative IDE developed by one of the graduates here, called codeboot) has a "turtle drawing" feature. Basically, it works using several basic commands;
Forward; fd(x), Back; bk(x), rotate right; rt(x), rotate left; lt(x), pen up; pu() and pen down; pd(). Where x represent pixels.
For example, to have it draw a circle, one would write something like:
for (var n = 0; n < 360; n++) {
fd(1);
rt(1);
}
I'm trying to have it draw graphs for basic functions,but the only way I found how to do it is to have it draw a single point for each 0.005x (so it looks like a continuous line when zoomed out), but it makes the execution extremely slow, and you can kind of see it's not legit. How would I go about having just a continuous line instead of individually plotting each point?
Screenshot of what it looks like using my first technique:
Screenshot of what it looks like using my first technique:
and the JS code for said technique:
var x = -9;
var traceDown = function(d) {
var y = Math.cos(x) * 30;
setpw(3);
pu();
fd(y);
pd();
fd(1);
pu();
bk(1 + (y));
rt(90);
fd(d);
lt(90);
pd();
x += 0.0005;
};
pu();
lt(90);
fd(90);
lt(90);
fd(45);
pd();
rt(180);
while (x < 9) {
traceDown(0.005);
}

Rendering too many points on Javascript-player

As part of a project, I have to render a video on a JS-player from a text file which has the points - all the changed coordinates along-with the color in each frame. Below is the code I'm using to draw these point on the screen.
But the issue is that the number of changed pixels in each frame are as high as ~20,000 and I need to display these in less than 30ms (inter-frame time difference). So, when I run this code the browser hangs for almost each frame. Could someone suggest an improvement for this?
Any help is really appreciated.
c.drawImage(img,0,0,800,800);
setInterval(
function(){
while(tArr[index]==time) {
var my_imageData = c.getImageData(0,0,width, height);
color(my_imageData,Math.round(yArr[index]),Math.round(xArr[index]),Math.round(iArr[index]),255);
c.putImageData(my_imageData,0,0);
index=index+1;
}
time = tArr[index];
}
,30);
xArr, yArr, iArr, tArr are arrays of x-coordinate, y-coordinate, intensity value and time of appearance respectively for the corresponding point to be rendered
function color(imageData,x,y,i,a){ //wrapper function to color the point
var index = (x + y * imageData.width) * 4;
imageData.data[index+0] = i;
imageData.data[index+1] = i;
imageData.data[index+2] = i;
imageData.data[index+3] = a;
}

Difficult to solve the phaser sliding puzzle as some parts of the original image is missing

Im trying to create the phaser examples game sliding puzzle
Live example is demonstrated here
But in the output game, some parts of the original image is missing. So it is difficult to solve the puzzle.
I am suspecting the algorithm of cutting the image to pieces is not correct.
The code for pieces is ,
function prepareBoard() {
var piecesIndex = 0,
i, j,
piece;
BOARD_COLS = Math.floor(game.world.width / PIECE_WIDTH);
BOARD_ROWS = Math.floor(game.world.height / PIECE_HEIGHT);
piecesAmount = BOARD_COLS * BOARD_ROWS;
shuffledIndexArray = createShuffledIndexArray();
piecesGroup = game.add.group();
for (i = 0; i < BOARD_ROWS; i++)
{
for (j = 0; j < BOARD_COLS; j++)
{
if (shuffledIndexArray[piecesIndex]) {
piece = piecesGroup.create(j * PIECE_WIDTH, i * PIECE_HEIGHT, "background", shuffledIndexArray[piecesIndex]);
}
else { //initial position of black piece
piece = piecesGroup.create(j * PIECE_WIDTH, i * PIECE_HEIGHT);
piece.black = true;
}
piece.name = 'piece' + i.toString() + 'x' + j.toString();
piece.currentIndex = piecesIndex;
piece.destIndex = shuffledIndexArray[piecesIndex];
piece.inputEnabled = true;
piece.events.onInputDown.add(selectPiece, this);
piece.posX = j;
piece.posY = i;
piecesIndex++;
}
}
}
function createShuffledIndexArray() {
var indexArray = [];
for (var i = 0; i < piecesAmount; i++)
{
indexArray.push(i);
}
return shuffle(indexArray);
}
function shuffle(array) {
var counter = array.length,
temp,
index;
while (counter > 0)
{
index = Math.floor(Math.random() * counter);
counter--;
temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
}
Please anyone have any idea ? Please share any algorithm to correctly cut the pieces.
Thanks in advance
iijb
This is the classic 15puzzle because it traditionally has a 4x4 grid with 1 tile missing (4x4-1=15 tiles). However the puzzle can practically be any grid size (4x3, 5x4, 6x6 etc).
You're using a .destIndex property to keep track of their position, but you could just give each tile a numbered index. I think that way it's easier because when all the tiles are ordered the puzzle is solved and it would also help the check-if-solvable-algorithm.
With these kind of sliding tile puzzles, there are two things to consider which are a little tricky, especially the 2nd point:
There is always one tile missing because that is the empty spot that the player can use to slide tiles into. Most commonly, the missing tile is the bottom-right tile of the image.
In your algorithm the blank tile is always the top-left tile of the image.
This is unusual and players might not expect that, however in theory it doesn't really matter and you could make a workable puzzle that way. You then keep track of the empty tile in code by value 1 (or maybe 0 for zero-indexed) because it's the first tile.
Some configurations are unsolvable, i.e. not every random scrambled tiles situation can be solved by sliding the tiles around.
A puzzle is solvable when the number of inversions (switches) needed to solve it is an even number, not odd. So count the number of pairs where a bigger number
is in front of a smaller one (=one inversions). For example in a 3x3 puzzle with the bottom-right tile missing:
5 3 4
2 6 1
8 7
In array it looks like this [5,3,4,2,6,1,8,7,9], so count pairs which are 5-3, 5-4, 5-2, 5-1, 3-2 3-1, 4-2 4-1, 2-1, 6-1, 8-7. This equals 11 pairs, so 11 inversions are needed. This is not an even number, thus this configuration is unsolvable. Btw note that the missing tile has internally the highest possible number, which is 9 in this case.
You can use this method to detect a unsolvable puzzle. All you need to do to make it solvable again is switch any two tiles, so for example the top first two tiles (so 5 and 3 in the example). When the number of switches needed is an even number, it's already solvable and you don't need to do anything.
I've made similar puzzle games, you can see the source code here to see how it works:
Photoscramble v2 (download incl Delphi source)
Photoscramble v1 (download incl BlitzBasic source)

Bilateral filter algorithm

I'm trying to implement a simple bilateral filter in javascript. This is what I've come up with so far:
// For each pixel
for (var y = kernelSize; y < height-kernelSize; y++) {
for (var x = kernelSize; x < width-kernelSize; x++) {
var pixel = (y*width + x)*4;
var sumWeight = 0;
outputData[pixel] = 0;
outputData[pixel+1] = 0;
outputData[pixel+2] = 0;
outputData[pixel+3] = inputData[pixel+3];
// For each neighbouring pixel
for(var i=-kernelSize; i<=kernelSize; i++) {
for(var j=-kernelSize; j<=kernelSize; j++) {
var kernel = ((y+i)*width+x+j)*4;
var dist = Math.sqrt(i*i+j*j);
var colourDist = Math.sqrt((inputData[kernel]-inputData[pixel])*(inputData[kernel]-inputData[pixel])+
(inputData[kernel+1]-inputData[pixel+1])*(inputData[kernel+1]-inputData[pixel+1])+
(inputData[kernel+2]-inputData[pixel+2])*(inputData[kernel+2]-inputData[pixel+2]));
var curWeight = 1/(Math.exp(dist*dist/72)*Math.exp(colourDist*colourDist*8));
sumWeight += curWeight;
outputData[pixel] += curWeight*inputData[pixel];
outputData[pixel+1] += curWeight*inputData[pixel+1];
outputData[pixel+2] += curWeight*inputData[pixel+2];
}
}
outputData[pixel] /= sumWeight;
outputData[pixel+1] /= sumWeight;
outputData[pixel+2] /= sumWeight;
}
}
inputData is from a html5 canvas object and is in the form of rgba.
My images are either coming up with no changes or with patches of black around edges depending on how i change this formula:
var curWeight = 1/(Math.exp(dist*dist/72)*Math.exp(colourDist*colourDist*8));
Unfortunately I'm still new to html/javascript and image vision algorithms and my search have come up with no answers. My guess is there is something wrong with the way curWeight is calculated. What am I doing wrong here? Should I have converted the input image to CIElab/hsv first?
I'm no Javasript expert: Are the RGB values 0..255? If so, Math.exp(colourDist*colourDist*8) will yield extremely large values - you'll probably want to scale colourDist to the range [0..1].
BTW: Why do you calculate the sqrt of dist and colourDist if you only need the squared distance afterwards?
First of all, your images turn out black/weird in the edges because you don't filter the edges. A short look at your code would show that you begin at (kernelSize,kernelSize) and finish at (width-kernelSize,height-kernelSize) - this means that you only filter a smaller rectangle inside the image where your have a margin of kernelSize on each side which is unfilterred. Without knowing your javscript/html5, I would assume that your outputData array is initialized with zero's (which means black) and then not touching them would leave them black. See my link the comment to your post for code that does handle the edges.
Other than that, follow #nikie's answer - your probably want to make sure the color distance is clamped to the range of [0,1] - youo can do this by adding the line colourDist = colourDist / (MAX_COMP * Math,sqrt(3)) (directly after the first line to calculate it). where MAX_COMP is the maximal value a color component in the image can have (usually 255)
I've found the error in the code. The problem was I was adding each pixel to itself instead of its surrounding neighbours. I'll leave the corrected code here in case anyone needs a bilateral filter algorithm.
outputData[pixel] += curWeight*inputData[kernel];
outputData[pixel+1] += curWeight*inputData[kernel+1];
outputData[pixel+2] += curWeight*inputData[kernel+2];

Categories

Resources