I am trying to take Dan Shiffman's prime spiral program and make it object oriented.
I am putting all variables into the constructor and making the functions into methods to encapsulate them.
But the OOP version of the code does not draw more than 1 triangle to the screen. I can't see why there should be a problem, as I have all the variables and functions included in the scope of the primeSpiral class.
Working code without classes
let x, y;
let step = 1;
let stepSize = 20;
let numSteps = 1;
let state = 0;
let turnCounter = 1;
let offset = 0;
function setup() {
createCanvas(900, 900);
background(0);
const cols = width / stepSize;
const rows = height / stepSize;
x = width / 2;
y = height / 2;
for (let i = 0; i < 500; i++) {
noStroke();
primeSpiral(20, 1)
primeSpiral(30, 200)
incrementStep();
}
}
function incrementStep() {
switch (state) {
case 0:
x += stepSize;
break;
case 1:
y -= stepSize;
break;
case 2:
x -= stepSize;
break;
case 3:
y += stepSize;
break;
}
if (step % numSteps == 0) {
state = (state + 1) % 4;
turnCounter++;
if (turnCounter % 2 == 0) {
numSteps++;
}
}
step++;
}
function primeSpiral(offset, color) {
if (!isPrime(step + offset)) {
//might put something here
} else {
let r = stepSize * 0.5;
fill(color, 99, 164);
push();
translate(x, y);
rotate(-PI / 4);
triangle(-r, +r, 0, -r, +r, +r);
pop();
}
}
function isPrime(value) {
if (value == 1) return false;
for (let i = 2; i <= sqrt(value); i++) {
if (value % i == 0) {
return false;
}
}
return true;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>
With classes:
let stepSize = 20;
function setup() {
createCanvas(900, 900);
background(0);
const cols = width / stepSize;
const rows = height / stepSize;
x = width / 2;
y = height / 2;
background(0);
prime = new PrimeSpiral(0, 0, 2, stepSize, 1, 0) //: x,y,offset,color
}
function draw() {
prime.walk();
}
class PrimeSpiral {
constructor(x, y, number) {
this.x = x;
this.y = y;
this.number = number;
this.stepSize = 20;
this.numSteps = 1;
this.state = 0;
this.turnCounter = 1;
}
walk() {
if (!this.isPrime(this.number)) {
console.log(this.succes);
} else {
let r = stepSize * 0.5;
fill(color, 99, 164);
push();
translate(x, y);
rotate(-PI / 4);
triangle(-r, +r, 0, -r, +r, +r);
pop();
this.incrementStep()
}
}
isPrime(value) {
if (value == 1) return false;
for (let i = 2; i <= Math.sqrt(value); i++) {
if (value % i == 0) {
return false;
}
}
return true;
}
incrementStep() {
switch (this.state) {
case 0:
this.x += this.stepSize;
break;
case 1:
this.y -= this.stepSize;
break;
case 2:
this.x -= this.stepSize;
break;
case 3:
this.y += this.stepSize;
break;
}
if (this.step % this.numSteps == 0) {
this.state = (this.state + 1) % 4;
this.turnCounter++;
if (this.turnCounter % 2 == 0) {
this.numSteps++;
}
}
this.step++;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>
Sometimes you need to slow down to go faster :)
My hunch is you might attempted to write the class all in one go, without slowing down and testing one function at a time to ensure no errors snuck through.
Here are some of the main errors:
you forgot to add the step property to the class (this.step = 1;) in the constructor. In incrementStep() this.step which is undefined gets incremented which results in NaN throwing the rest off (e.g. state, etc.)
you left stepSize mostly as global variable, but are also using this.stepSize: try to avoid ambiguity and use one or the other (I recommend using this.stepSize since it will allow multiple instances to have independent step sizes)
In walk you're still using translate(x, y); when you probably meant translate(this.x, this.y);
Here's a version with the above notes applied (and optionally no longer incrementing or drawing triangles if x,y go outside the screen bounds):
let spiral1;
let spiral2;
function setup() {
createCanvas(900, 900);
background(0);
noStroke();
// instantiate spirals here so width, height set
spiral1 = new PrimeSpiral(20, 1);
spiral2 = new PrimeSpiral(30, 200);
}
function draw(){
spiral1.walk();
spiral2.walk();
}
class PrimeSpiral{
constructor(offset, color){
this.x = width / 2;
this.y = height / 2;
this.step = 1;
this.stepSize = 20;
this.numSteps = 1;
this.state = 0;
this.turnCounter = 1;
this.offset = offset;
this.color = color;
}
incrementStep() {
switch (this.state) {
case 0:
this.x += this.stepSize;
break;
case 1:
this.y -= this.stepSize;
break;
case 2:
this.x -= this.stepSize;
break;
case 3:
this.y += this.stepSize;
break;
}
if (this.step % this.numSteps == 0) {
this.state = (this.state + 1) % 4;
this.turnCounter++;
if (this.turnCounter % 2 == 0) {
this.numSteps++;
}
}
this.step++;
}
walk(){
// optional, early exit function if we're offscreen
if(this.x < 0 || this.x > width) return;
if(this.y < 0 || this.y > height) return;
if (!isPrime(this.step + this.offset)) {
//console.log('not prime:', step + offset);
} else {
let r = this.stepSize * 0.5;
fill(this.color, 99, 164);
push();
translate(this.x, this.y);
rotate(-PI / 4);
triangle(-r, +r, 0, -r, +r, +r);
pop();
}
this.incrementStep();
}
}
function isPrime(value) {
if (value == 1) return false;
for (let i = 2; i <= sqrt(value); i++) {
if (value % i == 0) {
return false;
}
}
return true;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.min.js"></script>
Related
I am trying the simple circle filling and it works. But when I try to fill the first circle with more circles, it hangs the program immediately. Here's my draw and and class code which fills the circles inside circles:
let krr=[];
function draw() {
background(0);
print(cir.length,krr.length)
if (cir.length<=100){
let temp=new c();
cir.push(temp);
}
else if (krr.length<100){
print(1)
fr=cir[0]
if (fr.r>50){
let re=new cirgain(fr.x,fr.y,(fr.r)/2)
krr.push(re);
}
}
for (let h of krr){
h.show();
}
for (let g of cir){
g.show();
g.grow();
}
}
class cirgain{
constructor(x,y,r){
this.smr=floor(r/3);
if (krr.length==0){
while (true){
this.x=random(x-r+1,x+r-1);
this.y=random(y-r+1,y+r-1)
if (this.x*this.x+this.y*this.y<r*r-2){
break
}
}
}
else{
let flag1=1
let count1=0
while (flag){
if (count1>=500){
count1=0;
this.smr--;
}
while (true){
this.x=random(x-r+1,x+r-1);
this.y=random(y-r+1,y+r-1);
if (this.x*this.x+this.y*this.y<r*r-2)
break
}
for (let i=0;i<krr.length;i++){
if (dist(krr[i].x,krr[i].y,this.x,this.y)<r+this.smr){
flag1=1
count1++;
break;
}
flag1=0;
}
}
}
this.ccc=createVector(random(255),random(100,255),random(100,255))
}
show(){
stroke(0);
noFill();
strokeWeight(3)
stroke(this.ccc.x,this.ccc.y,this.ccc.z);
circle(this.x,this.y,this.smr)
}
}
If the whole code (including setup and class c (which at first fills the space with circles)) is needed, let me know, I will edit to include it.
Edit: Okay, here is the whole code:
let cir = [];
let maxR;
let krr = [];
function setup() {
createCanvas(windowWidth, windowHeight);
maxR = width / 4;
if (height > width)
maxR = height / 4
colorMode(HSB);
angleMode(DEGREES);
}
function draw() {
background(0);
print(cir.length, krr.length)
if (cir.length <= 100) {
let temp = new c();
cir.push(temp);
} else if (krr.length < 100) {
print(1)
fr = cir[0]
if (fr.r > 50) {
let re = new cirgain(fr.x, fr.y, (fr.r) / 2)
krr.push(re);
}
}
for (let h of krr) {
h.show();
}
for (let g of cir) {
g.show();
g.grow();
}
}
class c {
constructor() {
this.tempr = 1
if (cir.length == 0) {
this.x = random(maxR + 1, width - maxR - 1);
this.y = random(maxR + 1, height - maxR - 1)
this.r = maxR;
} else {
let flag = 1
let count = 0
while (flag) {
if (count >= 500) {
count = 0;
maxR--;
}
this.x = random(maxR / 2 + 1, width - maxR / 2 - 1);
this.y = random(maxR / 2 + 1, height - maxR / 2 - 1);
this.r = maxR;
for (let i = 0; i < cir.length; i++) {
if (dist(cir[i].x, cir[i].y, this.x, this.y) < cir[i].r / 2 + this.r / 2 + 3) {
flag = 1
count++;
break;
}
flag = 0;
}
}
}
this.cc = createVector(random(255), random(100, 255), random(100, 255))
}
show() {
stroke(0);
noFill();
strokeWeight(3)
stroke(this.cc.x, this.cc.y, this.cc.z);
circle(this.x, this.y, this.tempr)
rectMode(CENTER);
}
grow() {
if (this.tempr <= this.r)
// this.tempr+=.5
this.tempr += this.r / 100
}
}
class cirgain {
constructor(x, y, r) {
this.smr = floor(r / 3);
if (krr.length == 0) {
while (true) {
this.x = random(x - r + 1, x + r - 1);
this.y = random(y - r + 1, y + r - 1)
if (this.x * this.x + this.y * this.y < r * r - 2) {
break
}
}
} else {
let flag1 = 1
let count1 = 0
while (flag) {
if (count1 >= 500) {
count1 = 0;
this.smr--;
}
while (true) {
this.x = random(x - r + 1, x + r - 1);
this.y = random(y - r + 1, y + r - 1);
if (this.x * this.x + this.y * this.y < r * r - 2)
break
}
for (let i = 0; i < krr.length; i++) {
if (dist(krr[i].x, krr[i].y, this.x, this.y) < r + this.smr) {
flag1 = 1
count1++;
break;
}
flag1 = 0;
}
}
}
this.ccc = createVector(random(255), random(100, 255), random(100, 255))
}
show() {
stroke(0);
noFill();
strokeWeight(3)
stroke(this.ccc.x, this.ccc.y, this.ccc.z);
circle(this.x, this.y, this.smr)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
Your logic for finding viable x/y values randomly in the cirgain constructor seems broken. Several of your while loops seem like they never exit.
let cir = [];
let krr = [];
let maxR;
function setup() {
createCanvas(windowWidth, windowHeight);
maxR = width / 4;
if (height > width) {
maxR = height / 4;
}
colorMode(HSB);
angleMode(DEGREES);
}
function draw() {
background(0);
print(cir.length, krr.length);
if (cir.length <= 100) {
let temp = new c();
cir.push(temp);
} else if (krr.length < 100) {
print(1)
fr = cir[0]
if (fr.r > 50) {
let re = new cirgain(fr.x, fr.y, (fr.r) / 2)
krr.push(re);
}
}
for (let h of krr) {
h.show();
}
for (let g of cir) {
g.show();
g.grow();
}
}
class c {
constructor() {
this.tempr = 1
if (cir.length == 0) {
this.x = random(maxR + 1, width - maxR - 1);
this.y = random(maxR + 1, height - maxR - 1)
this.r = maxR;
} else {
let flag = 1
let count = 0
while (flag) {
if (count >= 500) {
count = 0;
maxR--;
}
this.x = random(maxR / 2 + 1, width - maxR / 2 - 1);
this.y = random(maxR / 2 + 1, height - maxR / 2 - 1);
this.r = maxR;
for (let i = 0; i < cir.length; i++) {
if (dist(cir[i].x, cir[i].y, this.x, this.y) < cir[i].r / 2 + this.r / 2 + 3) {
flag = 1
count++;
break;
}
flag = 0;
}
}
}
this.cc = createVector(random(255), random(100, 255), random(100, 255))
}
show() {
stroke(0);
noFill();
strokeWeight(3)
stroke(this.cc.x, this.cc.y, this.cc.z);
circle(this.x, this.y, this.tempr)
rectMode(CENTER);
}
grow() {
if (this.tempr <= this.r)
// this.tempr+=.5
this.tempr += this.r / 100
}
}
class cirgain {
constructor(x, y, r) {
this.smr = floor(r / 3);
if (krr.length == 0) {
let n = 0;
while (++n < 1000) {
this.x = random(x - r + 1, x + r - 1);
this.y = random(y - r + 1, y + r - 1);
if (this.x * this.x + this.y * this.y < r * r - 2) {
break;
}
}
if (n >= 1000) {
console.warn('1. Unable to find an x/y value that satisfied the constraint');
debugger;
}
} else {
let flag1 = 1;
let count1 = 0;
let n = 0;
while (flag1 && ++n < 10000) {
if (count1 >= 500) {
count1 = 0;
this.smr--;
}
let n2 = 0;
while (++n2 < 1000) {
this.x = random(x - r + 1, x + r - 1);
this.y = random(y - r + 1, y + r - 1);
if (this.x * this.x + this.y * this.y < r * r - 2) {
break;
}
}
if (n2 >= 1000) {
console.warn('2. Unable to find an x/y value that satisfied the constraint');
debugger;
}
for (let i = 0; i < krr.length; i++) {
if (dist(krr[i].x, krr[i].y, this.x, this.y) < r + this.smr) {
flag1 = 1
count1++;
break;
}
flag1 = 0;
}
}
if (n >= 10000) {
console.warn('3. Flag never set to false');
debugger;
}
}
this.ccc = createVector(random(255), random(100, 255), random(100, 255))
}
show() {
stroke(0);
noFill();
strokeWeight(3)
stroke(this.ccc.x, this.ccc.y, this.ccc.z);
circle(this.x, this.y, this.smr);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
Update
You seem to have an error in your logic checking if the randomly generated x/y values are acceptable:
// Instead of this, which checks if the square of the distance from the top left (0, 0) to the randomly generated center is less than the radius squared
if (this.x * this.x + this.y * this.y < r * r - 2) {
break;
}
// You probably meant to check if the distance from the center of the circle to the randomly generated position is less than the radius:
if (dist(x, y, this.x, this.y) < r - 2) {
break;
}
Additionally I cannot make heads or tails of this logic:
for (let i = 0; i < krr.length; i++) {
if (dist(krr[i].x, krr[i].y, this.x, this.y) < r + this.smr) {
flag1 = 1
count1++;
break;
}
flag1 = 0;
}
For each cirgain that you create you are check that for every existing cirgain the new one has a distance from the existing that is less than the containing circle's radius plus the current circle's diameter*, and if that is the case (which it almost always is!) you try to find a different random position, and only after 500 attempts you decrease the current circle's radius. I can hardly tell what your objective is, but there has to be a better way.
* You're using the default ellipse mode, which means the third argument to circle() is the diameter, which makes this code especially confusing
If all my NPCs are stationary, the collision detection works as expected. However, when they are moving, the objects move through each other. I don't understand the difference between the moving object and the stationary object. I am tracking the hitboxes of all the NPCs and I'm pretty sure they're accurate. Maybe someone has some insight. Thanks in advance.
function Hero(map, x, y, facing, image, chars, type) {
this.map = map;
this.x = x;
this.y = y;
this.width = map.tsize;
this.height = map.tsize;
this.chars = chars
// facing = R=0 U=1 L=2 D=3
this.facing = facing
this.image = Loader.getImage(image)
this.type = type
}
Hero.prototype.hitBox = function(type){
if (this.type === "hero"){
return {
left: this.x - this.width/2,
right: this.x + this.width/2 -1,
top: this.y + this.height/2,
bottom: this.y + this.height -1
}
} else if (this.type === "npc") {
return {
left: this.x - this.width/2,
right: this.x + this.width/2 -1,
top: this.y - this.height/2,
bottom: this.y + this.height -1
}
}
};
Hero.SPEED = 256; // pixels per second
Hero.prototype.move = function (delta, dirx, diry) {
// move hero
this.x += dirx * Hero.SPEED * delta;
this.y += diry * Hero.SPEED * delta;
// clamp values
var maxX = (this.map.cols-2) * this.map.tsize;
var maxY = (this.map.rows-2) * this.map.tsize;
this.x = Math.max(0, Math.min(this.x, maxX));
this.y = Math.max(0, Math.min(this.y, maxY));
// check if we walked into a non-walkable tile
this._collide(delta, dirx, diry);
};
Hero.prototype.objCollision = function (obj){
let objHitBox = obj.hitBox()
let heroHitBox = this.hitBox()
if (objHitBox.left < heroHitBox.right&&
objHitBox.right > heroHitBox.left &&
objHitBox.top < heroHitBox.bottom &&
objHitBox.bottom > heroHitBox.top) {
return true
} else {
return false
}
}
Hero.prototype._collide = function (delta, dirx, diry) {
let row, col;
// check for collisions on sprite sides
let collision =
this.map.isSolidTileAtXY(this.hitBox()["left"], this.hitBox()["top"]) ||
this.map.isSolidTileAtXY(this.hitBox()["right"], this.hitBox()["top"]) ||
this.map.isSolidTileAtXY(this.hitBox()["right"], this.hitBox()["bottom"]) ||
this.map.isSolidTileAtXY(this.hitBox()["left"], this.hitBox()["bottom"])
//loop through all hexes with NPCs
let objCollision = this.chars.all.reduce(function (res, obj) {
let tmp;
if (obj !== this) {
tmp = this.objCollision(obj)
}
else {
return false
}
return res || tmp;
}.bind(this), false)
if (!collision && !objCollision) { return; }
if (diry > 0) {
row = this.map.getRow(this.hitBox()["bottom"]);
this.y = -Hero.SPEED*delta + this.y
}
else if (diry < 0) {
row = this.map.getRow(this.hitBox()["top"]);
this.y = Hero.SPEED*delta + this.y
}
else if (dirx > 0) {
col = this.map.getCol(this.hitBox()["right"]);
this.x = -Hero.SPEED*delta+ this.x;
}
else if (dirx < 0) {
col = this.map.getCol(this.hitBox()["left"]);
this.x = Hero.SPEED*delta + this.x;
}
};
aiMove(delta) {
switch(this.facing){
case 0:
this.dirx= 1
this.diry= 0
break;
case 1:
this.dirx= 0
this.diry= -1
break;
case 2:
this.dirx= -1
this.diry= 0
break;
case 3:
this.dirx= 0
this.diry= 1
break;
}
if (!this.waiting && this.moveTimer < 1) {
//Random direction
this.facing = Math.floor(Math.random() * Math.floor(4))
//random time
this.restStepTimer();
} else if (!this.waiting && this.moveTimer > 0) {
//Move to preset direction
this.move(delta, this.dirx, this.diry)
//subtract timer
this.moveTimer--;
//if timer is empty, switch to waiting and reset timer.
if (this.moveTimer <= 0) {
this.waiting = true;
this.restStepTimer();
}
} else if (this.waiting && this.moveTimer > 0) {
//while waiting, we lower the timer.
this.moveTimer--;
//when the timer runs out, we flip the flag back to not waiting and start again.
if (this.moveTimer <= 0) {
this.waiting = false;
}
}
};
restStepTimer() {
this.moveTimer = Math.floor(Math.random() * 50 + 20);
};
};
I'm working on a project where I simulate physics with balls.
Here is the link to the p5 editor of the project.
My problem is the following, when I add a lot of ball (like 200), balls are stacking but some of them will eventually collapse and I don't know why.
Can somebody explain why it does this and how to solve the problem ?
Thanks.
Here is the code of the sketch.
document.oncontextmenu = function () {
return false;
}
let isFlushing = false;
let isBallDiameterRandom = false;
let displayInfos = true;
let displayWeight = false;
let clickOnce = false;
let FRAME_RATE = 60;
let SPEED_FLUSH = 3;
let Y_GROUND;
let lastFR;
let balls = [];
function setup() {
frameRate(FRAME_RATE);
createCanvas(window.innerWidth, window.innerHeight);
Y_GROUND = height / 20 * 19;
lastFR = FRAME_RATE;
}
function draw() {
background(255);
if (isFlushing) {
for (let i = 0; i < SPEED_FLUSH; i++) {
balls.pop();
}
if (balls.length === 0) {
isFlushing = false;
}
}
balls.forEach(ball => {
ball.collide();
ball.move();
ball.display(displayWeight);
ball.checkCollisions();
});
if (mouseIsPressed) {
let ballDiameter;
if (isBallDiameterRandom) {
ballDiameter = random(15, 101);
} else {
ballDiameter = 25;
}
if (canAddBall(mouseX, mouseY, ballDiameter)) {
isFlushing = false;
let newBall = new Ball(mouseX, mouseY, ballDiameter, balls);
if (mouseButton === LEFT && !clickOnce) {
balls.push(newBall);
clickOnce = true;
}
if (mouseButton === RIGHT) {
balls.push(newBall);
}
}
}
drawGround();
if (displayInfos) {
displayShortcuts();
displayFrameRate();
displayBallCount();
}
}
function mouseReleased() {
if (mouseButton === LEFT) {
clickOnce = false;
}
}
function keyPressed() {
if (keyCode === 32) {//SPACE
displayInfos = !displayInfos;
}
if (keyCode === 70) {//F
isFlushing = true;
}
if (keyCode === 71) {//G
isBallDiameterRandom = !isBallDiameterRandom;
}
if (keyCode === 72) {//H
displayWeight = !displayWeight;
}
}
function canAddBall(x, y, d) {
let isInScreen =
y + d / 2 < Y_GROUND &&
y - d / 2 > 0 &&
x + d / 2 < width &&
x - d / 2 > 0;
let isInAnotherBall = false;
for (let i = 0; i < balls.length; i++) {
let d = dist(x, y, balls[i].position.x, balls[i].position.y);
if (d < balls[i].w) {
isInAnotherBall = true;
break;
}
}
return isInScreen && !isInAnotherBall;
}
function drawGround() {
strokeWeight(0);
fill('rgba(200,200,200, 0.25)');
rect(0, height / 10 * 9, width, height / 10);
}
function displayFrameRate() {
if (frameCount % 30 === 0) {
lastFR = round(frameRate());
}
textSize(50);
fill(255, 0, 0);
let lastFRWidth = textWidth(lastFR);
text(lastFR, width - lastFRWidth - 25, 50);
textSize(10);
text('fps', width - 20, 50);
}
function displayBallCount() {
textSize(50);
fill(255, 0, 0);
text(balls.length, 10, 50);
let twBalls = textWidth(balls.length);
textSize(10);
text('balls', 15 + twBalls, 50);
}
function displayShortcuts() {
let hStart = 30;
let steps = 15;
let maxTW = 0;
let controlTexts = [
'LEFT CLICK : add 1 ball',
'RIGHT CLICK : add 1 ball continuously',
'SPACE : display infos',
'F : flush balls',
'G : set random ball diameter (' + isBallDiameterRandom + ')',
'H : display weight of balls (' + displayWeight + ')'
];
textSize(11);
fill(0);
for (let i = 0; i < controlTexts.length; i++) {
let currentTW = textWidth(controlTexts[i]);
if (currentTW > maxTW) {
maxTW = currentTW;
}
}
for (let i = 0; i < controlTexts.length; i++) {
text(controlTexts[i], width / 2 - maxTW / 2 + 5, hStart);
hStart += steps;
}
fill(200, 200, 200, 100);
rect(width / 2 - maxTW / 2,
hStart - (controlTexts.length + 1) * steps,
maxTW + steps,
(controlTexts.length + 1) * steps - steps / 2
);
}
Here is the code of the Ball class.
class Ball {
constructor(x, y, w, e) {
this.id = e.length;
this.w = w;
this.e = e;
this.progressiveWidth = 0;
this.rgb = [
floor(random(0, 256)),
floor(random(0, 256)),
floor(random(0, 256))
];
this.mass = w;
this.position = createVector(x + random(-1, 1), y);
this.velocity = createVector(0, 0);
this.acceleration = createVector(0, 0);
this.gravity = 0.2;
this.friction = 0.5;
}
collide() {
for (let i = this.id + 1; i < this.e.length; i++) {
let dx = this.e[i].position.x - this.position.x;
let dy = this.e[i].position.y - this.position.y;
let distance = sqrt(dx * dx + dy * dy);
let minDist = this.e[i].w / 2 + this.w / 2;
if (distance < minDist) {
let angle = atan2(dy, dx);
let targetX = this.position.x + cos(angle) * minDist;
let targetY = this.position.y + sin(angle) * minDist;
this.acceleration.set(
targetX - this.e[i].position.x,
targetY - this.e[i].position.y
);
this.velocity.sub(this.acceleration);
this.e[i].velocity.add(this.acceleration);
//TODO : Effets bizarre quand on empile les boules (chevauchement)
this.velocity.mult(this.friction);
}
}
}
move() {
this.velocity.add(createVector(0, this.gravity));
this.position.add(this.velocity);
}
display(displayMass) {
if (this.progressiveWidth < this.w) {
this.progressiveWidth += this.w / 10;
}
stroke(0);
strokeWeight(2);
fill(this.rgb[0], this.rgb[1], this.rgb[2], 100);
ellipse(this.position.x, this.position.y, this.progressiveWidth);
if (displayMass) {
strokeWeight(1);
textSize(10);
let tempTW = textWidth(int(this.w));
text(int(this.w), this.position.x - tempTW / 2, this.position.y + 4);
}
}
checkCollisions() {
if (this.position.x > width - this.w / 2) {
this.velocity.x *= -this.friction;
this.position.x = width - this.w / 2;
} else if (this.position.x < this.w / 2) {
this.velocity.x *= -this.friction;
this.position.x = this.w / 2;
}
if (this.position.y > Y_GROUND - this.w / 2) {
this.velocity.x -= this.velocity.x / 100;
this.velocity.y *= -this.friction;
this.position.y = Y_GROUND - this.w / 2;
} else if (this.position.y < this.w / 2) {
this.velocity.y *= -this.friction;
this.position.y = this.w / 2;
}
}
}
I see this overlapping happen when the sum of ball masses gets bigger than the elasticity of the balls. At least it seems so. I made a copy with a smaller pool so it doesn't take so much time to reproduce the problem.
In the following example, with 6 balls (a mass of 150 units) pressing on the base row, we see that the 13 balls in the base row overlap. The base row has a width of ca. 300 pixels, which is only enough space for 12 balls of diameter 25. I think this is showing the limitation of the model: the balls are displayed circular but indeed have an amount of elasticity that they should display deformed instead. It's hard to say how this can be fixed without implementing drawing complicated shapes. Maybe less friction?
BTW: great physics engine you built there :-)
Meanwhile I was able to make another screenshot with even fewer balls. The weight of three of them (eq. 75 units) is sufficient to create overlapping in the base row.
I doubled the size of the balls and changed the pool dimensions as to detedt that there is a more serious error in the engine. I see that the balls are pressed so heavily under pressure that they have not enough space for their "volume" (area). Either they have to implode or it's elastic counter force must have greater impact of the whole scene. If you pay close attention to the pendulum movements made by the balls at the bottom, which have the least space, you will see that they are very violent, but apparently have no chance of reaching the outside.
Could it be that your evaluation order
balls.forEach(ball => {
ball.collide();
ball.move();
ball.display(displayWeight);
ball.checkCollisions();
});
is not able to propagate the collisions in a realistic way?
I have a class called MovingThing that is extended by a class called Monster. Each monster needs its own setinterval timing. Here is what I am using:
GameBoard.js
this.monsters = [
new Monster(this, 'monster_0'),
new Monster(this, 'monster_1'),
new Monster(this, 'monster_2')
]
movingThings.js
class Monster extends MovingThing{
constructor(gameboard, dom_id) {
super('monster', 20, document.getElementById(dom_id), gameboard);
}
class MovingThing {
characterType = '';
speed = 0;
dom = null;
gameBoard = null;
step = 4;
direction = 0;
gameLoop = null
size = 40;
x = 0;
y = 0;
sprite = {
left: 0,
right:0,
top: 0,
bottom: 0
};
constructor(characterType, speed, dom, gameboard) {
this.characterType = characterType;
this.speed = speed;
this.dom = dom;
this.gameBoard = gameboard;
self = this;
this.gameLoop = setInterval(function(){self.moveLoop()}, this.speed);
}
moveLoop(){
if (!this.gameBoard.is_active) return;
//if (this.characterType == 'monster') console.log(this.x, this.y)
switch (this.direction) {
case 37:
this.sprite.left -= 2;
break;
case 40:
this.sprite.top += 2;
break;
case 39:
this.sprite.left += 2;
break;
case 38:
this.sprite.top -= 2;
break;
}
this.drawSprite();
if (this.sprite.left % this.size === 0 &&
this.sprite.top % this.size === 0 ){
this.x = this.sprite.left / this.size;
this.y = this.sprite.top / this.size;
this.handleIntersection();
}
// console.log(this.direction)
}
setPosition(grid_x, grid_y){
this.x = grid_x;
this.y = grid_y;
this.sprite.top = grid_y * this.size;
this.sprite.left = grid_x * this.size;
this.drawSprite();
}
drawSprite(){
this.sprite.bottom = this.sprite.top + this.size;
this.sprite.right = this.sprite.left + this.size;
this.dom.style.top = this.gameBoard.gameBoardPosition.top + this.sprite.top + 'px';
this.dom.style.left = this.gameBoard.gameBoardPosition.left + this.sprite.left + 'px';
};
}
What happens is only the last monster moves and it moves 3x as fast as it is supposed to. Like there are 3 timers running, but they all move the same monster.
Other than this code, I tried putting the setinterval in the Monster constructor, but there was the same issue.
Your self is global so you keep overwriting it. Use let to scope it to the function:
let self = this;
this.gameLoop = setInterval(function(){self.moveLoop()}, this.speed);
I have a problem with my canvas game. I try to make it jump but I have some troubles with it. Everything is working fine but if I touch an object from the bottom, it throw me up to the object.
Problem may be with LastColison. Can someone help me ? GIF Image link.
function Block(x, y) {
var size = 80
GameObject.call(this, x*size, y*size, size)
this.img = document.getElementById("block")
this.class = "block"
}
// Dedi vlastnosti z GameObject
Block.prototype = Object.create(GameObject.prototype)
Block.prototype.draw = function() {
ctx.fillStyle = "green"
ctx.drawImage(this.img,this.x,this.y,this.size,this.size)
}
function Player(x, y) {
var size = 120
this.dx = Math.random() * 50 - 25
this.dy = Math.random() * 50 - 25
GameObject.call(this, x*size, y*size, size)
// this.player = document.getElementById("player")
this.img = [document.getElementById("player"),document.getElementById("ball_r"),document.getElementById("ball_l"),document.getElementById("ball_j")]
this.player = this.img[0]
this.class = "player"
}
// Dedi vlastnosti z GameObject
Player.prototype = Object.create(GameObject.prototype)
Player.prototype.move = function() {
var x = this.x
var y = this.y
//ak dam this.y = y -5 môžem pohnuť aj so stlačenou sipkou dole
this.player = this.img[0]
// Posun
if ( keys[37] )
{
if(level == 0){
x -= 4;
}
if (level == 1) {
x -= 8;
}
this.player = this.img[2];
this.y = y;
}
if ( keys[39])
{
if (level == 0) {
x += 4;
}
if (level == 1) {
x += 8;
}
this.player = this.img[1];
}
if ( keys[38] )
{ this.player = this.img[3], this.dy = -10; }
// ak nedam else if mozem ouzzivat naraz viac tlacidiel takze upravit potom
// Test novej pozicie
var collision = false
for (i in scene) {
var obj = scene[i]
if (obj.class == "cloud") { continue; }
if (obj.class == "ladder") { continue; }
if (obj.class == "touched") { continue; }
if (obj.class == "dirt") { this.x = x; this.y = y }
if (obj.class == "block") { this.x = x; this.y = y }
if (obj.class == "enemy") { this.x = x; this.y = y}
var test = x +30 >= obj.x + obj.size || x + this.size - 40<= obj.x /* kolide right*/|| y >= obj.y + obj.size /*kolizia up*/|| y + 40 + this.size <= obj.y /*kolizia bottom*/
if (!test) {
collision = true;
var touch = 0;
if (obj.class == "enemy") {
touch = 1;
if (touch == 1) {
health -= 20; console.log(health);
this.x = x - 250;
if (klik % 2 == 0){
var hit = new Audio('snd/Hit_Hurt15.wav')
hit.play()
}
}
if (health == 0) { health = 0; console.log("GAMEOVER");scene = []}
}
if (obj.class == "coin") {
score += 10; obj.class = "touched";
if (klik % 2 == 0) {
var hrahra = new Audio('snd/pickcoin.wav')
hrahra.play()
}
}
else { touch = 0; }
if (obj.class == "touched") {}
break;
}
}
if (score >= 200 && score <= 200) {
if (klik % 2 == 0) {
var levelup = new Audio('snd/Powerup9.wav')
levelup.loop = false;
levelup.play()
}
level = 1;
health = 100;
score += 1;
}
// Ladder
// if(collision){score += 1,scene.pop() }
// Posun bez kolizie
if (!collision) {
this.x = x
this.y = y + this.dy
this.dy += 0.3;
}
**else {
if (obj.class == this.LastColl) {
this.dy = 0;
this.y = obj.y -160
}
this.dy = 0;
this.LastColl = obj.class
}**
}
Player.prototype.draw = function() {
ctx.fillStyle = "blue"
ctx.beginPath()
ctx.drawImage(this.player,this.x, this.y, 110,160)
ctx.shadowColor = "rgba( 0, 0, 0, 0.3 )";
ctx.shadowOffsetX = -10;
ctx.shadowOffsetY = 0
ctx.shadowBlur = 3;
ctx.drawImage(this.player,this.x,this.y,110,160)
ctx.closePath()
ctx.fill()
}
I can't currently access the GIF you provided. But from what i can gather these lines are your problem:
if (!collision) {
this.x = x
this.y = y + this.dy
this.dy += 0.3;
}
**else {
if (obj.class == this.LastColl) {
this.dy = 0;
this.y = obj.y -160
}
This line - this.y = obj.y -160
Looks like you're telling it to move up -160 pixels on the canvas.
Does this answer your question?
Just a note too - I'd recommend using semicolons at the end of each statement. Don't sometimes use them and other times don't - it will cause problems for you and is bad practice :)
I don't know much about canvas right now, but I noticed that you don't have any semicolons to end your statements...
example:
var x = this.x;
etc.
Another thing that I noticed... The score checks for both greater than or equal to and less than or equal to 200...
if (score >= 200 && score <= 200)
{
...
}