p5js instance mode and object orientation - javascript

I have a "sketch.js" where i want to instance multiple canvases und display different objects of one class in them.
sketch.js:
var myObjects = [];
var sketch1 = function (p) {
p.setup = function () {
p.createCanvas(600, 400);
}
p.draw = function () {
p.background(51);
p.rect(p.width/2, p.height/2, 200, 200);
}
};
new p5(sketch1, "canvas_container");
var sketch2 = function (p) {
p.setup = function () {
p.createCanvas(600, 400);
myObjects.push(new myObject(p, 1, 2));
myObjects.push(new myObject(p, 3, 4));
}
p.draw = function () {
p.background();
for (var i=0; i<myObjects.length; i++) {
p.line(0, 0, myObjects[i].x, myObjects[i].y);
myObjects[i].doSomething(Math.random()*10);
}
}
};
new p5(sketch2, "canvas_container");
When do i use "this." and when "p." in this case?
Furthermore I would like to use some "other" methods from the p5 library in my sketch.js file, outside of the instaces, like:
select('..') ...
but I get the error:
select is not defined
I found myself a dirt workaround:
new p5().select('...') ...
What is the clean way to do this?
myObjects.js
function myObject(canvas, x, y) {
this.canvas = canvas;
this.x = x;
this.y = y;
this.doSomething = function(rad) {
this.canvas.ellipse(this.x, this.y, rad, rad);
}
}
Has anybody an example for handeling multiple instances of canvases?

Note that right now, you aren't ever creating a new instance of myObject. Your array is named myObjects, but you're only ever adding p (which is an instance of p5) to it. So you can't magically call your doSomething() function on the objects you've added to your myObjects array, because they aren't myObject objects. They're p5 objects.
I think what you would want to do is take the p variable passed into each of your sketches and pass that into your "class" functions. Something like this:
p.setup = function () {
p.createCanvas(600, 400);
var myObjectOne = new myObject(p, 1, 2);
var myObjectTwo = new myObject(p, 3, 4);
}
Then it should work how you're expecting it to.

Related

copy function does not recognize image on another canvas in p5js

