JS constructor class scope error - javascript

var bubbles;
function setup() {
createCanvas(600, 400);
bubbles = new Bubble();
}
function draw() {
background(50);
bubbles.displ();
bubbles.mov();
}
class Bubble {
constructor() {
this.x = 200;
this.y = 200;
};
displ() {
noFill();
stroke(255);
strokeWeight(4);
ellipse(this.x, this.y, 25, 25);
};
mov() {
this.x = this.x + random(-1, 1);
this.y = this.y + random(-1, 1);
}
}
ERROR MESSAGE
14: Uncaught SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
Did you just try to use p5.js's str() function?
If so, you may want to move it into your sketch's setup() function. For more details, see here.
What is wrong here?

The error message says it all... You are not allowed to declare variables before setup.
And there is a solution for your problem, too. 1 I have rewritten it so it fits your code.
var s = function( sketch ) {
var bubbles;
function setup() {
sketch.createCanvas(600,400);
bubbles = new Bubble();
}
function draw() {
sketch.background(50);
sketch.bubbles.displ();
sketch.bubbles.mov();
}
};
var myp5 = new p5(s);
1 From the github repository referred to in your error-message: https://github.com/processing/p5.js/wiki/Global-and-instance-mode

Related

Receving a this error: Uncaught ReferenceError: mainLoop is not defined

My 9 year old son is learning Javascript. I'm not able to easily help him. He's working on a small project, and can't seem to get past an error:
Uncaught ReferenceError: mainLoop is not defined.
This is a great learning opportunity for him. We appreciate any clues as to what's going on in his code that's causing the error. Thanks!
Here's what he's got:
var CANVAS_WIDTH = 800;
var CANVAS_HEIGHT = 400;
var LEFT_ARROW_KEYCODE = 37;
var RIGHT_ARROW_KEYCODE = 39;
//SETUP
var canvas = document.createElement('canvas');
var c = canvas.getContext('2d');
canvas.width = CANVAS_WIDTH;
canvas.height = CANVAS_HEIGHT;
document.body.appendChild(canvas);
window.requestAnimationFrame(mainLoop);
var shapeInfo = {
squares: {
square1: {
x: 10,
y: 10,
w: 30,
h: 30,
color: 'orange'
}
}
};
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
var leftArrowKeyIsPressed = false;
var rightArrowKeyIsPressed = false;
var touchingRightEdge = false;
// SENSORS
function sense() {
if (shapeInfo.squares.square1.x <= CANVAS_WIDTH - 30) {
touchingRightEdge = true;
}
// PLAYER CONTROLS
function onKeyDown(event) {
if (event.keyCode === RIGHT_ARROW_KEYCODE) {
rightArrowKeyIsPressed = true;
}
}
function onKeyUp(event) {
if (event.keyCode === RIGHT_ARROW_KEYCODE) {
rightArrowKeyIsPressed = false;
}
}
//MAIN LOOP
function mainLoop() {
window.requestAnimationFrame(mainLoop);
draw();
}
//DRAW
function draw() {
c.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
// Draw the frame
c.strokeStyle = 'black';
c.strokeRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
// Draw square1
c.fillStyle = shapeInfo.squares.square1.color;
c.fillRect(shapeInfo.squares.square1.x, shapeInfo.squares.square1.y, shapeInfo.squares.square1.w, shapeInfo.squares.square1.h);
if (rightArrowKeyIsPressed) {
if (!touchingRightEdge) {
shapeInfo.squares.square1.x++;
}
}
if (leftArrowKeyIsPressed) {
shapeInfo.squares.square1.x--;
}
// end
}
}
Great to hear that your son is learning something as cool as JavaScript. Now as #Pointy pointed out (no pun intended) you are calling window.requestAnimationFrame(mainLoop); outside the sense function which causes the error. The mainLoop function does not exist outside sense.
The solution to this would to be define your functions globally, in this case meaning:
not inside another function.
So prevent doing:
function foo() {
// Do something
function bar() {
// Do something else
}
}
foo() // Do someting
bar() // Uncaught ReferenceError: bar is not defined.
Now bar only exists within foo. Instead do this:
function foo() {
// Do something
}
function bar() {
// Do something else
}
foo() // Do something
bar() // Do something else
Both functions can now be called from the same scope (remember this word).
Also in your mainLoop function you got to switch some things around. Try to call the draw function first before you start the mainLoop again. JavaScript works from top to bottom. So in the example below it will first draw and then start the loop again.
function mainLoop() {
draw();
window.requestAnimationFrame(mainLoop);
}
You're doing great, kid! Keep it up and come back whenever you want. We'll help you out!

