How to optimize this simple pathfinding - javascript

//Editable Vars
let cols = 35;
let rows = 35;
let fps = 5;
//Declarations
let canvas;
let ctx;
let background;
let grid = new Array(cols);
let w;
let h;
let pathfinder;
let target;
let timer;
let renderQueue = [];
let canPathfind = true;
//Space Class
class Space{
constructor(x,y,c='lightgrey'){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.c = c;
}
draw(){
ctx.fillStyle = this.c;
ctx.strokeStyle = 'black';
ctx.fillRect(this.x * w, this.y * h, this.w, this.h);
ctx.rect(this.x * w, this.y * h, this.w, this.h);
ctx.stroke();
}
move(x,y){
if(x < 0 || x > cols-1 || y < 0|| y > rows-1){
return;
}
grid[this.x][this.y] = new Space(this.x,this.y);
renderQueue.push(grid[this.x][this.y]);
this.x = x;
this.y = y;
grid[this.x][this.y] = this;
renderQueue.push(grid[this.x][this.y]);
}
}
//Game-Run code
function gameStart(){
canvas = document.getElementById('gameCanvas');
ctx = canvas.getContext('2d');
w = canvas.width / cols;
h = canvas.height / rows;
createGrid();
pathfinder = new Space(randomInt(cols-1),randomInt(rows-1),'green');
grid[pathfinder.x][pathfinder.y] = pathfinder;
target = new Space(randomInt(cols-1),randomInt(rows-1),'red');
grid[target.x][target.y] = target;
drawGrid();
timer = setInterval(updateScreen, 1000/fps);
}
function restartGame(){
clearInterval(timer);
gameStart();
}
//Starts loading process on windows load
window.onload = gameStart();
//Checks all 8 possible move directions and calls pathfinder.move for best move
function pathfind(pathfinder, target){
if(!canPathfind) return;
let p = {x: pathfinder.x, y: pathfinder.y};
let t = {x: target.x, y: target.y};
let move = {x : 0, y : 0};
// 0,0 9,9
//if(p.x == t.x && p.y == t.y){
//restartGame();
//}
if(t.x - p.x >= 1){
move.x = 1;
}else if(t.x - p.x <= -1){
move.x = -1;
}else{
move.x = 0;
}
if(t.y - p.y >= 1){
move.y = 1;
}else if(t.y - p.y <= -1){
move.y = -1;
}else{
move.y = 0;
}
pathfinder.move(pathfinder.x + move.x, pathfinder.y + move.y);
}
function updateScreen(){
pathfind(pathfinder,target);
drawUpdatedSpaces();
}
function drawUpdatedSpaces(){
for(let i = 0; i < renderQueue.length; i++){
renderQueue[i].draw();
}
renderQueue = [];
}
function drawGrid(){
for(let i = 0; i < grid.length; i++){
for(let j = 0; j < grid[i].length; j++){
grid[i][j].draw();
}
}
}
//Creates grid and instantiates Space in every cell
function createGrid(){
for(let i = 0; i < grid.length; i++){
grid[i] = new Array(rows);
}
for(let i = 0; i < grid.length; i++){
for(let j = 0; j < grid[i].length; j++){
grid[i][j] = new Space(i,j);
}
}
}
// Returns distance to target from specified coords
function distanceFromTarget(x, y) {
return (Math.sqrt(Math.pow(Math.abs(x - target.x), 2) + (Math.pow(Math.abs(y - target.y), 2))));
}
// Returns random Integer between 0 and Max
function randomInt(max) {
return Math.floor(Math.random() * max);
}
It runs as expected which is great, but performance is super slow. That may be because I'm using jsfiddle to work on this while away from my personal PC setup, but is there a way to make this perform better? As of right now I can't move the grid size to >50 without it running extremely slow. I would love to eventually move to a 4k x 4k grid and create an auto-generating maze for the 'pathfinder' to pathfind through.
Thoughts/things I'm considering for performance:
Using HTML grid and updating via CSS instead of HTML5 Canvas
Only re-drawing cells that have changed (Implemented in the Space.move() function with array renderQueue)
literally re-doing everything in python :D

