p5.js classes (class "example" is not changing my this.x value) - javascript

I want to move an ellipse but I am stuck because my this.x value isn't changing... print("hit this.y = 30"); is working just fine but this--; is not working. Is anything blocking it? Thank you in advance for your time!
let redBubble = 0;
let x = false;
let y = 0;
let d = 0;
function setup() {
createCanvas(400, 400);
redBubble = new Bubble(300, 100, 10);
}
function draw() {
background(56, 23, 56);
redBubble.show();
redBubble.toRight();
redBubble.toLeft();
redBubble.example();
};
class Bubble {
constructor(x, y, d) {
this.x = x;
this.y = y;
this.d = d;
}
show() {
fill(255);
ellipse(this.x, this.y, this.d * 2);
}
toRight() {
if (this.x < 350) {
this.x = this.x + 1;
} else if (this.x === 350) {
this.y = this.y - 1;
}
}
toLeft() {
if (this.y <= 30) {
x = true;
} else if (this.y === 30) {
x = false;
}
}
example() {
if (x) {
this.x--; // not working
print("hit this.y = 30"); // working
}
}
}

Your code is running, but it sounds like it's not doing what you want it to.
All of your functions show(), toRight(), toLeft(), example(), are running every single draw. So every single time, toRight() is incrementing this.x by 1 (unless this.x is exactly 350). So when example() decrements it by 1, they are in effect canceling each other out and not changing this.x, when x is true and this.x is 349.
If you wanted the net effect to be this.x to decrease, you could replace this.x--; with this.x = this.x - 2;. But this is not a great solution.
Using states
It looks like you want to do different things at different stages of your animation, i.e. go right, then go up, then go left. As you've discovered, it can be pretty tricky to coordinate all of these different changes when all of your functions are running every cycle.
Instead, it's a lot easier to conditionally run only the functions you want, depending on what stage of your animation you're in. For this you can use an internal variable to store what it should be doing (I'll use this.direction for this example). Then in your draw function, you'd use if/then statements, like this:
function draw() {
background(56, 23, 56);
redBubble.show();
if (redBubble.direction === 'right') {
redBubble.goRight();
} else if (redBubble.direction === 'up') {
redBubble.goUp();
} else if (redBubble.direction === 'left') {
redBubble.goLeft();
}
};
And then in your individual functions, you'd change direction based on some condition. For example:
toRight() {
this.x = this.x + 1;
if (this.x === 350) {
this.direction = 'up';
}
}
Here's a working snippet of the above technique:
let redBubble;
function setup() {
createCanvas(400, 400);
redBubble = new Bubble(300, 100, 10);
}
function draw() {
background(56, 23, 56);
redBubble.show();
if (redBubble.direction === 'right') {
redBubble.goRight();
} else if (redBubble.direction === 'up') {
redBubble.goUp();
} else if (redBubble.direction === 'left') {
redBubble.goLeft();
} else if (redBubble.direction === 'down') {
redBubble.goDown();
}
};
class Bubble {
constructor(x, y, d) {
this.x = x;
this.y = y;
this.d = d;
this.direction = 'right';
}
show() {
fill(255);
ellipse(this.x, this.y, this.d * 2);
}
goRight() {
this.x = this.x + 1;
if (this.x === 350) {
this.direction = 'up';
}
}
goLeft() {
this.x = this.x - 1;
if (this.x === 30) {
this.direction = 'down';
}
}
goUp() {
this.y = this.y - 1;
if (this.y === 30) {
this.direction = 'left';
}
}
goDown() {
this.y = this.y + 1;
if (this.y === 300) {
this.direction = 'right';
}
}
}
<script src="https://unpkg.com/p5#0.6.1/lib/p5.min.js"></script>

Related

Applying prototype functions to 2 different objects in a loop only works on the second object

Sorry if question is bad. Its my first one. Im setting prototype functions on Ball object instances but in the loop the function only work on the instance that is called second(last). I tries to search for answer online.
function Ball(x, y, velX, velY, color, size) {
this.x = x;
this.y = y;
this.velX = velX;
this.velY = velY;
this.color = color;
this.size = size;
}
Ball.prototype.draw = function() {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.fill();
}
Ball.prototype.boundaries = function() {
if (this.x - this.size > width) {
this.x = 0 - this.size;
} else if (this.x + this.size < 0) {
this.x = width - this.size;
} else if (this.y - this.size > height) {
this.y = 0 - this.size;
} else if (this.y + this.size < 0) {
this.y = height - this.size;
}
}
/** Controls that is only working on second object**/
Ball.prototype.setControls = function() {
let _this = this;
window.onkeydown = function(e) {
if (e.key === 'a' && _this.velX > -4) {
_this.velX -= 1;
} else if (e.key === 'd' && _this.velX < 4) {
_this.velX += 1;
} else if (e.key === 'w' && _this.velY > -4) {
_this.velY -= 1;
} else if (e.key === 's' && _this.velY < 4) {
_this.velY += 1;
}
}
this.x += _this.velX;
this.y += _this.velY;
}
let testBall = new Ball(50, 100, 4, 4, 'blue', 10);
let ball2 = new Ball(200, 200, 4, 4, 'green', 12);
function loop() {
ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
ctx.fillRect(0, 0, width, height);
/** If I switch testBall with ball2** setControls
will work on testBall**/
testBall.draw();
testBall.setControls();
testBall.boundaries();
ball2.draw();
ball2.boundaries();
ball2.setControls();
I've tried to put an if else statement where I initialize x = 1 before the loop and the for each odd num I run the function on 1 object and then even numbers on the other object but its laggy and it obviously becomes a problem when I need more objects.
requestAnimationFrame(loop);
}
loop();
by using window.onkeydown = you are overriding the existing handler, so it will work only for the last initialized instance, you can use addEventListener instead

Refers to a global variable while encapsulated one is available

In case below "player" referring to global variables, that breaks OOP principle of encapsulation. Whatever I need from the global scope pass to the class instance via constructor. How can I did it?
If I create parent variable (some Character), than code will not work. Maybe this is a nube question, but I don't know, how it works.
p.s. - when I take "player" in "Enemy" thought "this", all construction breaks down.
var Enemy = function(x, y, speed, player) {
this.x = x;
this.y = y;
this.speed = speed;
this.sprite = 'images/enemy-bug.png';
this.player = player;
};
Enemy.prototype.update = function(dt) {
this.x += +(this.speed * dt);
if (this.x > canvasWidth) {
this.x = borgSpawn;
this.speed = Math.floor(100 + Math.random() * 200);
}
if (
player.x > this.x - enemySize &&
player.x < this.x + enemySize &&
player.y > this.y - enemySize &&
player.y < this.y + enemySize
) {
player.x = playerSpawnX;
player.y = playerSpawnY;
}
};
Enemy.prototype.render = function() {
ctx.drawImage(Resources.get(this.sprite), this.x, this.y);
};
var enemyLocation = [60, 140, 220];
var allEnemies = enemyLocation.map(y => new Enemy(0, y, 300, player));
var Player = function(x, y) {
this.x = x;
this.y = y;
this.sprite = 'images/char-boy.png';
};
Player.prototype.update = function() {
if (this.y < 60) {
this.x = playerSpawnX;
this.y = playerSpawnY;
}
};
Player.prototype.render = function() {
ctx.drawImage(Resources.get(this.sprite), this.x, this.y);
};
Player.prototype.handleInput = function(key) {
if (key === 'left' && this.x > 0) {
this.x = this.x - horizontalStep;
} else if (key === 'right' && this.x != stepZone) {
this.x = this.x + horizontalStep;
} else if (key === 'up') {
this.y = this.y - verticalStep;
} else if (key === 'down' && this.y != stepZone) {
this.y = this.y + verticalStep;
}
};
var player = new Player(playerSpawnX, playerSpawnY);
document.addEventListener('keyup', function(e) {
var allowedKeys = {
37: 'left',
38: 'up',
39: 'right',
40: 'down'
};
player.handleInput(allowedKeys[e.keyCode]);
});
No need to pass "player" parameter to "enemy" object. Your Enemy.prototype.update function will work fine without this reference.

Brackets p5.js "Uncaught TypeError: Cannot read property 'offscreen' of undefined"

I am learning about Neural Networks(and learning JS at the same time) at the moment and wanted to start with a little game in Javascript to implement my learned stuff. So first things first, create a game, shouldn't be too hard right? Well.. Looks like I am too blind to find the mistake, so I would be really glad if someone could help me.
I am getting the following error message: "Uncaught TypeError: Cannot read property 'offscreen' of undefined".
It occurs when the player dies. I guess it has to do with the array being cleared when "resetting" and maybe the for-loop then still tries to delete the offscreen pipe, but it cant, as the array is empty or something like this. Dont know how to fix that tho..
https://thumbs.gfycat.com/WanDifficultAnaconda-size_restricted.gif
The Game is just an easy "objects come flying to you and you gotta jump over them".
Here's the whole code, it really isnt much:
sketch.js
let obstacle;
let player;
let obstacles = [];
let score = 0;
let highscore = 0;
function setup() {
createCanvas(600, 600);
obstacle = new Obstacle();
player = new Player();
}
function draw() {
background(0, 128, 128);
for(let i = obstacles.length-1; i >= 0; i--) {
if(obstacles[i].hits(player)) {
reset();
}
if(obstacles[i].offscreen()) {
obstacles.splice(i, 1);
}
obstacles[i].show();
obstacles[i].update();
}
player.show();
player.update();
score++;
currentScore();
newhighscore();
if(frameCount % 100 == 0) {
obstacles.push(new Obstacle());
}
}
function keyPressed() {
if(keyCode == 87 && player.y + player.h == height) {
player.jump();
}
}
function reset() {
if(score > highscore) {
highscore = score;
}
obstacles = [];
score = 0;
player = new Player();
}
function currentScore() {
strokeWeight(0);
stroke(102,205,170);
fill(14,47,68);
textSize(24);
text(score, 10, 30);
}
function newhighscore() {
strokeWeight(0);
stroke(102,205,170);
fill(14,47,68);
textSize(16);
text(highscore, 11, 48);
}
player.js
class Player {
constructor() {
this.x = 100;
this.h = 30;
this.w = this.h;
this.y = height - this.h;
this.gravity = 0.5;
this.lift = -9;
this.velocity = 0;
}
show() {
fill(0);
rect(this.x, this.y, this.w, this.h);
}
update() {
this.velocity += this.gravity;
this.y += this.velocity;
if(this.y + this.h > height) {
this.y = height - this.h;
this.velocity = 0;
}
}
jump() {
this.velocity += this.lift;
}
}
obstacle.js
class Obstacle {
constructor() {
this.x = width;
this.h = random(30, 50);
this.w = this.h;
this.y = height-this.h;
this.speed = 5;
}
show() {
noStroke();
fill(255, 115, 115);
rect(this.x, this.y, this.w, this.h);
}
update() {
this.x -= this.speed;
}
offscreen() {
if(this.x < -width) {
return true;
} else {
false;
}
}
hits(player) {
if(player.x <= this.x + this.w && player.x + player.w >= this.x) {
if(player.y + player.h >= this.y) {
return true;
} else {
return false;
}
}
}
}
My Solution
Explanation
Yeah you had the reason correct, when reset() is called, it changes to obstacles = [] but the
for(let i = obstacles.length-1; i >= 0; i--) {
if(obstacles[i].hits(player)) {
reset();
...*Continued*
}
in your draw function still continues. So you just had to add break; in the hit condition after reset. See in the link I've uploaded it works completely fine.
for(let i = obstacles.length-1; i >= 0; i--) {
if(obstacles[i].hits(player)) {
reset();
break;
...*Continued*
}
When you add break, the for loop terminates and moves down to the functions that follow. Since obstacles = [] is empty, undefined error is not shown as empty indexes are not accessed.

Image does not translate when keys are pressed

Want to move an image across the screen. At the moment the image renders but does not translate the page when the specific keys are pressed. Can't see to get it to work - any suggestions?? I included the entire block of code as I believe it makes more sense.
var player;
var img;
function preload() {
img = loadImage("charcter.png");
}
function setup() {
background(255, 0, 0, 0.4);
createCanvas(1000, 600);
player = new Astro(0,0);
}
function draw() {
// clear();
player.show();
player.update();
}
function keyPressed() {
if (keyCode === UP_ARROW) {
player.dir(0, -10);
} else if (keyCode === DOWN_ARROW) {
player.dir(0, 10);
} else if (keyCode === RIGHT_ARROW) {
player.dir(10, 0);
} else if (keyCode === LEFT_ARROW) {
player.dir(-10, 0);
}
}
function Astro(x, y) {
this.x = x;
this.y = y;
this.xSpeed = 0;
this.ySpeed = 0;
this.img = img;
this.show = function() {
image(img, this.x, this.y);
};
this.update = function() {
this.x += this.xSpeed;
this.y += this.ySpeed;
if (this.x > width || this.x < 0) {
this.xSpeed = this.xSpeed * -1;
}
if (this.y > height || this.y < 0) {
this.ySpeed = this.ySpeed * -1;
}
};
this.dir = function(x, y) {
this.xspeed = x;
this.yspeed = y;
}
}
You need to take a step back and debug your program. For example, are you sure the keyPressed() function is firing? Are you sure the if statements are working the way you expected? Are you sure the player.dir() function is being called?
Use console.log() statements, or use the JavaScript debugger, to answer all of the above. Figure out exactly which line of code is behaving differently from what you expected, and then isolate that problem in a MCVE. (For example, don't include image loading code if the problem is not directly related to loading images. Use a basic rectangle instead.) Good luck.

Why isnt collision detection working? (p5 js frame work)

I created a collision detection between Snake and BasicEnemy. I created a for loop to make five different enemies but the collision detection doesn't get called on any of the enemies that were created from the for loop. The collision only works with the one BasicEnemy object. Why isn't collision function being called for all of the enemies inside the array? Thank you.
Sketch.js
var snake;
var food;
var basicEnemy;
var scl = 20;
var enemies = [];
function setup() {
createCanvas(600, 500);
snake = new Snake();
basicEnemy = new BasicEnemy();
//** CREATE FIVE ENEMIES **
for (var i = 0; i < 5; i++) {
enemies[i] = new BasicEnemy();
}
}
// **FUNCTION WHEN SNAKE HITS ENEMY**
function collision() {
console.log("hit!");
}
function draw() {
background(51);
//Draw snake
snake.update();
snake.show();
//Draw basicEnemy
basicEnemy.update();
basicEnemy.show();
//** LOOP THROUGH ENEMIES AND UPDATE AND SHOW **
for (var i = 0; i < enemies.length; i++) {
enemies[i].show();
enemies[i].update();
if (enemies[i].hits(snake)) {
collision();
}
}
}
function keyPressed() {
if (keyCode === UP_ARROW){
snake.dir(0, -1);
} else if (keyCode === DOWN_ARROW) {
snake.dir(0, 1);
} else if (keyCode === LEFT_ARROW) {
snake.dir(-1 , 0);
} else if (keyCode === RIGHT_ARROW) {
snake.dir(1 , 0);
}
}
BasicEnemy.js
function BasicEnemy() {
this.x = random(700);
this.y = random(700);
this.velX = 15;
this.velY = 15;
}
//** FUNCTION TO CHECK IF ENEMY AND SNAKE ARE IN THE SAME LOCATION **
this.hits = function (pos) {
var = d = dist(this.x, this.y, pos.x, pos.y);
if(d < 1) {
return true;
} else {
return false;
}
}
this.show = function () {
fill(255, 0, 100);
rect(this.x, this.y, scl, scl);
}
Snake.js
function Snake() {
this.x = 0;
this.y = 0;
this.xspeed = 1;
this.yspeed = 0;
this.update = function() {
this.x = this.x + this.xspeed * scl;
this.y = this.y + this.yspeed * scl;
this.x = constrain(this.x, 0, width - scl);
this.y = constrain(this.y, 0, height - scl);
}
this.show = function() {
fill(255);
rect(this.x, this.y, scl, scl);
}
this.dir = function (x , y) {
this.xspeed = x;
this.yspeed = y;
}
}
Because you're essentially checking for the distance between the top left corners of the snake and the enemy, this'll only return true, if they completely overlap.
Use an AABB collision detection instead:
return this.x + scl >= pos.x && this.x <= pos.x + scl && this.y + scl >= pos.y && this.y <= pos.y + scl;
This returns true, if the first rectangle contains the second rectangle.
MDN says:
One of the simpler forms of collision detection is between two rectangles that are axis aligned — meaning no rotation. The algorithm works by ensuring there is no gap between any of the 4 sides of the rectangles. Any gap means a collision does not exist.

Categories

Resources