Avoid using global scope in p5

I just started a new project in p5, I've already used it directly imported in the browser, but this time, since it's a more complex project, I'm going to use it in webpack.
I imported the library and bootstraped it in this way:
import * as p5 from 'p5';
function setup() {
createCanvas(640, 480);
}
function draw() {
if (mouseIsPressed) {
fill(0);
} else {
fill(255);
}
ellipse(mouseX, mouseY, 80, 80);
}
But it doesn't work.
The reason is simple: webpack wraps the module in a local scope, and p5 isn't aware of it.
For this reason, I assigned the functions to the global scope:
import * as p5 from 'p5';
window.setup = function () {
createCanvas(640, 480);
}
window.draw = function () {
if (mouseIsPressed) {
fill(0);
} else {
fill(255);
}
ellipse(mouseX, mouseY, 80, 80);
}
And it works fine, but still looks wrong. I don't think that pollulating the global scope is the correct way of working with JS in 2019. Expecially if I'm using webpack and I'm about to implement TypeScript.
So, how can I tell p5 to look for the functions in the module scope and not in the global one?
You'd use instance mode, which doesn't rely on globals. Here's the example from that page:
var sketch = function( p ) {
var x = 100;
var y = 100;
p.setup = function() {
p.createCanvas(700, 410);
};
p.draw = function() {
p.background(0);
p.fill(255);
p.rect(x,y,50,50);
};
};
var myp5 = new p5(sketch);
Live Example:
var sketch = function( p ) {
var x = 100;
var y = 100;
p.setup = function() {
p.createCanvas(700, 410);
};
p.draw = function() {
p.background(0);
p.fill(255);
p.rect(x,y,50,50);
};
};
var myp5 = new p5(sketch);
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>

JS what is the difference of methods defined inside and outside of the constructor function

I have this class object that needs to have its draw function inside the constructor to update the score, if it is outside the score returns undefined.
export class Hud {
constructor(world) {
var self = this;
this.canvas = world.canvas;
this.ctx = world.ctx
this.score = 0;
this.draw = function() {
this.ctx.font = "16px Arial";
this.ctx.fillStyle = "#0095DD";
this.ctx.fillText("Score: " + self.score, 8, 20);
}
}
}
my other class objects have the draw function outside of the constructor like so works fine,
export class Ball {
constructor(world) {
var self = this;
this.canvas = world.canvas;
this.ctx = world.ctx;
self.x = canvas.width / 2;
self.y = canvas.height - 30;
this.ballRadius = 10
this.dx = 2;
this.dy = -2
}
draw() {
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.ballRadius, 0, Math.PI * 2);
this.ctx.fillStyle = "#0095DD";
this.ctx.fill();
this.ctx.closePath();
}
}
My question is what the difference between the two are. I thought if its defined in the constructor the variables are accessible throughout the class object. I guess I'm confused, should there be functions outside of constructor function? Having everything in the constructor seems to be hassle free.
Function defined in the class (not in the constructor) live on the class prototype which each instance is linked to. Functions defined in the constructor become own properties of each instance. If you defined the function in the constructor, each instance will get its own copy of the function. If you don't the instances will defer to the prototype chain and all the instances will point to the same function. For example:
class Hud {
constructor(world) {
this.name = world
this.draw = function() {
console.log("draw on instance from", this.name)
}
}
draw_outside(){
console.log("draw on class from", this.name )
}
}
let h1 = new Hud('h1')
let h2 = new Hud('h2')
console.log(h1.__proto__.draw) // draw not on prototype
console.log(h1.__proto__.draw_outside) // draw_outside is
console.log(h1.draw === h2.draw) // each object gets its own draw
console.log(h1.draw_outside === h2.draw_outside) // but both point to the same draw_outside
console.log(Object.getOwnPropertyNames(h1)) // only draw & name
// both access `this` the same way when called on an instance:
h1.draw()
h1.draw_outside()