Related

I cant figure out how to make my loop work in my space invader like game in Javascript

For my space invader like game I want to make bullets that get shot by the enemies but the problem is that the bullets dont appear in this loop that I made. I tried several things like making only one bullet appear while that works it is not the result that I want. My idea behind the code is that a new bullet gets shot from the position of the enemies when the enemybullet.position == 1 and if it exceeds the height of the canvas I want the bullet to return to the enemies and be shot again.
The code that I used for this result is here:
sketch.js
var enemies = [];
var enemybullet = [];
function setup() {
createCanvas(800, 600);
for (var i = 0; i < 2; i++) {
enemies[i] = new Enemy(i*300+300, 100);
}
// I tried enemybullet = new Enemybullet(120, 200); and that drew the bullet but it wasnt assigned to the enemy
rectMode(CENTER);
}
function draw() {
background(0);
// enemybullet.show(); this is wat I used to draw the single projectile
// enemybullet.move();
var edge = false;
for (var i = 0; i < enemies.length; i++) {
enemies[i].show();
enemies[i].move();
if(enemies[i].x > width || enemies[i].x < 0) {
edge = true;
}
}
if (edge) {
for (var i = 0; i < enemies.length; i++) {
enemies[i].shiftDown();
}
}
for (var i = 0; i < enemybullet.length; i++) {
enemybullet[i].show();
enemybullet[i].move();
for (var j = 0; j < enemies.length; j++) {
if(enemybullet[i].position == 1){
var enemybullets = new Enemybullet(enemies[j].x(), enemies[j].y());
enemybullet.push(enemybullets);
enemybullet[i].x = enemybullet[i].x;
enemybullet[i].y = enemybullet[i].y + enemybullet[i].speed;
if(enemybullet[i].y >= height){
enemybullet[i].position = 2;
}
}
else{
enemybullet[i].y = enemies[i].y;
enemybullet[i].x = enemies[i].x;
}
if(enemybullet[i].position == 2 ){
enemybullet[i].y = enemies[i].y;
enemybullet[i].x = enemies[i].x;
enemybullet[i].position = 1;
}
}
}
enemybullet.js
function Enemybullet(x, y) {
this.x = x;
this.y = y;
this.width = 10;
this.height = 20;
this.position = 1;
this.speed = 2;
this.show = function() {
fill('#ADD8E6');
rect(this.x, this.y, this.width, this.height);
}
this.move = function() {
this.x = this.x;
this.y = this.y + this.speed;
}
}
enemy.js
function Enemy(x, y) {
this.x = x;
this.y = y;
this.r = 100;
this.xdir = 1;
this.shot = function() {
this.r = this.r * 0;
this.xdir = this.xdir * 0;
}
this.shiftDown = function() {
this.xdir *= -1;
this.y += this.r/2;
}
this.show = function() {
fill('#0000FF');
rect(this.x, this.y, this.r, this.r);
}
this.move = function() {
this.x = this.x + this.xdir;
}
}
Rather than having the bullets be in their own global array, try having the bullets be a variable in the Enemy class.
this.bullet = new Enemybullet(this.x, this.y);
Then you can give the enemies functions such as the following to update the bullets.
this.updateBullet = function() {
this.bullet.move();
this.bullet.show();
}
this.resetBullet = function() {
this.bullet = new Enemybullet(this.x, this.y);
}
Where you were looping through each bullet before, you can instead call enemies[i].updateBullet(); when you move and show the enemies.
And when you want the enemies to shoot another shot, call enemies[i].resetBullet();
You can see my implementation here.

Fix particle system errors in processing

I'm trying to set up a feather-looking particle system in processing. I am trying to base it off of some code I found on OpenProcessing. When I copy and paste the code into processing (using Java) I get an error saying "expecting SEMI, found 'points'.
I figured this was maybe because the code uses var and not int indicating that this would be Javascript code instead. So I switched the processing mode to p5.js, and it runs but the browser that opens is just a blank white screen.
Any help on getting this to run would be appreciated! Thanks!
The code is below:
var points = [];
var painting = false;
var strokeNumber = 0;
var scl = 6;
var cols, rows;
var inc = 0.1;
var zOff = 0;
var particles = [];
var flowField = [];
var saturation = [];
function setup() {
createCanvas(windowWidth, windowHeight);
// createCanvas(400, 400);
background(0);
pixelDensity(5);
cols = floor(width / scl);
rows = floor(height / scl);
flowField = Array(cols * rows);
saturation = Array(width * height).fill(0);
greateForceField();
}
function mousePressed() {
painting = true;
strokeNumber++;
}
function mouseReleased() {
painting = false;
}
function updateForceField(){
var v = createVector(mouseX, mouseY);
var vPrev = createVector(pmouseX, pmouseY);
v.sub(vPrev);
v.setMag(1);
var i = floor(mouseX / scl);
var j = floor(mouseY / scl);
var index = i * rows + j;
flowField[index] = v;
}
function showForceField(){
for(var i = 0; i < cols; i++){
for(var j = 0; j < rows; j++){
var index = i * rows + j;
var v = flowField[index];
stroke(0,50);
strokeWeight(1);
push();
translate(i * scl, j * scl);
rotate(v.heading());
line(0,0,scl,0);
pop();
}
}
}
function greateForceField(){
var xOff = 0;
for(var i = 0; i < cols; i++){
var yOff = 0;
for(var j = 0; j < rows; j++){
yOff += inc;
var angle = noise(xOff, yOff, zOff) * TWO_PI;
var v = p5.Vector.fromAngle(angle);
v.setMag(.1);
var index = i * rows + j;
flowField[index] = v;
}
xOff += inc;
}
// zOff += inc * 0.1;
}
function draw() {
// background(255);
// showForceField();
if(painting){
updateForceField();
var idx = mouseY * width + mouseX;
if(saturation[idx] < 10){
var r = 1+sqrt(sq(mouseX-pmouseX)+sq(mouseY-pmouseY));
for(var a = 0; a < 100; a++){
var particle = new Particle(mouseX+random()*r*cos(random(TWO_PI)), mouseY+random()*r*sin(random(TWO_PI)));
particles.push(particle);
}
saturation[idx] ++;
}
}
particles.filter(particle => particle.spread > 0).map(particle => {
particle.update();
particle.show();
// particle.edges();
particle.follow();
})
particles.map((particle, idx) => {
if(particle.spread <= 0){
particles.splice(idx,1);
}
});
}
function Particle(x,y){
this.pos = createVector(x,y);
// this.color = color(245, 225, 50);
// this.color = color(145, 225, 192);
this.color = color(255);
this.spread = 127;
this.spreadInc = this.spread/100;
this.prevPos = this.pos.copy();
this.vel = p5.Vector.random2D();
this.acc = createVector(0,0);
this.maxSpeed = 2;
this.update = function(){
this.spread -= this.spreadInc;
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.mult(0);
}
this.applyForce = function(force){
this.acc.add(force);
}
this.follow = function(){
var i = floor(this.pos.x / scl);
var j = floor(this.pos.y / scl);
var index = i * rows + j;
var force = flowField[index];
this.applyForce(force);
}
this.show = function(){
stroke(red(this.color),green(this.color),blue(this.color),this.spread);
strokeWeight(.3*this.spread/127);
// point(this.pos.x, this.pos.y);
line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
this.updatePrev();
}
this.updatePrev = function(){
this.prevPos = this.pos.copy();
}
this.edges = function(){
if(this.pos.x > width) {
this.pos.x = 0;
this.updatePrev();
}
if(this.pos.x < 0){
this.pos.x = width;
this.updatePrev();
}
if(this.pos.y > height){
this.pos.y = 0;
this.updatePrev();
}
if(this.pos.y < 0) {
this.pos.y = height;
this.updatePrev();
}
}
}
This looks like javascript waaay more than java. I'm not exactly a buff in these matters, but... are you trying to run javascript as java?
If you are using the Processing IDE, look in the upper right corner. Do you see the word "Java" ?
Like this:
If this is the case, you might want to consider installing p5.js :
Click here and choose "Add mode":
Now search for p5.js and install it:
Now your code will compile. I'm not saying it'll work, though, but your current problem will be behind you. Have fun!

Multiple intersecting squares

This is in continuation to my previous post here Changing color of intersecting area of squares
I was able to make the intersecting area of the squares change color. The issue was that I wanted multiple intersecting squares and not a single one so I modified the code. Now the problem is that the square is leaving a trail since it is drawing square wherever it moves. And it starts to slow down after a while. How can I overcome this
function draw() {
background(135,206,250);
myColour = (255);
// if a square is being dragged, update its position
if (this.dragObject != null) {
this.dragObject.position.x = mouseX;
this.dragObject.position.y = mouseY;
}
//draw all squares
for (let i = 0; i < squares.length; i++) {
let s = squares[i];
s.show();
}
for (let i = 0; i < squares.length; i++) {
for (let j = i + 1; j < squares.length; j++) {
//block checking collision
if (i != j && squares[i].collides(squares[j])) {
squares[i].changecolor();
//set intersection color
fill(myColour);
//calculate parameters
newX = Math.max(squares[i].position.x, squares[j].position.x);
newY = Math.max(squares[i].position.y, squares[j].position.y);
newW = Math.min(squares[i].position.x + squares[i].w, squares[j].position.x + squares[j].w) - newX;
newH = Math.min(squares[i].position.y + squares[i].h, squares[j].position.y + squares[j].h) - newY;
//draw rectangle
let Osquare = new OverlappingSquares(newX, newY, newW, newH);
overlappingsquares.push(Osquare);
}
}
}
}
changecolor() {
// fill(random(255), random(255), random(255));
// background(200, 255, 100);
for (let i = 0; i < squares.length; i++) {
let s = squares[i];
s.show();
}
for (let i = 0; i < overlappingsquares.length; i++) {
let s = overlappingsquares[i];
s.show();
}
}
//Overlapping sqaures class
class OverlappingSquares {
constructor(X, Y, W, H) {
this.w = W;
this.h = H;
this.position = {
x: X,
y: Y
};
}
show() {
fill(myColour)
rect(this.position.x, this.position.y, this.w, this.h);
}
}

Update function in Conway's game of life is not working

I am trying to make Conway's Game of Life in JavaScript, but cannot find what is wrong with a specific function after hours of debugging.
The program works by making a 2d array based of global "row" and "column" variables, and then pushes "Square" objects to each space in the array. The program then sets an interval for the "draw" function for a specific interval, (the global variable "rate").
I apologize if this is hard to understand, but basically, every specific time interval, like every 1000 milliseconds, the program checks every "Square" object in the array, updates the amount of neighbors it has, and then draws it on the screen.
This is where I am stuck; the update function is supposed to check all 8 neighbors that a square has, (or 3-5 if it is an edge square) but it will only check 4 neighbors. No matter what I do, if I click a square to populate it, only the top, top left, top right, and left neighbors of the now populated square will register that their neighbor has become populated.
Other than this bug the code is working fine, and i'm 99% sure the problem is in this one function, because the code will still function as a cellular automata currently, just not as Conway's Game of Life.
var canvas = document.getElementById("life");
var ctx = canvas.getContext('2d');
var timer;
var rate = 1000;
var rows = 20;
var columns = 20;
var width = 20;
var clickX;
var clickY;
var board;
var running = false;
var checkArray = [
[-1, 0, 1, 1, 1, 0, -1, -1],
[-1, -1, -1, 0, 1, 1, 1, 0]
];
var visuals = true;
var gridColor = "#000000";
/*
live cell with < 2 neighbors = death
live cell with 2 or 3 neighbors = live for 1 generation
live cell with > 4 neighbors = death
dead cell with 3 neighbors = live for 1 generation
0 = death
1 = death
2 = continues life if alive
3 = continues life if alive OR brings to life if dead
4 = death
5 = death
6 = death
7 = death
8 = death
*/
window.onload = function() {
makeBoard();
var timer = setInterval(draw, rate);
window.addEventListener("mousedown", clickHandler);
// for(var i = 0; i < 8; i++){
// var checkIndexX = checkArray[0][i];
// var checkIndexY = checkArray[1][i];
// console.log(checkIndexX, checkIndexY);
// }
}
function makeBoard() {
board = new Array(columns);
for (var i = 0; i < rows; i++) {
var intRow = new Array(rows);
for (var j = 0; j < columns; j++) {
intRow[j] = new Square(false, j, i);
}
board[i] = intRow;
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var y = 0; y < rows; y++) {
for (var x = 0; x < columns; x++) {
if (running) {
board[y][x].update();
}
board[y][x].draw();
if (visuals) {
board[y][x].visuals();
}
}
}
drawRunButton();
}
function Square(alive, PARX, PARY) {
this.alive = false;
this.X = PARX;
this.Y = PARY;
this.neighbors = 0;
this.update = function() {
this.neighbors = 0;
for (var i = 0; i < 8; i++) {
var checkIndexX = checkArray[0][i];
var checkIndexY = checkArray[1][i];
if ((this.X + checkIndexX) >= 0 && (this.X + checkIndexX) < columns &&
(this.Y + checkIndexY) >= 0 && (this.Y + checkIndexY) < rows) {
var check = board[this.Y + checkIndexY][this.X + checkIndexX];
// console.log(this.X, this.Y, check.X, check.Y, checkIndexX, checkIndexY);
if (check.alive) {
this.neighbors++;
}
}
}
if (this.alive) {
if (this.neighbors < 2 || this.neighbors > 3) {
this.alive = false;
}
} else {
if (this.neighbors == 3) {
this.alive = true;
}
}
};
this.visuals = function() {
drawVisuals(this.neighbors, this.X * width, this.Y * width);
};
this.draw = function(alive) {
drawSquare(this.alive, this.X * width, this.Y * width, width);
}
}
function clickHandler(e) {
var clickX = e.screenX - 68;
var clickY = e.screenY - 112;
mapClick(clickX, clickY);
manageRun(clickX, clickY);
}
function mapClick(x, y) {
var indexX = Math.floor(x / width);
var indexY = Math.floor(y / width);
if (indexX >= 0 && indexX < columns && indexY >= 0 && indexY < rows) {
board[indexY][indexX].alive = true;
}
}
function manageRun(x, y) {
if (x >= (columns * width) + 5 && x <= (columns * width) + 45 && y >= 5 && y <= 45) {
if (running) {
running = false;
} else {
running = true;
}
console.log(running);
}
}
function drawRunButton() {
drawSquare(false, (columns * width) + 5, 5, 40)
}
function drawSquare(fill, x, y, width) {
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + width, y);
ctx.lineTo(x + width, y + width);
ctx.lineTo(x, y + width);
ctx.lineTo(x, y);
if (fill) {
ctx.fillStyle = gridColor;
ctx.fill();
} else {
ctx.strokeStyle = gridColor;
ctx.stroke();
}
}
function drawVisuals(neighbors, x, y) {
ctx.beginPath();
ctx.fillStyle = "#ff0000";
ctx.font = '20px serif';
ctx.fillText(neighbors, x + (width / 3), y + (width / 1.25));
}
<!DOCTYPE html>
<html>
<head>
<title>template.com</title>
<link href="style.css" type="text/css" rel="stylesheet">
</head>
<body>
<canvas id="life" width="1400" height="700" style="border: 1px solid black"></canvas>
</body>
</html>

