Hello, @anoniem, and welcome to the Processing Forum!
The code below is almost identical to your original. Note that among the several changes that were made, the most significant is that this line was added to the move() method, so that the Sheep gets displayed whenever its position changes:
From an OOP perspective, there are still additional improvements that should be made to the code. For example, it is best, in general, for changes to properties of instances of a class to be made via methods, rather than directly via external functions.
Note these statements within the mouseDragged() method:
newSheep.pos.x = mouseX;
newSheep.pos.y = mouseY;
Updating the affected properties should be the responsibility of the move() method of the Sheep class, rather than to be performed directly by that external method. This will be especially beneficial if multiple instances of Sheep are to be created.
If you continue to refine the code and need additional assistance, we will be glad to provide it.
You can revise the code so that the move() method does the work of looking at mouseX and mouseY. Then remove that task from the mouseDragged() function, and let it just call the move() method to do that job.
Yes, it is possible for the mouse to outrun the Sheep. With there being only one Sheep, you can comment out a line to overcome the problem:
} else {
// this.active = false;
}
However, when you have more than one Sheep, you’ll probably need to maintain a variable that keeps track of which one is current. Then, that would be the one that goes wherever the mouse does, even when the mouse outruns it. You can set things up so that a click on a Sheep designates it as current.
class Sheep {
constructor(x, y) {
this.pos = createVector(x, y);
this.size = 20;
this.active = false;
}
move() {
this.prevX = this.x;
this.prevY = this.y;
if (this.active) {
this.pos.x = mouseX;
this.pos.y = mouseY;
}
// reset when sheep is outside viewport
if (
this.pos.x > width ||
this.pos.x < 0 ||
this.pos.y > height ||
this.pos.y < 0
) {
this.pos.x = width / 2;
this.pos.y = height / 2;
}
console.log(this.active);
this.show();
}
show() {
rectMode(CENTER);
rect(this.pos.x, this.pos.y, this.size);
}
point_is_within(x, y) {
if (
x > this.pos.x - this.size / 2 &&
y > this.pos.y - this.size / 2 &&
x < this.pos.x + this.size / 2 &&
y < this.pos.y + this.size / 2
) {
return true;
} else {
return false;
}
}
activate() {
this.active = true;
}
deactivate() {
this.active = false;
}
}
let newSheep;
let myCanvas;
function setup() {
myCanvas = createCanvas(500, 500);
background(0);
newSheep = new Sheep(20, 20);
}
function draw() {
background(220);
newSheep.show();
}
function mouseDragged() {
newSheep.move();
}
function mousePressed() {
x = mouseX;
y = mouseY;
if (newSheep.point_is_within(x, y)) {
newSheep.activate();
} else {
newSheep.deactivate();
}
}
Note that some new methods have been added to class Sheep, and note how those methods are used to interact with the instance, newSheep.
A mousePressed() function has also been added that enables newSheep to be activated when the mouse is pressed within its boundaries. Then it can be dragged. The instance newSheep is deactivated whenever the mouse is pressed outside its boundaries.
The changes made are only suggestions. You may prefer a different way of organizing the code. However, the suggested changes may help prepare for managing multiple Sheep, which could be organized and managed by a new class, perhaps named Flock. A feature of this Flock class could be an Array of Sheep instances. Every time the mouse is pressed, the instances of Sheep would be checked for possible activation. Then, when the mouse is dragged, only the active Sheep would be moved.
This project is really interesting, so I have also been experimenting with your code, specifically to instantiate multiple Sheep.
Yes, I noticed that when multiple Sheep are dragged, they all occupy the same bounds. So this was my solution, developed in order to drag only one Sheep at a time:
class Sheep {
constructor(x, y, id) {
this.pos = createVector(x, y);
this.size = 20;
this.active = false;
this.id = id;
}
move() {
this.prevX = this.x;
this.prevY = this.y;
if (this.active) {
this.pos.x = mouseX;
this.pos.y = mouseY;
}
// reset Sheep to center when Sheep is outside viewport
if (
this.pos.x > width ||
this.pos.x < 0 ||
this.pos.y > height ||
this.pos.y < 0
) {
this.pos.x = width / 2;
this.pos.y = height / 2;
}
console.log(this.active);
this.show();
}
show() {
rect(this.pos.x, this.pos.y, this.size);
text(this.id, this.pos.x, this.pos.y);
}
point_is_within(x, y) {
if (
x > this.pos.x - this.size / 2 &&
y > this.pos.y - this.size / 2 &&
x < this.pos.x + this.size / 2 &&
y < this.pos.y + this.size / 2
) {
return true;
} else {
return false;
}
}
activate() {
this.active = true;
}
deactivate() {
this.active = false;
}
}
class Flock {
constructor() {
this.sheep = [];
this.active_sheep = [];
}
addSheep(sh) {
this.sheep.push(sh);
}
activateSheep(sh) {
sh.activate();
this.active_sheep.push(sh);
}
deactivateAllSheep() {
for (let i = 0; i < this.active_sheep.length; i++) {
this.active_sheep[i].deactivate();
this.active_sheep = [];
}
}
pollSheep() {
let x = mouseX;
let y = mouseY;
this.deactivateAllSheep();
for (let i = this.sheep.length - 1; i >= 0; i--) {
if (this.sheep[i].point_is_within(x, y)) {
this.activateSheep(this.sheep[i]);
break; // drag only one Sheep at a time
}
}
}
moveSheep() {
for (let i = 0; i < this.active_sheep.length; i++) {
this.active_sheep[i].move();
}
}
showSheep() {
for (let i = 0; i < this.sheep.length; i++) {
this.sheep[i].show();
}
}
}
let myCanvas;
let myFlock;
function setup() {
myCanvas = createCanvas(500, 500);
background(0);
rectMode(CENTER);
textAlign(CENTER, CENTER);
myFlock = new Flock();
for (let i = 0; i < 10; i++) {
myFlock.addSheep(new Sheep(random(width), random(height), "" + i));
}
}
function draw() {
background(220);
myFlock.showSheep();
}
function mouseDragged() {
myFlock.moveSheep();
}
function mousePressed() {
myFlock.pollSheep();
console.log(myFlock.active_sheep);
}
This is the line that limits the dragging to one Sheep:
break; // drag only one Sheep at a time
However, your solution represents the the best path to proceed, especially with the animation.
The significant detail is that the method uses this to refer to the current instance of a class, for instance, here:
let d = dist(this.pos.x, this.pos.y, otherSheep.pos.x, otherSheep.pos.y);
With that method situated in the definition of class Herd, this refers to the current instance of Herd, and therefore the statement attempts to compute a distance between the Herd and a Sheep. Instead, what is needed is a distance between the current Sheep and another Sheep. Therefore, the method should be moved into the definition for class Sheep. So, the best action to take would be to make that move, then to continue with testing and refinement of the code.