Understanding of namespace in JavaScript [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 4 years ago.
I'm fairly new to JavaScript though I do have some programming experience with Python.
My problem is, that I do not seem understand the concept of namespace in JS since it appears to be different than in Python. This is my code:
function snake(x, y) {
// x and y coordinate of square (topleft)
this.x = x;
this.y = y;
// reference to div object 'box2'
this.boxid = "#box";
this.box = document.getElementById(this.boxid);
// attempts to move the box by args
this.move = function (speedx, speedy) {
var m = 50;
// check if the box is within the container, moves if true
if ((this.x+(speedx*m))<=150 && (this.y+(speedy*m))<=150 &&
(this.y+(speedy*m))>=0 && (this.x+(speedx*m))>=0) {
this.x = this.x + speedx*m;
this.y = this.y + speedy*m;
}
}
// called every frame to update position of the box
this.update = function () {
$(this.boxid).css({top: this.y, left: this.x});
}
}
var snakeObj = new snake(100, 100);
var t = setInterval(s.update, 100);
When hitting one of the four arrow keys, the move() function is being executed with the correct parameters.
Now the way code is shown up there, JS tells me that the x and y values in the update() function are "undefined". But as soon as I change them from this.x and this.y to snakeObj.x and snakeObj.y, as well as this.boxid to "#box", everything works perfectly.
I would like to understand why the update() function can't "access" the values from the object while the move() function is perfectly fine with it.
Just for clarification, the working code looks like this:
function snake(x, y) {
// x and y coordinate of square (topleft)
this.x = x;
this.y = y;
// reference to div object 'box2'
this.boxid = "#box";
this.box = document.getElementById(this.boxid);
// attempts to move the box by args
this.move = function (speedx, speedy) {
var m = 50;
// check if the box is within the container, moves if true
if ((this.x+(speedx*m))<=150 && (this.y+(speedy*m))<=150 &&
(this.y+(speedy*m))>=0 && (this.x+(speedx*m))>=0) {
this.x = this.x + speedx*m;
this.y = this.y + speedy*m;
}
}
// called every frame to update position of the box
this.update = function () {
$("#box).css({top: snakeObj.y, left: snakeObj.x});
}
}
var snakeObj = new snake(100, 100);
var t = setInterval(s.update, 100);
It's because you've got another this value; the one for the inside function.
In JavaScript, every function gets its own this value. Functions are bound at call-time, which means that if you don't call them via the attribute access they're called without the correct this value. A common workaround is to set var that = this; in your object's constructor so you can use the original value via closure from a function defined in the constructor.
Another workaround, if you don't mind dropping support for IE11, is to use an ES6 arrow function like so:
function snake(x, y) {
// x and y coordinate of square (topleft)
this.x = x;
this.y = y;
// reference to div object 'box2'
this.boxid = "#box";
this.box = document.getElementById(this.boxid);
// attempts to move the box by args
this.move = (speedx, speedy) => (function (speedx, speedy) {
var m = 50;
// check if the box is within the container, moves if true
if ((this.x+(speedx*m))<=150 && (this.y+(speedy*m))<=150 &&
(this.y+(speedy*m))>=0 && (this.x+(speedx*m))>=0) {
this.x = this.x + speedx*m;
this.y = this.y + speedy*m;
}
}).call(this, speedx, speedy);
// called every frame to update position of the box
this.update = () => $("#box").css({top: this.y, left: this.x});
}
var snakeObj = new snake(100, 100);
var t = setInterval(s.update, 100);

Javascript - proper OOP

Ok, so I wrote some code for a JS game. The code itself works, but isn't in proper OOP form. In the class "Enemy" I need to reference variables and a method from the "Player" class. Look in the "Collision" method, where the variables are referenced. Notice that I get the data specifically from the new instance of "Player" called "player" at the end of the script. For OOP, how am I suppose to share information between these two classes?
Thanks!
var Player = function() {
this.x = 15;
this.y = 15;
};
Player.prototype.reset = function() {
this.x = 200; // reset to this
this.y = 320; // reset to this
};
var Enemy = function() {
this.x = 25;
this.y = 30;
};
Enemy.prototype.collision = function() {
if (player.x >= this.x - 35 & player.x <= this.x + 35) { // check column
if (player.y >= this.y - 30 & player.y <= this.y + 30) { // check row
player.reset(); // calls player method "reset"
}
}
};
// Start Game
setEnemies();
var player = new Player();
in javascript functions can take arguments
solution to your problem could be passing instance of Player to method collision
as #Álvaro Touzón mentioned, a good practice would be to use inheritance as Enemy and Player in your code are now basically the same
also, you could read about ES6 classes, which make programming a bit easier, however they still rely on prototype inheritance which makes them just a syntactic sugar
If you want to use OOP, then perhaps this will help you.
Add helper extend function
function extend(current, base) {
for (var key in base) {
if (base.hasOwnProperty(key)) {
current[key] = base[key];
}
}
current.prototype = Object.create(base.prototype);
};
Create class Avatar as #Álvaro Touzón suggested
var Avatar = (function () {
function Avatar (x, y) {
this.x = x;
this.y = y;
}
Avatar.prototype.reset = function() {
return this;
};
Avatar.prototype.collision = function(object) {
if (object.x >= this.x - 35 & object.x <= this.x + 35) { // check column
if (object.y >= this.y - 30 & object.y <= this.y + 30) { // check row
object.reset(); // calls object method "reset"
}
}
};
return Avatar;
}());
Class Player and Enemy
var Player = (function (superClass) {
extend(Player, superClass);
function Player (x, y) {
superClass.apply(this, arguments);
}
Player.prototype.reset = function() {
this.x = 200; // reset to this
this.y = 320; // reset to this
return this;
};
return Player;
}(Avatar));
var Enemy = (function (superClass) {
extend(Enemy, superClass);
function Enemy (x, y) {
superClass.apply(this, arguments);
}
return Enemy;
}(Avatar));
Create Game
var Game = (function () {
Game.prototype.player = new Player(15, 15);
Game.prototype.enemys = [
new Enemy(25, 30),
new Enemy(10, 30),
];
function Game () {
// setup
}
Game.prototype.start = function() {
for (var i = 0; i < array.length; i++){
var enemy = this.enemys[i];
this.player.collision(enemy);
}
return this;
};
return Game;
}());
Use:
var game = new Game();
game.start();
How it works
You have a group of objects - enemies and the object - player, all of them have the ability to calculate collision between each other, because common ancestor. Every time you call the start of the game will be calculated collision. I would add setInterval in game.start for calculated collision but this complicate the code.
I cannot say how you want to run your game but in general, to share data I always believe in making singleton classes and use it in different objects. This way we can do better error handling also. In js, there are no singleton classes as such to say. But you can always make simple js modules like below:
var abc = (function(){
var abc = "someval"; //private variable
var setAbc = function() {
//some logic
}
var getAbc = function() {
//some logic
}
var publicMethod = function {
//some logic
}
return {
setAbc: setAbc,
getAbc: getAbc,
publicMethod: publicMethod
}
})();
It's non of the buisness of the Enemy, to mutate/reset the Player. Nor is it the buisness of the Player, to wich Position it is reset to.
These things should be done by the game in the main game loop, and the collision-method should only determine wether this Element has hit the passed Element.
//manages the bounding-Box / Collisions,
//also pretty much everything related to (basic) rendering, like Assets/Image or DOM-Node, ... but since you've not included that in your code, I can't adapt it.
class Element{
constructor(config){
let {x, y, width, height, hitBoxOffsetX, hitBoxOffsetY} = config || {};
this.x = +x || 0;
this.y = +y || 0;
this._hitBoxOffsetX = +hitBoxOffsetX || 0;
this._hitBoxOffsetY = +hitBoxOffsetY || 0;
this.width = +width || 0;
this.height = +height || 0;
}
//bounding box
get left(){ return this.x + this._hitBoxOffsetX }
get right(){ return this.left + this.width }
get top(){ return this.y + this._hitBoxOffsetY }
get bottom(){ return this.top - this.height }
collision(other){
return this.right > other.left && this.left < other.right &&
this.top > other.bottom && this.bottom < other.top;
}
}
//everything that can somehow be hit, extends Element
class Player extends Element {
constructor(){
super({
hitBoxOffsetX: -35, //hitBox starts 35px to the left of this.x
hitBoxOffsetY: -30, //hitBox starts 30px to the top of this.y
width: 70, //width of the hitBox
height: 60 //height of the hitBox
});
}
}
class Enemy extends Element {
constructor(){
//since I have no idea about the dimensions of these Entities
super();
}
}
//and walls, for example
class Wall extends Element {
constructor(x, y, width, height){
super({
x, y,
width: +width || 20,
height: +height || 20
});
}
}
And the mentioned collision-checking and resetting happens in the main game-loop, but since I don't know how your game-loop looks like, here some pseudocode:
update(){
requestAnimationFrame(update);
//move everything
//collision-checks
//if your player has some sort of weapon, maybe you want to first check
//wether it has "killed" some enemies,
//before checking wether an enemy has hit your player.
var hitEnemy = enemies.find(enemy => enemy.collision(player));
//btw. Bullets would also be "enemies"
if(hitEnemy){
//and you probably don't want to
player.x = currentLevel.playerResetPosition.x;
player.y = currentLevel.playerResetPosition.y;
//destroy hitEnemy
//maybe mark the player as blinking/non-hitable for a few seconds?
}
//render everything
}

Categories

Resources