I'm trying to make 'slitscan' effects.
I set 2 canvases. One on the right is for source image, and another is for scan effect.
When i tried it with text function it works perfectly, however with an image, the copy function gives me an error message.
This is the p5 code I wrote
let a1, a2;
let cv1, cv2;
let dy = 0;
let s1 = function(p) {
p.setup = () => {
cv1 = p.createCanvas(300, 300);
p.background(150);
cv1.position(0, 0);
}
p.draw = () => {
p.copy(cv2, 0, dy, 400, 1, 0, dy, 400, 1);
if (dy > cv2.height) {
dy = 0;
} else {
dy += 1;
}
}
}
a1 = new p5(s1);
let s2 = function(p) {
let img;
p.preload = () => {
img = p.loadImage('https://pbs.twimg.com/profile_images/635910989498724356/uY4bc8q2.jpg');
}
p.setup = () => {
cv2 = p.createCanvas(300, 300);
cv2.position(300, 0);
}
p.draw = () => {
p.background(30);
p.imageMode(p.CENTER);
p.image(img, p.mouseX, p.mouseY);
}
}
a2 = new p5(s2);
In addition, if you have a better idea to make multi-canvas or any suggestion about my code, please leave a comment.
Thanks,
In your s1 sketch, you try to do stuff with the canvas cv2. Yet this canvas is only created later in your sketch.
To fix it, just change the order of your two sketches, i.e., call a1 = new p5(s1); after a2 = new p5(s2); (it doesn't matter when you define s1 and s2, only when you instantiate them).
See, for example, this p5 editor sketch.

superclass function being called when subclass function is invoked

I was really struggling coming up with a title but basically I'm working on a game in the html5 canvas and have a class called player with a subclass aiPlayer for when playing against ai. The code for updating the players looks like this:
var entitiesCount = this.entities.length;
for (var i = 0; i < entitiesCount; i++) {
var entity = this.entities[i];
entity.update();
if (entity instanceof special && entity.x > WIDTH || entity.x + 200 < 0) {
this.entities.splice(i, 1);
entitiesCount--;
}
}
However, the aiPlayer never updates with the aiPlayer update function. I've printed out the constructor of each entity and there is one Player and one aiPlayer. However, when printing out the method they are calling, both of them are calling the Player update. Does anyone know why it would do this?
Also, if it helps, the aiPlayer update looks like:
aiPlayer.prototype.update = function() {
if((this.game.timer.gameTime % this.moveTime) > (this.moveTime * 0.9)) {
this.chooseMove();
}
Player.prototype.update.call(this);
};
And ai constructor looks like:
function aiPlayer (game, character, x, y, health) {
Player.call(this, game, character, x, y, health, PLAYER2_CONTROLS, "left");
aiPlayer.prototype = new Player(this.game, this.character, this.x, this.y,
this.health, this.control, this.facing);
aiPlayer.prototype.constructor = aiPlayer;
this.controls = PLAYER2_CONTROLS;
this.attackLength = 50;
this.fleeLength = 70;
this.moveTime = 1;
this.prevControl = "idle";
}
function aiPlayer (game, character, x, y, health) {
Player.call(this, game, character, x, y, health, PLAYER2_CONTROLS, "left");
aiPlayer.prototype = new Player(this.game, this.character, this.x, this.y,this.health, this.control, this.facing);
aiPlayer.prototype.constructor = aiPlayer;
this.controls = PLAYER2_CONTROLS;
this.attackLength = 50;
this.fleeLength = 70;
this.moveTime = 1;
this.prevControl = "idle";
}
These lines here
aiPlayer.prototype = new Player(this.game, this.character,
this.x, this.y,this.health,
this.control, this.facing);
aiPlayer.prototype.constructor = aiPlayer;
are wrong. They are wrong because
you are setting the prototype to an Instance of Player
you are resetting the prototype and the constructor of the prototype of aiPlayer every time you create a new instance of aiPlayer. You should move all modifications to the prototype outside of the constructor function, like this:
-
function aiPlayer (game, character, x, y, health) {
Player.call(this, game, character, x, y, health, PLAYER2_CONTROLS, "left");
this.controls = PLAYER2_CONTROLS;
this.attackLength = 50;
this.fleeLength = 70;
this.moveTime = 1;
this.prevControl = "idle";
}
aiPlayer.prototype.someMethod = function someMethod() {
....
}
A correct way to set the prototype of the subclass is like this
aiPlayer.prototype = Object.create(Player.prototype, {
constructor : {
value : aiPlayer
}
};
This will set as the aiPlayer prototype a new object that inherits from Player.prototype (i.e. has Player.prototype as its prototype) and has aiPlayer registered as its constructor function
Also, the .update of Player is called from aiPlayer because you explicitly calling it here
aiPlayer.prototype.update = function() {
if((this.game.timer.gameTime % this.moveTime) > (this.moveTime * 0.9)) {
this.chooseMove();
}
Player.prototype.update.call(this); //you call the Player.update()
};
Considering the above, this is how you should register the aiPlayer.update
aiPlayer.prototype = Object.create(Player.prototype, {
constructor : {
value : aiPlayer
}
};
aiPlayer.prototype.update = function update() {
//your code here
}
Now, when you create a new aiPlayer object instance, the inheritance chain will go like this
aiPlayerInstance --> aiPlayer.prototype --> Player.prototype
and when you call aiPlayerInstance.update() it will first look to aiPlayer.prototype and since aiPlayer.prototype does have a method called update it will execute it, and it will not look any further down the inheritance chain (i.e. in Player.prototype)

Javascript constructor not working

I posted this on gamedev.stackexchange but was referred here so I'll try. I've got this simple menu that is a function, with a mainmenu.prototype.Render to draw it to the screen. Inside the mainmenu function i would like to make an array of objects containing the buttons x, y positions and the .src.
This is my current code that works, so no problem with the function itself:
this.Mainmenu = function() {
}
this.Mainmenu.prototype.Render = function() {
imgPause = new Image();
imgPause.src = 'img/pause.png';
c.drawImage(imgPause, canvas.width - 42, 10);
}
var mainmenu = new self.Mainmenu();
What I would like the final result to look like, but can't get to work (I've included the error in a comment):
this.Mainmenu = function() {
this.button = function(src, X, Y) {
this = new Image(); // Gives error "Invalid left-hand side in assignement"
this.src = src;
this.X = X;
this.Y = Y;
}
this.buttons = [pause = new this.button(src, X, Y)];
}
this.Mainmenu.prototype.Render = function() {
for (i = 0; i < this.buttons.length; i++) {
c.drawImage(this.src, this.X, this.Y);
}
}
var mainmenu = new self.Mainmenu();
But it doesn't work, if anyone can identify where my mistake is it would be appreciated, my patience is about to run out.
Well, your mistake is exactly what your js interpreter says it is - the left side of your assignment is invalid. Namely, you cannot assign this to anything, that's a rule of thumb in all languages that have the this word. The reasoning behind that is obvious - this denotes the current context of the function, the hidden argument of its. If you could overwrite it dynamically, you could alter the behaviour of every single function that is using yours thus the whole program.
How not to use this in this broken way:
this.MainMenu = function() {
this.Button = function(src, X, Y) {
var image = new Image();
image.src = src;
image.X = X;
image.Y = Y;
return image;
}
this.buttons = [pause = new this.Button(src, X, Y)];
}
Also, name your classes with PascalCase (Button, not button) and your variables with camelCase EVERYWHERE (x, not X).
You cannot do this
this.button = function(src, X, Y) {
this = new Image(); // Gives error "Invalid left-hand side in assignement"
}
this represents the current instance of Mainmenu. You cannot override an instance by another
instance.
No sense.

Updating an object's definition in javascript

I'm fairly new to object orientated stuff so this may very well be the wrong way to be going about getting this done.
This is a very slimmed down version of what I currently have, but the concept is essentially the same. When the user clicks on a canvas element on my page, I create 20 instances of the particle object below, append them to an array whilst at the same time updating the canvas at 30FPS and drawing circles based on the x property of the instances of each object. Once a particle is off the screen, it's removed from the array.
var particle = function()
{
var _this = this;
this.velocity = 1;
this.x = 0;
this.updateVelocity = function(newVelocity)
{
_this.multiplier = newVelocity;
}
var updateObject = function()
{
_this.x += velocity;
}
}
I would like the user to be able to control the velocity of new particles that are created using an input element on the page. When this is updated I have an event listener call
particle.updateVelocity(whateverTheUserEntered);
However I get the error "particle has no method updateVelocity". After a bit of reading up on the subject I understand that to call that function I need to create an instance of the object, but this will only update the velocity value of that instance which isn't going to work for me.
My question is, is there a way to achieve what I'm doing or have I approached this in completely the wrong way? As I said, I'm still getting to grips with OOP principles so I may have just answered my own question...
Try this:
var particle = new (function()
{
var _this = this;
this.velocity = 1;
this.x = 0;
this.updateVelocity = function(newVelocity)
{
_this.multiplier = newVelocity;
}
var updateObject = function()
{
_this.x += velocity;
}
})();
Your's is creating a function and then setting the variable particle to that value. particle will not have any special properties because of this. My example above, however, by using new and the function as a constructor assigns particle an instance of a (now anonymous) class.
I think what you want is:
// define a particle "class"
function Particle() {
var _this = {};
_this.velocity = 1;
_this.x = 0;
_this.multiplier = 1;
_this.updateVelocity = function(newVelocity)
{
_this.multiplier = newVelocity;
}
_this.updateObject = function()
{
_this.x += velocity;
}
return _this;
}
// make 1 particle
var myParticle = new Particle();
myParticle.updateVelocity(100);
// make a bunch of particles
var myParticles = [];
for (var i=0; i < 100; i++) {
var p = new Particle();
p.updateVelocity(Math.random * 100);
myParticles.push(p);
}
If you change it to
var particle = new function () {
}
The 'new' will cause creation of an instance.
So create a function that builds new particle instances for you.
Make velocity static and have a static method to update it. This way, you can still make instances of particle and update the velocity for all of them.
var particle = function() {
// particle stuff
}
particle.velocity = 1;
particle.updateVelocity = function(newVelocity) {
this.velocity = newVelocity
}

Initiation of an object inside another

I have a piece of js software that is structured like so:
obj = new object[id]();
function wrapperFunction (e) {
var pos = findPos(this);
e._x = e.pageX - pos.x;
e._y = e.pageY - pos.y;
var func = obj[e.type];
if (func) {
func(e);
}
}
__
obj.line = function () {
this.started = false;
this.mousedown = function (e) {
}
this.mousemove = function (e) {
if (this.started) {
}
}
this.mouseup = function (e) {
if (this.started) {
}
}
}
The above code block is duplicated for multiple shapes so there is also a obj.square obj.circle etc...
I also have a shape object that is as follows.
function Shape (type, color, height, width, radius, x, y) {
this.type = type;
this.color = color;
this.h = height;
this.w = width;
this.r = radius;
this.points = ["x","y"];
this.points["x"] = [x];
this.points["y"] = [y];
};
I would like to initiate the shape object on a mousedown for each obj.* and populate the shape object with the propper info.
Now for the issue.
The radius is calcuated on every mousemove as well as height and width but when I add shapes = new Shape(circle, black, 10, 10, null, e._x, e._y) to the mousemove so it looks like...
this.mousemove = function (e) {
if (this.started) {
shapes = new Shape(circle, black, 10, 10, null, e._x, e._y);
}
}
The shape object does not create.
If I create the shape object inside the wrapper function instead of the mousemove then the object initiates but I cannot use radius or height/width.
How can I create an object inside another object inside a wrapper function so I can use calculated terms inside the created object? Is there an alternate route to take besides what I am doing?
Aside from wonkiness in the obj = new object[this.id](); line, I think you're just missing a this keyword:
this.mousemove = function (e) {
if (this.started) {
this.shapes = new Shape(circle, black, 10, 10, null, e._x, e._y);
}
}
Edit just noticed more wonkiness in your code (yes, that's a technical term :). I think you want to change these lines in the constructor:
this.points = ["x","y"]; // creates an array, which is indexed by numbers
this.points["x"] = [x]; // tacks on some ad-hoc properties to the array, which
this.points["y"] = [y]; // doesn't really make sense
to this:
this.points = {x: x, // I think this is what you actually mean to do.
y: y};

Categories