Ball Bounce - javascript

function randomXToY(minVal,maxVal,floatVal)
{
var randVal = minVal+(Math.random()*(maxVal-minVal));
return typeof floatVal=='undefined'?Math.round(randVal):randVal.toFixed(floatVal);
}
Ball = (function() {
// constructor
function Ball(x,y,radius,color){
this.center = {x:x, y:y};
this.radius = radius;
this.color = color;
this.dx = 2;
this.dy = 2;
this.boundaryHeight = $('#ground').height();
this.boundaryWidth = $('#ground').width();
this.dom = $('<p class="circle"></p>').appendTo('#ground');
// the rectange div a circle
this.dom.width(radius*2);
this.dom.height(radius*2);
this.dom.css({'border-radius':radius,background:color});
this.placeAtCenter(x,y);
}
// Place the ball at center x, y
Ball.prototype.placeAtCenter = function(x,y){
this.dom.css({top: Math.round(y- this.radius), left: Math.round(x - this.radius)});
this.center.x = Math.round(x);
this.center.y = Math.round(y);
};
Ball.prototype.setColor = function(color) {
if(color) {
this.dom.css('background',color);
} else {
this.dom.css('background',this.color);
}
};
// move and bounce the ball
Ball.prototype.move = function(){
var diameter = this.radius * 2;
var radius = this.radius;
if (this.center.x - radius < 0 || this.center.x + radius > this.boundaryWidth ) {
this.dx = -this.dx;
}
if (this.center.y - radius < 0 || this.center.y + radius > this.boundaryHeight ) {
this.dy = -this.dy;
}
this.placeAtCenter(this.center.x + this.dx ,this.center.y +this.dy);
};
return Ball;
})();
var number_of_balls = 5;
var balls = [];
$('document').ready(function(){
for (i = 0; i < number_of_balls; i++) {
var boundaryHeight = $('#ground').height();
var boundaryWidth = $('#ground').width();
var y = randomXToY(30,boundaryHeight - 50);
var x = randomXToY(30,boundaryWidth - 50);
var radius = randomXToY(15,30);
balls.push(new Ball(x,y,radius, '#'+Math.floor(Math.random()*16777215).toString(16)));
}
loop();
});
loop = function(){
for (var i = 0; i < balls.length; i++){
balls[i].move();
}
setTimeout(loop, 8);
};
I have never used in oops concepts in javascript. How do I change the ball color when the balls touches each other?
This is the link : http://jsbin.com/imofat/1/edit
You currently don't have any interaction with the balls. What you can do is checking whether two balls are "inside" each other, and change colors in that case: http://jsbin.com/imofat/1491/.
// calculates distance between two balls
var d = function(a, b) {
var dx = b.center.x - a.center.x;
var dy = b.center.y - a.center.y;
return Math.sqrt(dx*dx + dy*dy);
};
and:
// for each ball
for(var i = 0; i < balls.length; i++) {
// check against rest of balls
for(var j = i + 1; j < balls.length; j++) {
var a = balls[i];
var b = balls[j];
// distance is smaller than their radii, so they are inside each other
if(d(a, b) < a.radius + b.radius) {
// set to some other color using your random color code
a.setColor('#'+Math.floor(Math.random()*16777215).toString(16));
b.setColor('#'+Math.floor(Math.random()*16777215).toString(16));
}
}
}
Still, there are things for improvement:
Balls are changing colors as long as they are inside each other, not just once.
If you want them to "touch", you might want to implement some kind of bouncing effect to make it more realistic.

Categories