Related
//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
I'm trying to make trails of moving objects by using vector history array in p5js.
but after push updated vector, all elements in this.history replaced as last one.
I've searched some question here but still can't understand.
let ppp = [];
function setup() {
createCanvas(400, 400);
for (let i = 0; i < 3; i++) {
let p = new Particle();
ppp.push(p);
}
}
function draw() {
background(220);
for (let i = 0; i < ppp.length; i++) {
ppp[i].display();
ppp[i].update();
}
}
function Particle() {
this.pv = createVector(random(width), random(height));
this.history = [];
let rndV = p5.Vector.random2D();
this.spdV = rndV.mult(random(1, 3));
this.update = function() {
this.pv.add(this.spdV);
this.history.push(this.pv); // replace all vector element
console.log(this.history);
}
this.display = function() {
fill(30);
ellipse(this.pv.x, this.pv.y, 30);
for (let i = 0; i < this.history.length; i++) {
let trail = this.history[i];
ellipse(trail.x, trail.y, 10);
}
}
}
or if you think my approach isn't the best, I'll be happy to hear any suggestion^^
Thanks,
This can be a bit misleading in javascript:
this.history.push(this.pv);
You're pushing a reference to the same this.pv pre-allocated vector
What you are trying to do is something like:
this.history.push(this.pv.copy());
Where you are allocating memory for a completely new p5.Vector object with the x,y coordinates copied from this.pv (using the copy() method)
Demo:
let ppp = [];
function setup() {
createCanvas(400, 400);
for (let i = 0; i < 3; i++) {
let p = new Particle();
ppp.push(p);
}
}
function draw() {
background(220);
for (let i = 0; i < ppp.length; i++) {
ppp[i].display();
ppp[i].update();
}
}
function Particle() {
this.pv = createVector(random(width), random(height));
this.history = [];
let rndV = p5.Vector.random2D();
this.spdV = rndV.mult(random(1, 3));
this.update = function() {
this.pv.add(this.spdV);
this.history.push(this.pv.copy()); // replace all vector element
//console.log(this.history);
}
this.display = function() {
fill(30);
ellipse(this.pv.x, this.pv.y, 30);
for (let i = 0; i < this.history.length; i++) {
let trail = this.history[i];
ellipse(trail.x, trail.y, 10);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
Bare in mind as the sketch runs this will use more and more memory.
If simply need to render the trails and don't need the vector data for anything else you can simply render into a separate graphics layer (using createGraphics()) immediately which will save memory on the long run:
let ppp = [];
let trailsLayer;
function setup() {
createCanvas(400, 400);
// make a new graphics layer for trails
trailsLayer = createGraphics(400, 400);
trailsLayer.noStroke();
trailsLayer.fill(0);
for (let i = 0; i < 3; i++) {
let p = new Particle();
ppp.push(p);
}
}
function draw() {
background(220);
// render the trails layer
image(trailsLayer, 0, 0);
for (let i = 0; i < ppp.length; i++) {
ppp[i].display();
ppp[i].update();
}
}
function Particle() {
this.pv = createVector(random(width), random(height));
let rndV = p5.Vector.random2D();
this.spdV = rndV.mult(random(1, 3));
this.update = function() {
this.pv.add(this.spdV);
// render trails
trailsLayer.ellipse(this.pv.x, this.pv.y, 10);
}
this.display = function() {
fill(30);
ellipse(this.pv.x, this.pv.y, 30);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
Update to fade trails you could try something like Moving On Curves example. Notice noStroke(); is called in setup() and
fill(0, 2);
rect(0, 0, width, height);
render a faded out (alpha=2) rectangle ?
You could do something similar:
let ppp = [];
let trailsLayer;
function setup() {
createCanvas(400, 400);
background(255);
// make a new graphics layer for trails
trailsLayer = createGraphics(400, 400);
trailsLayer.noStroke();
// set translucent fill for fade effect
trailsLayer.fill(255, 25);
for (let i = 0; i < 3; i++) {
let p = new Particle();
ppp.push(p);
}
}
function draw() {
background(220);
// fade out trail layer by rendering a faded rectangle each frame
trailsLayer.rect(0, 0, width, height);
// render the trails layer
image(trailsLayer, 0, 0);
for (let i = 0; i < ppp.length; i++) {
ppp[i].display();
ppp[i].update();
}
}
function Particle() {
this.pv = createVector(random(width), random(height));
let rndV = p5.Vector.random2D();
this.spdV = rndV.mult(random(1, 3));
this.update = function() {
this.pv.add(this.spdV);
// reset at bounds
if(this.pv.x > width){
this.pv.x = 0;
}
if(this.pv.y > height){
this.pv.y = 0;
}
if(this.pv.x < 0){
this.pv.x = width;
}
if(this.pv.y < 0){
this.pv.y = height;
}
// render trails
trailsLayer.push();
trailsLayer.fill(0);
trailsLayer.noStroke();
trailsLayer.ellipse(this.pv.x, this.pv.y, 10);
trailsLayer.pop();
}
this.display = function() {
fill(30);
ellipse(this.pv.x, this.pv.y, 30);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
For the sake of completeness here's a version using the history vector array, but limiting that to a set size and reusing vectors allocated once (instead making new ones continuously):
let ppp = [];
function setup() {
createCanvas(400, 400);
noStroke();
for (let i = 0; i < 3; i++) {
let p = new Particle();
ppp.push(p);
}
}
function draw() {
background(220);
for (let i = 0; i < ppp.length; i++) {
ppp[i].display();
ppp[i].update();
}
}
function Particle() {
this.pv = createVector(random(width), random(height));
// limit number of history vectors
this.historySize = 24;
this.history = new Array(this.historySize);
// pre-allocate all vectors
for(let i = 0 ; i < this.historySize; i++){
this.history[i] = this.pv.copy();
}
let rndV = p5.Vector.random2D();
this.spdV = rndV.mult(random(1, 6));
this.update = function() {
this.pv.add(this.spdV);
this.resetBounds();
this.updateHistory();
};
this.updateHistory = function(){
// shift values back to front by 1 (loop from last to 2nd index)
for(let i = this.historySize -1; i > 0; i--){
// copy previous to current values (re-using existing vectors)
this.history[i].set(this.history[i-1].x, this.history[i-1].y);
}
// finally, update the first element
this.history[0].set(this.pv.x, this.pv.y);
};
this.resetBounds = function(){
// reset at bounds
if(this.pv.x > width){
this.pv.x = 0;
}
if(this.pv.y > height){
this.pv.y = 0;
}
if(this.pv.x < 0){
this.pv.x = width;
}
if(this.pv.y < 0){
this.pv.y = height;
}
};
this.display = function() {
fill(30);
ellipse(this.pv.x, this.pv.y, 30);
for (let i = 0; i < this.historySize; i++) {
let trail = this.history[i];
// fade trails
let alpha = map(i, 0, this.historySize -1, 192, 0);
fill(30, alpha);
ellipse(trail.x, trail.y, 10);
}
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
In my UFO shooter game I would like the player to be able to spike the closest UFO, to insure chaos and awesomeness. Lol. Anyways, I can't seem to figure out how to store the closest current UFO (or drone, as I call them in the code)...
Here are all of the functions and classes for the game. (kinda messy, I know, but I'll probably revise it later)
var score = 0;
var bullets = [];
var drones = [];
var keys = [];
keyPressed = function () {
keys[keyCode]=true;
};
keyReleased = function () {
keys[keyCode]=false;
};
var player = function (x,y) {
this.x = 25;
this.y = 440;
this.w = 15;
this.h = 20;
this.xvel = 0;
this.yvel = 0;
this.accel = 0.2;
this.frict = 0.08;
this.isShooting = false;
this.update = function () {
this.leftGun = this.x-11-this.xvel;
this.rightGun = this.x+20-this.xvel;
if (this.isShooting) {
this.gunsY = random(this.y,this.y+7);
} else {
this.gunsY = this.y+3;
}
if (keys[LEFT]) {
this.xvel -= this.accel;
}
if (keys[RIGHT]) {
this.xvel += this.accel;
}
if (keys[UP]) {
this.yvel -= this.accel;
}
if (keys[DOWN]) {
this.yvel += this.accel;
}
if (this.xvel > 0 || this.xvel < 0) {
this.xvel -= this.xvel * this.frict;
}
if (this.yvel > 0 || this.yvel < 0) {
this.yvel -= this.yvel * this.frict;
}
fill(255, 255, 255);
//rect(this.x-this.w/2,this.y + 5,this.w,this.h);
triangle(this.x,this.y+this.h-this.xvel+this.yvel,this.x+this.xvel+7,this.y,this.x+15,this.y+this.h+this.xvel+this.yvel);
fill(0,0,255);
//ellipse(this.x+7,this.y+14,7,7);
rect(this.leftGun,this.gunsY,4,15);
rect(this.rightGun,this.gunsY,4,15);
this.x += this.xvel;
this.y += this.yvel;
};
};
var p1 = new player();
// verify status
var active = function(obj){
return obj.x>0 && obj.y<height+100;
};
var bulletActive = function(blt){
return blt.x>0 && blt.x < width && blt.y>0 && blt.y<height+20;
};
var droneArt = function(){
var zz = this.z/2;
var dam = map(this.h, 0, 100, 0, this.z);
if (this.h<0){
this.x = -100;
score+=(80-this.z);
return;
}
fill(255, 0, 0);
rect(this.x-zz, this.y-zz-10, this.z, 4);
fill(0, 255, 0);
rect(this.x-zz, this.y-zz-10, dam, 4);
pushMatrix();
translate(this.x,this.y);
//rotate(r);
fill(255, 255, 255);
ellipse(0,0,this.z,this.z);
fill(0, 0, 0);
ellipse(0,0,this.z-this.z/1.8,this.z-this.z/1.8);
for(var i = 0; i < 360; i += 45){
rotate(i);
fill(0, 0, 0);
noStroke();
ellipse(this.z-this.z/1.6,0,this.z-this.z/1.15,this.z-this.z/1.15);
}
//r++;
popMatrix();
this.y += 0.4;
/*
for(var i = 0; i < 360; i += 45){
rotate(i);
fill(255,0,0);
ellipse(this.z-this.z/1.6,0,this.z-this.z/2,5);
}
*/
};
// drone object
var drone = function(x, y){
return {
x:x,
y:y,
z:20 + floor(random(2)) * 20,
h:100,
draw:droneArt };
};
// bullet object
var bullet = function(x, y, s, id){
return {
x:x, y:y, s:s, z:3,
draw: function(){
if (id==="canon"){
this.y -= 7;
fill(255, 97, 97);
ellipse(this.x+2,this.y+9,this.z*3,this.z*4);
fill(255, 0, 0);
ellipse(this.x,this.y+9,this.z*3.2,this.z*4.2);
} else {
this.y -= 14;
fill(102, 102, 255);
rect(this.x-this.z/2, this.y, this.z, this.z+10);
}
}
};
};
// collision test
var kill = function(obj){
var test = function(blt){
if (dist(obj.x, obj.y, blt.x, blt.y)<(obj.z+blt.z)/2){
obj.h -= blt.s;
blt.x = -100;
}
};
return test;
};
var r = 0;
var drawDrone = function(drn){
//r += 0.2;
pushMatrix();
translate(drone.x,0);
rotate(r);
drn.draw();
popMatrix();
bullets.forEach(kill(drn));
};
var drawBullet = function(blt){
blt.draw();
};
var ufo;
var distance;
var minUFO = 10000;
var drones = [drone(100, 100), drone(200, -100), drone(300, 0)];
/*minUFO = minUFO === null || t > minUFO ? t : minUFO;*/
var minUFO = 9999999999;
var findNearestDrone = function () {
for(var i=0; i < drones.length; i++) {
var ufo = drones[i];
var po = dist(ufo.x,ufo.y,p1.x,p1.y);
if (po < minUFO) {
minUFO = po;
}
fill(255, 0, 0);
stroke(255,0,0);
strokeWeight(2);
line(ufo.x,ufo.y,p1.x+8,p1.y);
noStroke();
//println(ufo + " drone " + i);
text(minUFO,30,100);
}
};
It turns out that, unlike what I expected; but logically, the minUFO only ever gets smaller. Instead of storing the position of the closest current UFO, it seems to store the smallest distance between the UFO and the player ever recorded in the session. Which, turns out, isn't what I want either. Anybody know how to store the current closest drone's position? Not just the distance like I have it right now, but its actual position. Thank you, and I'm sincerely sorry for my incredible ability to take a millennia to get to the point.
You should put the var minUFO = 9999999999; inside the findNearestDrone function. That way the variable is reset every time you call the function and is no longer influenced by the previous smallest distance.
Next, similarly to the way you store the minUFO, you can use a variable that keeps track of either the ufo itself or the index that is associated with the nearest ufo (so far).
I've modified your code with both options:
//initiate vars outside function
var nearestUFO;
var nearestIndex;
var findNearestDrone = function () {
//initiate var that tracks min distance inside function
var minUFO = 9999999999;
for(var i=0; i < drones.length; i++) {
var ufo = drones[i];
var po = dist(ufo.x,ufo.y,p1.x,p1.y);
if (po < minUFO) {
minUFO = po;
//store the current nearest ufo / index
nearestUFO= ufo;
nearestIndex= i;
}
fill(255, 0, 0);
stroke(255,0,0);
strokeWeight(2);
line(ufo.x,ufo.y,p1.x+8,p1.y);
noStroke();
//println(ufo + " drone " + i);
text(minUFO,30,100);
}
};
At my school I am learning how to code in JS using a site called codehs.com. After a while I learned about graphics with JS. There was this one point where I had to create a circle:
var circle = new Circle(50);
circle.setPosition(100,100);
add(circle);
After a few days I came across another website that was teaching students how code using JS. The website was called khanacademy.org I was interested and saw that the first lesson was making drawings. I looked at the video provided and it had a different code to make a circle.
ellipse(203, 197, 300, 350);
I am confused on how to make a circle using JS since I just started.
I'm one of the founders of CodeHS. CodeHS uses a custom JavaScript library on top of regular JavaScript. Khan Academy uses Processing JS, which is a different library (You can use Processing on CodeHS as well if you like).
You can see the documentation for everything in the CodeHS JS library at https://codehs.com/docs and learn how to use it in the Intro CS in JavaScript course.
We have designed this library to be great for learning -- it gives you experience using Object Oriented Programming while making it simple to create and manipulate shapes for programs like a Helicopter Game.
Additionally, you can include the library on an HTML page that runs JavaScript by adding this script tag to your page.
<script type="text/javascript" src="https://static.codehs.com/gulp/3d065bc81d3b7edf21e928ce2c764374a03c5cd6/chs-js-lib/chs.js"></script>
Here's an example of a full HTML page that runs JavaScript and uses the CodeHS library on it to draw a circle.
<html>
<head>
<title>Circle Example</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript" src="https://static.codehs.com/gulp/3d065bc81d3b7edf21e928ce2c764374a03c5cd6/chs-js-lib/chs.js"></script>
<style>
canvas {
border: 1px solid black;
display: inline-block;
vertical-align: top;
}
pre {
border: 1px solid black;
display: inline-block;
width: 400px;
height: 500px;
background-color: #F5F5F5;
}
</style>
</head>
<body>
<h1>Circle Example</h1>
<canvas
width="400"
height="500"
class="codehs-editor-canvas"></canvas>
<script>
window.onload = function() {
var circle = new Circle(50);
circle.setPosition(100,100);
add(circle);
};
</script>
</body>
</html>
Looks like KHAN ACADEMY uses ProcessingJS to draw the circle
I was unable to check what is the library CodeHS uses to draw a circle, but has to be a different one. But the fact is that there are so many good libraries developed in javascript to make whatever you can imagine. They're generally different one from another but their goal is to make our life easier.
JavaScript library | Wikipedia
What's a JS library? | KHAN ACADEMY
I tried using the processing platform for CodeHs, just copying and pasting this code:
/**
* This program finds the shortest path through a series of obstacles using A* search,
* smooths the path, then uses PID control to drive a robot to the goal.
**/
// You can play around with these constants---
var NUM_BLOCKS = 20;
var OBSTACLE_PROBABILITY = 0.2;
var MOVE_NOISE = 0.1;
var STEERING_NOISE = 0.1;
var ROBOT_SPEED = 0.5;
var MAX_STEERING_ANGLE = Math.PI/4.0;
var tP = 2.0;
var tI = 0.0001;
var tD = 16.0;
var weightData = 0.1;
var weightSmooth = 0.1;
// Search types
var A_STAR = 0;
var GREEDY = 1;
var BREADTH_FIRST = 2;
var DEPTH_FIRST = 3;
var SEARCH_MODE = A_STAR;
// --------------------------------------------
var BLOCK_SIZE = width/NUM_BLOCKS;
var START = 2;
var GOAL_X = NUM_BLOCKS-1;
var GOAL_Y = NUM_BLOCKS-1;
var GOAL = 3;
var START_COLOR = color(23, 33, 176);
var GOAL_COLOR = color(199, 188, 68);
var FRONTIER_COLOR = color(105, 179, 105);
var EXPLORED_COLOR = color(117, 117, 117, 100);
var PATH_COLOR = color(166, 53, 53);
var SMOOTH_PATH_COLOR = color(53, 68, 166);
var PLAN = 0;
var SMOOTH = 1;
var CALIBRATE = 2;
var NAVIGATE = 3;
var DELTA = [[-1, 0], [1, 0], [0, -1], [0, 1]];
var frontier = [[0, 0, 0, 0]];
var explored = [];
var predecessors = [];
var path = [];
var smoothPath = [];
var mode = PLAN;
var index = 0;
var cte = 0;
var sigma = 0;
angleMode = "radians";
frameRate(60);
// Initialize world
var world = [];
for (var i = 0; i < NUM_BLOCKS; i++) {
var row = [];
for (var j = 0; j < NUM_BLOCKS; j++) {
var r = random();
if (r < OBSTACLE_PROBABILITY) {
row.push(1);
}
else {
row.push(0);
}
}
world.push(row);
}
world[0][0] = START;
world[GOAL_Y][GOAL_X] = GOAL;
for (var i = 0; i < NUM_BLOCKS; i++) {
var row = [];
for (var j = 0; j < NUM_BLOCKS; j++) {
row.push(false);
}
explored.push(row);
}
explored[0][0] = true;
for (var i = 0; i < NUM_BLOCKS; i++) {
var row = [];
for (var j = 0; j < NUM_BLOCKS; j++) {
row.push(null);
}
predecessors.push(row);
}
var Robot = function(x, y, size) {
this.x = x;
this.y = y;
this.vx = 0;
this.vy = 0;
this.orientation = 0;
this.size = size;
// Thanks to Sebastian Thrun for this code:
// https://www.udacity.com/course/viewer#!/c-cs373/l-48696626/e-48403941/m-48729137
// My code for this movement can be found at:
// https://www.khanacademy.org/computer-programming/bicycle-model/5496951953031168
this.move = function(d, theta) {
var generator = new Random(millis());
var moveDistance = (generator.nextGaussian() * MOVE_NOISE) + d;
var turnAngle = (generator.nextGaussian() * STEERING_NOISE) + theta;
turnAngle = min(turnAngle, MAX_STEERING_ANGLE);
turnAngle = max(turnAngle, -MAX_STEERING_ANGLE);
var turn = tan(turnAngle)*moveDistance/this.size;
// Approximately straight motion
if (abs(turn) < 0.001) {
this.x += moveDistance*cos(this.orientation);
this.y += moveDistance*sin(this.orientation);
this.orientation = (this.orientation+turn)%(2.0*Math.PI);
}
// Move using the bicyle model
else {
var radius = moveDistance/turn;
var cx = this.x-(sin(this.orientation)*radius);
var cy = this.y+(cos(this.orientation)*radius);
this.orientation = (this.orientation+turn)%(2.0*Math.PI);
this.x = cx + (sin(this.orientation)*radius);
this.y = cy - (cos(this.orientation)*radius);
}
};
this.draw = function() {
pushMatrix();
translate(this.x, this.y);
rotate(this.orientation);
fill(128, 27, 27);
stroke(255, 0, 0);
rect(-this.size/2, -(this.size*0.75*0.5), this.size, this.size*0.75);
popMatrix();
};
};
var robot;
var addToFrontier = function(node) {
// Insert the node into the frontier
// Order by lowest cost
var i = frontier.length-1;
if (SEARCH_MODE === A_STAR) {
while (i > 0 && node[2]+node[3] < frontier[i][2]+frontier[i][3]) {
i--;
}
}
else if (SEARCH_MODE === GREEDY) {
while (i > 0 && node[3] < frontier[i][3]) {
i--;
}
}
else if (SEARCH_MODE === BREADTH_FIRST) {
frontier.push(node);
}
else if (SEARCH_MODE === DEPTH_FIRST) {
frontier.splice(0, 0, node);
}
frontier.splice(i+1, 0, node);
};
var distance = function(x1, y1, x2, y2) {
return sqrt(pow(x1-x2, 2) + pow(y1-y2, 2));
};
var manhattanDistance = function(x1, y1, x2, y2) {
return abs(x1-x2) + abs(y1-y2);
};
var drawWorld = function() {
background(255, 255, 255);
for (var i = 0; i < world.length; i++) {
for (var j = 0; j < world[0].length; j++) {
if (world[i][j] === 1) {
stroke(0, 0, 0);
fill(0, 0, 0);
}
else if (world[i][j] === START) {
fill(START_COLOR);
stroke(START_COLOR);
}
else if (world[i][j] === GOAL) {
fill(GOAL_COLOR);
stroke(GOAL_COLOR);
}
else {
fill(255, 255, 255);
noStroke();
}
rect(j*BLOCK_SIZE, i*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
}
};
var simulate = function(steps) {
var error = 0;
var crosstrackError = 0;
var r = new Robot(0, 1, BLOCK_SIZE/2);
var sigma = 0;
var diff = 0;
for (var i = 0; i < steps*2; i++) {
// Compute cte
diff = r.y-crosstrackError;
crosstrackError = r.y;
sigma += crosstrackError;
if (i > steps) {
error += pow(crosstrackError, 2);
}
// Update robot
r.move(ROBOT_SPEED, -tP*crosstrackError - tD*diff - tI*sigma);
}
return error;
};
draw = function() {
drawWorld();
// Use A* to find a path to the goal
if (mode === PLAN) {
// Draw explored
fill(EXPLORED_COLOR);
noStroke();
for (var i = 0; i < explored.length; i++) {
for (var j = 0; j < explored[0].length; j++) {
if (explored[i][j]) {
rect(j*BLOCK_SIZE, i*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
}
}
// Draw frontier
fill(FRONTIER_COLOR);
noStroke();
for (var i = 0; i < frontier.length; i++) {
rect(frontier[i][0]*BLOCK_SIZE, frontier[i][1]*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
// A*
if (frontier.length > 0) {
// Remove a node from the frontier
var x = frontier[0][0];
var y = frontier[0][1];
var cost = frontier[0][2];
//println(cost + ", " + frontier[0][3]);
frontier.splice(0, 1);
// Goal check
if (world[y][x] === GOAL) {
mode = SMOOTH;
}
else {
// Add all adjacent unexplored nodes
for (var i = 0; i < DELTA.length; i++) {
// If the new position is in the world
var x2 = x + DELTA[i][0];
var y2 = y + DELTA[i][1];
if (x2 >= 0 && x2 < world[0].length && y2 >= 0 && y2 < world.length) {
// If the position is unexplored
if (!explored[y2][x2] && world[y2][x2] !== 1) {
explored[y2][x2] = true;
predecessors[y2][x2] = [x, y];
addToFrontier([x2, y2, cost+1, dist(x2, y2, GOAL_X, GOAL_Y)]);
}
}
}
}
}
else {
mode = -1;
println("No possible path to goal.");
}
}
// Smooth the path
else if (mode === SMOOTH) {
// Build path
if (path.length === 0) {
var x = GOAL_X;
var y = GOAL_Y;
path.splice(0, 0, [x, y]);
smoothPath.splice(0, 0, [x, y]);
while (x !== 0 || y !== 0) {
var newX = predecessors[y][x][0];
var newY = predecessors[y][x][1];
x = newX;
y = newY;
path.splice(0, 0, [x, y]);
smoothPath.splice(0, 0, [x, y]);
}
}
// Draw the original path
stroke(PATH_COLOR);
strokeWeight(5);
for (var i = 0; i < path.length; i++) {
point(BLOCK_SIZE*path[i][0]+BLOCK_SIZE/2, BLOCK_SIZE*path[i][1]+BLOCK_SIZE/2);
}
strokeWeight(1);
for (var i = 0; i < path.length-1; i++) {
line(BLOCK_SIZE*path[i][0]+BLOCK_SIZE/2, BLOCK_SIZE*path[i][1]+BLOCK_SIZE/2, BLOCK_SIZE*path[i+1][0]+BLOCK_SIZE/2, BLOCK_SIZE*path[i+1][1]+BLOCK_SIZE/2);
}
// Draw the new path
stroke(SMOOTH_PATH_COLOR);
strokeWeight(5);
for (var i = 0; i < smoothPath.length; i++) {
point(BLOCK_SIZE*smoothPath[i][0]+BLOCK_SIZE/2, BLOCK_SIZE*smoothPath[i][1]+BLOCK_SIZE/2);
}
strokeWeight(1);
for (var i = 0; i < smoothPath.length-1; i++) {
line(BLOCK_SIZE*smoothPath[i][0]+BLOCK_SIZE/2, BLOCK_SIZE*smoothPath[i][1]+BLOCK_SIZE/2, BLOCK_SIZE*smoothPath[i+1][0]+BLOCK_SIZE/2,
BLOCK_SIZE*smoothPath[i+1][1]+BLOCK_SIZE/2);
}
// Perform gradient descent
var update;
var diff = 0;
for (var i = 1; i < smoothPath.length-1; i++) {
update = [0, 0];
for (var j = 0; j < smoothPath[0].length; j++) {
update[j] += weightData * (path[i][j] - smoothPath[i][j]);
update[j] += weightSmooth * (smoothPath[(i+1)%smoothPath.length][j] + smoothPath[(i-1+smoothPath.length)%smoothPath.length][j] - 2*smoothPath[i][j]);
}
// Simulataneous update
for (var j = 0; j < smoothPath[0].length; j++) {
smoothPath[i][j] += update[j];
diff += abs(update[j]);
}
}
if (diff < 0.000001) {
robot = new Robot(BLOCK_SIZE/2, BLOCK_SIZE/2, BLOCK_SIZE/2);
mode = NAVIGATE;
}
}
else if (mode === CALIBRATE) {
var steps = 100;
var error = simulate(steps);
var dp = [1.0, 1.0, 1.0];
var params = [0, 0, 0];
if (error/steps > 0.04) {
for (var i = 0; i < dp.length; i++) {
params[i] += dp[i];
var newError = simulate(steps);
if (newError < error) {
error = newError;
dp[i] *= 1.1;
}
else {
params[i] -= 2*dp[i];
newError = simulate(steps);
if (newError < error) {
error = newError;
dp[i] *= -1.1;
}
else {
params[i] += dp[i];
dp[i] *= 0.9;
}
}
}
tP = params[0];
tD = params[1];
tI = params[2];
println(error/steps);
}
else {
println(params);
tP = params[0];
tD = params[1];
tI = params[2];
mode = NAVIGATE;
}
}
// Use PID control to follow the path
else if (mode === NAVIGATE) {
// Draw path
stroke(SMOOTH_PATH_COLOR);
strokeWeight(5);
for (var i = 0; i < smoothPath.length; i++) {
point(BLOCK_SIZE*smoothPath[i][0]+BLOCK_SIZE/2, BLOCK_SIZE*smoothPath[i][1]+BLOCK_SIZE/2);
}
strokeWeight(1);
for (var i = 0; i < smoothPath.length-1; i++) {
line(BLOCK_SIZE*smoothPath[i][0]+BLOCK_SIZE/2, BLOCK_SIZE*smoothPath[i][1]+BLOCK_SIZE/2, BLOCK_SIZE*smoothPath[i+1][0]+BLOCK_SIZE/2,
BLOCK_SIZE*smoothPath[i+1][1]+BLOCK_SIZE/2);
}
// Draw robot
robot.draw();
// Compute cte
var diff = -cte;
var x1 = smoothPath[index][0]*BLOCK_SIZE+BLOCK_SIZE/2;
var y1 = smoothPath[index][1]*BLOCK_SIZE+BLOCK_SIZE/2;
var x2 = smoothPath[index+1][0]*BLOCK_SIZE+BLOCK_SIZE/2;
var y2 = smoothPath[index+1][1]*BLOCK_SIZE+BLOCK_SIZE/2;
var dx = x2-x1;
var dy = y2-y1;
var d = sqrt(pow(dx, 2) + pow(dy, 2));
var u = ((robot.x-x1)*dx + (robot.y-y1)*dy)/(pow(dx, 2) + pow(dy, 2));
var Px = x1 + d*u*cos(atan2(dy, dx));
var Py = y1 + d*u*sin(atan2(dy, dx));
cte = ((robot.y-y1)*dx-(robot.x-x1)*dy)/(pow(dx, 2) + pow(dy, 2));
sigma += cte;
diff += cte;
if (u > 1) {
index++;
index = min(index, smoothPath.length-2);
}
// Update robot
robot.move(ROBOT_SPEED, -tP*cte - tD*diff - tI*sigma);
//println(index);
}
};
but all that happens is that a grey screen shows up
I'm working on getting all the components for my final together using the P5.play engine and while I have made some progress with setting up aspects of my mini game I'm having a hard time with the collision. It should be easy but for whatever reason when I set up my two objects (the fish and the garbage) they do not collide. I am trying to set it up so that when the garbage collides with the fish the fish either get removed or are reset to a place where they can continue to move on the screen while tallying the score. I managed to get the player sprite to collect the garbage and add to the score using overlapPoint and placing the condition in the update for the garbage object. But when I attempt the same technique for the fish on the garbage object an error occurs and everything disappears on the screen. I commented out the portion that is I have tried multiple ways including the collide() function on the objects with the proper conditionals but nothing seems to work. A bit frustrating. I have tried various other ways. So I'm asking for expert advice. Any assistance is appreciated. this is the code that i have thus far:
var bg;
var player;
var player_stand_sprites;
var player_stand;
var fish_swim_sprites;
var fish_swim;
var fish = [];
var garbage_drop_sprites;
var garbage_drop;
var garbage = [];
var score = 0;
function preload() {
bg = loadImage("final-bg.png");
player_stand_sprites = loadSpriteSheet("player2.png", 100, 100, 1);
player_stand = loadAnimation(player_stand_sprites);
fish_swim_sprites = loadSpriteSheet("fish.png", 75, 75, 1);
fish_swim = loadAnimation(fish_swim_sprites);
garbage_drop_sprites = loadSpriteSheet("metal.png", 41, 75, 1);
garbage_drop = loadAnimation(garbage_drop_sprites);
}
function setup() {
createCanvas(720, 360);
player = createSprite(100, 0);
player.addAnimation("stand", player_stand);
player.setCollider("circle", 0, 0, 32, 32);
player.depth = 10;
//player.debug = true;
//to make fish
for (var i = 0; i < 10; i++){
fish.push( new Fish(random(0,width), random(height/2, height)) );
for (var i = 0; i < fish.length; i++) {
fish[i].init();
}
}
//to make garbage
for (var a = 0; a < 5; a++){
garbage.push( new Garbage(random(0,width), random(width/2, width)));
}
}
function draw() {
background(bg);
player.position.x = mouseX;
player.position.y = mouseY;
for (var i = 0; i < fish.length; i++) {
fish[i].update();
}
for (var a = 0; a < garbage.length; a++) {
garbage[a].update();
}
/**for (var b = 0; b < fish.length; b++) {
if(fish[b].collide(garbage[b])){
fish[b].remove;
}
}**/
text(score,100,100);
drawSprites();
}
function Garbage(x,y){
this.sprite = createSprite(x, y);
this.sprite.addAnimation("drop", garbage_drop);
this.sprite.setCollider("circle",0,0,32,32);
this.speed = random(1,2);
this.sprite.debug = true;
this.update = function() {
this.sprite.position.y += this.speed;
if(this.sprite.position.y > height){
this.sprite.position.y = 0;
}
if(this.sprite.overlapPoint(player.position.x, player.position.y)){
this.sprite.position.x = random(0,width);
this.sprite.position.y = -75;
score++;
}
}
}
function Fish(x,y) {
this.sprite = createSprite(x, y);
this.sprite.addAnimation("swim", fish_swim);
this.sprite.setCollider("rectangle",0,0,75,32);
this.speed = 0;
this.sprite.debug = true;
this.init = function() {
if (this.sprite.position.x < width/2) {
this.sprite.mirrorX(-1);
this.speed = random(1, 2);
} else {
this.speed = -random(1,2);
}
}
this.update = function() {
this.sprite.position.x += this.speed;
if(this.sprite.position.x > width){
this.sprite.position.x = 0;
}else if(this.sprite.position.x < -width){
this.sprite.position.x = width;
}
}
}
I actually found the answer to my question a while back but am going to post it here.
The initial problem was that the sprites would not collide but with a simple for loop nested within another for loop and adding a .sprite to each of the objects being checked, I was able to get all the elements to collide properly.
Here is the code I revised to make it work seamlessly using the P5.play.js library:
var bg;
var img;
var dead = false;
var deadFish = 0;
var player;
var player_stand_sprites;
var player_stand;
var fish_swim_sprites;
var fish_swim;
var fish = [];
var garbage_drop_sprites;
var garbage_drop;
var garbage = [];
var score = 0;
//////////////////////////////////////////
function preload() {
bg = loadImage("final-bg.png");
img = loadImage("fish.png");
player_stand_sprites = loadSpriteSheet("player2.png", 100, 100, 1);
player_stand = loadAnimation(player_stand_sprites);
fish_swim_sprites = loadSpriteSheet("fish.png", 75, 75, 1);
fish_swim = loadAnimation(fish_swim_sprites);
garbage_drop_sprites = loadSpriteSheet("metal.png", 41, 75, 1);
garbage_drop = loadAnimation(garbage_drop_sprites);
}
//////////////////////////////////////////
function setup() {
createCanvas(720, 360);
player = createSprite(100, 0);
player.addAnimation("stand", player_stand);
player.setCollider("circle", 0, 0, 32, 32);
player.depth = 10;
//player.debug = true;
//to make fish
for (var i = 0; i < 10; i++){
fish.push( new Fish(random(0,width), random(height/2, height)) );
for (var i = 0; i < fish.length; i++) {
fish[i].init();
}
}
//to make garbage
for (var a = 0; a < 5; a++){
garbage.push( new Garbage(random(0,width), random(width/2, width)));
}
}
function draw() {
scene_start();
}
//////////////////////////////////////////
function scene_start(){
push();
background("green");
fill("white");
textAlign(CENTER);
textSize(50);
textStyle(BOLD);
text("SPOT A FISH", width/2,height/3.5);
image(img, width/2.3, height/3);
textSize(15);
text("Rules: dont let the cans touch the fish. 5 fish die and you must start over", width/2, height/1.5);
textSize(30);
text("press up arrow key to start", width/2, height/1.2);
pop();
if (keyCode == UP_ARROW) {
scene_1();
}
}
function scene_1(){
background(bg);
score_card();
if(!dead){
player.position.x = mouseX;
player.position.y = mouseY;
for (var i = 0; i < fish.length; i++) {
fish[i].update();
}
for (var i = 0; i < garbage.length; i++) {
garbage[i].update();
}
for (var a = 0; a < garbage.length; a++) {
for (var b = 0; b < fish.length; b++) {
if(fish[b].sprite.collide(garbage[a].sprite)){
fish[b].sprite.position.x = random(-500, -100);
deadFish ++;
if(deadFish === 5){
dead = true;
}
}
}
}
drawSprites();
}else{
score_card();
textSize(30);
textStyle(BOLD);
textAlign(CENTER);
text("YOU DIED PLEASE TRY AGAIN",width/2,height/2);
text("press any button to start again",width/2,height/1.5);
if(keyIsPressed === true){
deadFish = 0;
dead = false;
}
}
}
function Garbage(x,y){
this.sprite = createSprite(x, y);
this.sprite.addAnimation("drop", garbage_drop);
this.sprite.setCollider("circle",0,0,32,32);
this.speed = random(1,2);
//this.sprite.debug = true;
this.update = function() {
this.sprite.position.y += this.speed;
if(this.sprite.position.y > height){
this.sprite.position.y = 0;
}
if(this.sprite.overlapPoint(player.position.x, player.position.y)){
this.sprite.position.x = random(0,width);
this.sprite.position.y = random(-200,-75);
score++;
}
if(score === 100){
this.speed = random(2,3);
score += 1000;
}else if(score === 1200){
this.speed = random(3,3.5);
score += 1000;
}
}
}
var score_card = function(){
fill("black");
rect(0,0,width,30);
textStyle(BOLD);
fill("white");
text("SCORE: "+score,10,20);
text("DEAD FISH: "+deadFish,width/1.2,20)
}
function Fish(x,y) {
this.sprite = createSprite(x, y);
this.sprite.addAnimation("swim", fish_swim);
this.sprite.setCollider("rectangle",0,0,75,32);
this.speed = 0;
this.sprite.debug = true;
this.init = function() {
if (this.sprite.position.x < width/2) {
this.sprite.mirrorX(-1);
this.speed = random(1, 2);
} else {
this.speed = -random(1,2);
}
}
this.update = function() {
this.sprite.position.x += this.speed;
if(this.sprite.position.x > width){
this.sprite.position.x = 0;
}else if(this.sprite.position.x < -width){
this.sprite.position.x = width;
}
}
}