p5js mousePressed on a 2D grid - javascript

So in the p5js editor, I am trying to visualize a 2d array using squares. I want a square to change color via mousPressed() depending on the coordinates of the mouse in relation to the square. I am getting color changes but the square I click isn't the one that changes.
I am logging the node that I am clicking, and it seems like it is correct, but the visualization of the 2d array seems wrong.
I have a 3x3 grid of square x's such that:
x x x
x x x
x x x
I expect when I click the top left then top middle, it would change color to blue such that
o o x
x x x
x x x
But when I click the top left then the top middle, i get
x o o
x x x
x x x
it seems the square that I click changes the square next to it, rather than the one I expect.
example in p5.editor:
https://editor.p5js.org/julien24lopez/present/8_vwyHTjRW
let arr = [];
function setup(){
canvas = createCanvas(200, 200);
for(var j = 0; j < 3; j++){
var inArr = [];
for(var i = 0; i < 3; i++){
var rect = new Rect(i,j);
inArr.push(rect);
}
arr.push(inArr)
}
}
function draw(){
for(var i = 0; i < arr.length; i++){
for(var j = 0; j < arr[i].length; j++){
arr[i][j].show()
}
}
}
function mousePressed(){
arr.forEach(function(e, index){
e.forEach(function(d,index2){
arr[index][index2].clicked()
});
});
}
function Rect(i,j){
this.fill = 'red'
this.i = i;
this.j = j;
this.x = i * 20
this.y = j * 20
this.clicked = function(){
let x1 = this.x, x2 = x1 + 20,
y1 = this.y, y2 = y1 + 20;
if((mouseX>x1&&mouseX<x2)&&(mouseY>y1&&mouseY< y2)){
console.log(this)
this.fill = 'black'
}
}
this.show = function(){
rect(i*20,j*20,20,20)
fill(this.fill)
stroke('blue')
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>

You do not draw the quads at the correct position. The position of the quads is (this.x, this.y) rather than (i*20, j*20):
function Rect(i,j){
// [...]
this.show = function(){
fill(this.fill)
stroke('blue')
// rect(i*20,j*20,20,20)
rect(this.x, this.y, 20, 20)
}
}
Example:
let arr = [];
function setup(){
canvas = createCanvas(200, 200);
for(var j = 0; j < 3; j++){
var inArr = [];
for(var i = 0; i < 3; i++){
var rect = new Rect(i,j);
inArr.push(rect);
}
arr.push(inArr)
}
}
function draw(){
background(255);
for(var i = 0; i < arr.length; i++){
for(var j = 0; j < arr[i].length; j++){
arr[i][j].show()
}
}
}
function mousePressed(){
arr.forEach(function(e, index){
e.forEach(function(d,index2){
arr[index][index2].clicked()
});
});
}
function Rect(i,j){
this.fill = 'red'
this.i = i;
this.j = j;
this.x = i * 20
this.y = j * 20
this.clicked = function(){
let x1 = this.x, x2 = x1 + 20,
y1 = this.y, y2 = y1 + 20;
if( mouseX > x1 && mouseX < x2 && mouseY > y1 && mouseY < y2){
console.log(this)
this.fill = 'black'
}
}
this.show = function(){
fill(this.fill)
stroke('blue')
rect(this.x, this.y, 20, 20)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>

Related

Program integrated into html that creates a 4x4 grid and draws like Armin Hoffman

So far, I've done research into the design by Armin Hoffman (rubber band design). The objective is to create a grid of circles which can be selected to change from white to black and connect these circles in a natural fluid shape that avoids white circles but never traps them. I've been using Codecademy to learn the basics while copying and studying examples on the p5.js website and additionally following The Coding Train on Youtube (specifically: 7.4 mouse interaction and 7.6 clicking on objects).
Check #MauriceMeilleur on Twitter between Feb 2021 and Aug 2021
Also: https://discourse.processing.org/t/shape-generator-help-armin-hofmanns-rubber-band-shape-generator/33190
Every time I try to make a matrix or array of x and y values stored and to be used in the for loop within the draw/setup functions, it becomes invalid and it doesn't show the shapes I would like.
I know I'm missing info.
This is my latest trial:
class boundingBox {
createBox() {
stroke(0);
strokeWeight(3);
fill(255, 255, 255, 255);
rect(100, 100, 700, 700);
}
}
function Bubble(x, y, x1, y1, x2, y2, x3, y3) {
// creating a distinction from x/y private and x/y public
this.x = x;
this.y = y;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.x3 = x3;
this.y3 = y3;
//this.x = x = [225,350,475,600];
//this.y = y = [225,350,475,600];
// creating color public for later use
this.col = color(255);
// this expression works to make a public function within a private function
this.display = function() {
stroke(0);
fill(this.col);
// creating the ability to display the circles within the parameters set
//ellipse(this.x, this.y, 48,48);
// in theory this should loop through the x array position values against all the y values in it's array creating multiple circles
for (i = 0; i < 4; i++) {
ellipse(this.x /*[i]*/ , this.y, 100, 100);
ellipse(this.x1, this.y1, 100, 100);
ellipse(this.x2, this.y2, 100, 100);
ellipse(this.x3, this.y3, 100, 100);
}
}
//this.move = function() {
// //making the circles change position and act like they are moving within air
// this.x = this.x + random(-0.5, 0.5);
// this.y = this.y + random(-0.5, 0.5);
// this.x1 = this.x1 + random(-0.5, 0.5);
// this.y1 = this.y1 + random(-0.5, 0.5);
// //this.x = this.x + random(1, 0.5);
// //this.y = this.y + random(1, 0.5);
//}
// again expression to make function but this time implementing a clicking function
this.clicked = function() {
// calculating the distance from the circle center to help fill it with color and nothing else
var d = dist(mouseX, mouseY, this.x, this.y)
// diameter of the circle
if (d < 50) {
this.col = color(0, 0, 0)
}
}
//joinBubbles(bubbles); {
// bubbles.forEach(element => {
// let dis = dist(this.x, this.y, element.x, element.y);
// stroke(200, 200, 200, 127);
// line(this.x, this.y, element.x, element.y);
// });
//}
//}
//class lines {
// constructor(){
// this.x4 = x4;
// this.y4 = y4;
// this.x5 = x5;
// this.y5 = y5;
// }
// this.displayLine = function(){
// stroke(0)
// }
}
let bubbles = [];
let boxii = [];
function setup() {
createCanvas(1536, 1250);
for (let i = 0; i < 1; i++) {
boxii.push(new boundingBox());
}
for (let i = 200; i < 860; i += 165) {
//var x = random (width);
var x = 200;
var y = i;
bubbles.push(new Bubble(x, y /*x1,y1*/ ));
}
for (let i = 200; i < 860; i += 165) {
//var x = random (width);
var x1 = 375;
var y1 = i;
bubbles.push(new Bubble(x1, y1 /*x1,y1*/ ));
}
for (let i = 200; i < 860; i += 165) {
//var x = random (width);
var x2 = 550;
var y2 = i;
bubbles.push(new Bubble(x2, y2 /*x1,y1*/ ));
}
for (let i = 200; i < 860; i += 165) {
//var x = random (width);
var x3 = 700;
var y3 = i;
bubbles.push(new Bubble(x3, y3 /*x1,y1*/ ));
}
//for (let i = 0; i < 1; i++) {
//var x1 = 400;
//var y1 = 200;
//bubbles.push(new Bubble(x1,y1));
//}
//for (let i = 200; i < 1000; i+=200){
//var x2 = 200;
//var y2 = i;
//bubbles.push(new Bubble(x2,y2));
//}
}
function mousePressed() {
// checking where the mouse presses so that clicking outside the circle does nothing
for (let i = 0; i < bubbles.length; i++) {
//for (let i = 0; i < 4; i++) {
// using this function on all the circles and again using .this to bring properties from /bubble
bubbles[i].clicked();
}
}
function draw() {
background(140);
for (let i = 0; i < 1; i++) {
boxii[i].createBox();
}
for (let i = 0; i < bubbles.length; i++) {
//bubbles[i].move();
bubbles[i].display();
}
if (mousePressed) {
for (let i = 0; i < bubbles.length; i++) {
bubbles[i].display(fill(0))
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>

How to optimize this simple pathfinding

//Editable Vars
let cols = 35;
let rows = 35;
let fps = 5;
//Declarations
let canvas;
let ctx;
let background;
let grid = new Array(cols);
let w;
let h;
let pathfinder;
let target;
let timer;
let renderQueue = [];
let canPathfind = true;
//Space Class
class Space{
constructor(x,y,c='lightgrey'){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.c = c;
}
draw(){
ctx.fillStyle = this.c;
ctx.strokeStyle = 'black';
ctx.fillRect(this.x * w, this.y * h, this.w, this.h);
ctx.rect(this.x * w, this.y * h, this.w, this.h);
ctx.stroke();
}
move(x,y){
if(x < 0 || x > cols-1 || y < 0|| y > rows-1){
return;
}
grid[this.x][this.y] = new Space(this.x,this.y);
renderQueue.push(grid[this.x][this.y]);
this.x = x;
this.y = y;
grid[this.x][this.y] = this;
renderQueue.push(grid[this.x][this.y]);
}
}
//Game-Run code
function gameStart(){
canvas = document.getElementById('gameCanvas');
ctx = canvas.getContext('2d');
w = canvas.width / cols;
h = canvas.height / rows;
createGrid();
pathfinder = new Space(randomInt(cols-1),randomInt(rows-1),'green');
grid[pathfinder.x][pathfinder.y] = pathfinder;
target = new Space(randomInt(cols-1),randomInt(rows-1),'red');
grid[target.x][target.y] = target;
drawGrid();
timer = setInterval(updateScreen, 1000/fps);
}
function restartGame(){
clearInterval(timer);
gameStart();
}
//Starts loading process on windows load
window.onload = gameStart();
//Checks all 8 possible move directions and calls pathfinder.move for best move
function pathfind(pathfinder, target){
if(!canPathfind) return;
let p = {x: pathfinder.x, y: pathfinder.y};
let t = {x: target.x, y: target.y};
let move = {x : 0, y : 0};
// 0,0 9,9
//if(p.x == t.x && p.y == t.y){
//restartGame();
//}
if(t.x - p.x >= 1){
move.x = 1;
}else if(t.x - p.x <= -1){
move.x = -1;
}else{
move.x = 0;
}
if(t.y - p.y >= 1){
move.y = 1;
}else if(t.y - p.y <= -1){
move.y = -1;
}else{
move.y = 0;
}
pathfinder.move(pathfinder.x + move.x, pathfinder.y + move.y);
}
function updateScreen(){
pathfind(pathfinder,target);
drawUpdatedSpaces();
}
function drawUpdatedSpaces(){
for(let i = 0; i < renderQueue.length; i++){
renderQueue[i].draw();
}
renderQueue = [];
}
function drawGrid(){
for(let i = 0; i < grid.length; i++){
for(let j = 0; j < grid[i].length; j++){
grid[i][j].draw();
}
}
}
//Creates grid and instantiates Space in every cell
function createGrid(){
for(let i = 0; i < grid.length; i++){
grid[i] = new Array(rows);
}
for(let i = 0; i < grid.length; i++){
for(let j = 0; j < grid[i].length; j++){
grid[i][j] = new Space(i,j);
}
}
}
// Returns distance to target from specified coords
function distanceFromTarget(x, y) {
return (Math.sqrt(Math.pow(Math.abs(x - target.x), 2) + (Math.pow(Math.abs(y - target.y), 2))));
}
// Returns random Integer between 0 and Max
function randomInt(max) {
return Math.floor(Math.random() * max);
}
It runs as expected which is great, but performance is super slow. That may be because I'm using jsfiddle to work on this while away from my personal PC setup, but is there a way to make this perform better? As of right now I can't move the grid size to >50 without it running extremely slow. I would love to eventually move to a 4k x 4k grid and create an auto-generating maze for the 'pathfinder' to pathfind through.
Thoughts/things I'm considering for performance:
Using HTML grid and updating via CSS instead of HTML5 Canvas
Only re-drawing cells that have changed (Implemented in the Space.move() function with array renderQueue)
literally re-doing everything in python :D

Multiple intersecting squares

This is in continuation to my previous post here Changing color of intersecting area of squares
I was able to make the intersecting area of the squares change color. The issue was that I wanted multiple intersecting squares and not a single one so I modified the code. Now the problem is that the square is leaving a trail since it is drawing square wherever it moves. And it starts to slow down after a while. How can I overcome this
function draw() {
background(135,206,250);
myColour = (255);
// if a square is being dragged, update its position
if (this.dragObject != null) {
this.dragObject.position.x = mouseX;
this.dragObject.position.y = mouseY;
}
//draw all squares
for (let i = 0; i < squares.length; i++) {
let s = squares[i];
s.show();
}
for (let i = 0; i < squares.length; i++) {
for (let j = i + 1; j < squares.length; j++) {
//block checking collision
if (i != j && squares[i].collides(squares[j])) {
squares[i].changecolor();
//set intersection color
fill(myColour);
//calculate parameters
newX = Math.max(squares[i].position.x, squares[j].position.x);
newY = Math.max(squares[i].position.y, squares[j].position.y);
newW = Math.min(squares[i].position.x + squares[i].w, squares[j].position.x + squares[j].w) - newX;
newH = Math.min(squares[i].position.y + squares[i].h, squares[j].position.y + squares[j].h) - newY;
//draw rectangle
let Osquare = new OverlappingSquares(newX, newY, newW, newH);
overlappingsquares.push(Osquare);
}
}
}
}
changecolor() {
// fill(random(255), random(255), random(255));
// background(200, 255, 100);
for (let i = 0; i < squares.length; i++) {
let s = squares[i];
s.show();
}
for (let i = 0; i < overlappingsquares.length; i++) {
let s = overlappingsquares[i];
s.show();
}
}
//Overlapping sqaures class
class OverlappingSquares {
constructor(X, Y, W, H) {
this.w = W;
this.h = H;
this.position = {
x: X,
y: Y
};
}
show() {
fill(myColour)
rect(this.position.x, this.position.y, this.w, this.h);
}
}

Making functions with Crafty.js

I am working on a top down video game with the crafty.js game library and I keep having to repeat a piece of code to make borders and the background sprite, So i want to put it into its own seperate function and call it, the problem is whenever I do this with traditional javascript syntax it causes an error, and i cant work out how else to do it: heres my scenes code:
Crafty.scene('World', function() {
//Draw Floor
for (var i = 0; i < 32; i++)
{
for (var y = 0; y < 24; y++)
{
Crafty.e('GrateFloor').at(i, y);
}
}
//Draw borders, gotta find a more code efficient way
for (var i = 0; i < 32; i++)
{
Crafty.e('Border').at(i, 0);
}
for (var y = 0; y < 24; y++)
{
Crafty.e('Border').at(0, y);
}
for (var y = 0; y < 24; y++)
{
Crafty.e('Border').at(31, y);
}
for (var y = 0; y < 32; y++)
{
Crafty.e('Border').at(y, 23);
}
//draw in game entities such as the player and obstacles
//drawing walls here
//horizontal walls
for (var y = 10; y <29; y++)
{
Crafty.e('WallHor').at(y, 20);
}
for (var y = 10; y <29; y++)
{
Crafty.e('WallHor').at(y, 5);
}
//vertical walls
for (var i = 6; i <20; i++)
{
Crafty.e('WallVert').at(29, i);
}
for (var i = 6; i <12; i++)
{
Crafty.e('WallVert').at(9, i);
}
for (var i = 14; i <20; i++)
{
Crafty.e('WallVert').at(9, i);
}
//single wall points
Crafty.e('WallTpRht').at(29,5);
Crafty.e('WallBtmRht').at(29,20);
Crafty.e('WallTpLft').at(9,5);
Crafty.e('WallBtmLft').at(9,20);
//everything else
Crafty.e('Enemy').at(20, 10);
Crafty.e('Teleporter1').at(1, 11);
Crafty.e('Player').at(15,15);
});
Crafty.scene('Loading', function() {
//Load in Visual Assets
Crafty.e('2D, DOM, Text')
.attr({ x: 15, y: 15 })
.text('Loading...');
Crafty.load(['assets/White_Male_Student_Animation_Bitmap.gif', 'assets/Walls_Bitmap.gif'], function(){
//Define terrain
Crafty.sprite(24, 24, 'assets/Walls_Bitmap.gif' , {
spr_WallWireHor: [0,0],
spr_Border: [0,1],
spr_WallWireVert: [1,1],
spr_WallWireTopRight: [1,0],
spr_WallWireBottomRight: [1,2],
spr_RobotSkull: [0,2],
spr_WallWireTopLeft: [2,0],
spr_WallWireBottomLeft: [2,1],
spr_Teleporter: [3,0],
spr_GrateFloor: [3,1],
})
//Define player
Crafty.sprite(25, 36.75, 'assets/White_Male_Student_Animation_Bitmap.gif' , {
spr_player: [0,0],
spr_BattlePlayer: [0,1],
},0, 0)
Crafty.scene('World')
})
});
//Screen for all combat to happen upon
Crafty.scene('BattleScreen', function() {
Crafty.e('2D, DOM, Text')
.attr({ x: 24, y: 22 })
.text('Monster destroyed!');
//Draw borders, gotta find a more code efficient way
for (var i = 0; i < 32; i++)
{
Crafty.e('Border').at(i, 0);
}
for (var y = 0; y < 24; y++)
{
Crafty.e('Border').at(0, y);
}
for (var y = 0; y < 24; y++)
{
Crafty.e('Border').at(31, y);
}
for (var y = 0; y < 32; y++)
{
Crafty.e('Border').at(y, 23);
}
//draws Player sprite for the combat screen
Crafty.e('BattlePlayer').at(16,20);
});
Crafty.scene('HubWorld', function() {
//Draw Floor
for (var i = 0; i < 32; i++)
{
for (var y = 0; y < 24; y++)
{
Crafty.e('GrateFloor').at(i, y);
}
}
//Draw borders, gotta find a more code efficient way
for (var i = 0; i < 32; i++)
{
Crafty.e('Border').at(i, 0);
}
for (var y = 0; y < 24; y++)
{
Crafty.e('Border').at(0, y);
}
for (var y = 0; y < 24; y++)
{
Crafty.e('Border').at(31, y);
}
for (var y = 0; y < 32; y++)
{
Crafty.e('Border').at(y, 23);
}
//draw other stuff
Crafty.e('Player').at(28,11);
Crafty.e('Teleporter2').at(30,11);
});
I want to move the Draw floor and Draw boders bits into there own subroutine, and I also need to know how to pass variables to and from functions with crafty.js
Crafty is a framework that uses the component design pattern. (http://gameprogrammingpatterns.com/component.html)
A component is exactly what you are asking for, a reusable piece of code.
So you should create a component which draws the floor and another for the border.
But first you should think again about the way you are drawing. Entities should not be used like that, except if you explicitely need that many entities. An entity is basically an ID with a list of components. The components should do the hard work, not the entities.
Take a look at the TextShadow component below, maybe it helps you to understand the idea:
var TextShadow = {
init: function init () {
this.requires('DOM');
this._textShadowColor = '#FFFFFF';
this._apply();
},
_apply: function _apply () {
this.css('text-shadow', '1px 1px 2px ' + this._textShadowColor);
},
textShadowColor: function textShadowColor (color) {
this._textShadowColor = color;
this._apply();
return this;
}
};
Crafty.c('TextShadow', TextShadow);

Ball Bounce - javascript

function randomXToY(minVal,maxVal,floatVal)
{
var randVal = minVal+(Math.random()*(maxVal-minVal));
return typeof floatVal=='undefined'?Math.round(randVal):randVal.toFixed(floatVal);
}
Ball = (function() {
// constructor
function Ball(x,y,radius,color){
this.center = {x:x, y:y};
this.radius = radius;
this.color = color;
this.dx = 2;
this.dy = 2;
this.boundaryHeight = $('#ground').height();
this.boundaryWidth = $('#ground').width();
this.dom = $('<p class="circle"></p>').appendTo('#ground');
// the rectange div a circle
this.dom.width(radius*2);
this.dom.height(radius*2);
this.dom.css({'border-radius':radius,background:color});
this.placeAtCenter(x,y);
}
// Place the ball at center x, y
Ball.prototype.placeAtCenter = function(x,y){
this.dom.css({top: Math.round(y- this.radius), left: Math.round(x - this.radius)});
this.center.x = Math.round(x);
this.center.y = Math.round(y);
};
Ball.prototype.setColor = function(color) {
if(color) {
this.dom.css('background',color);
} else {
this.dom.css('background',this.color);
}
};
// move and bounce the ball
Ball.prototype.move = function(){
var diameter = this.radius * 2;
var radius = this.radius;
if (this.center.x - radius < 0 || this.center.x + radius > this.boundaryWidth ) {
this.dx = -this.dx;
}
if (this.center.y - radius < 0 || this.center.y + radius > this.boundaryHeight ) {
this.dy = -this.dy;
}
this.placeAtCenter(this.center.x + this.dx ,this.center.y +this.dy);
};
return Ball;
})();
var number_of_balls = 5;
var balls = [];
$('document').ready(function(){
for (i = 0; i < number_of_balls; i++) {
var boundaryHeight = $('#ground').height();
var boundaryWidth = $('#ground').width();
var y = randomXToY(30,boundaryHeight - 50);
var x = randomXToY(30,boundaryWidth - 50);
var radius = randomXToY(15,30);
balls.push(new Ball(x,y,radius, '#'+Math.floor(Math.random()*16777215).toString(16)));
}
loop();
});
loop = function(){
for (var i = 0; i < balls.length; i++){
balls[i].move();
}
setTimeout(loop, 8);
};
I have never used in oops concepts in javascript. How do I change the ball color when the balls touches each other?
This is the link : http://jsbin.com/imofat/1/edit
You currently don't have any interaction with the balls. What you can do is checking whether two balls are "inside" each other, and change colors in that case: http://jsbin.com/imofat/1491/.
// calculates distance between two balls
var d = function(a, b) {
var dx = b.center.x - a.center.x;
var dy = b.center.y - a.center.y;
return Math.sqrt(dx*dx + dy*dy);
};
and:
// for each ball
for(var i = 0; i < balls.length; i++) {
// check against rest of balls
for(var j = i + 1; j < balls.length; j++) {
var a = balls[i];
var b = balls[j];
// distance is smaller than their radii, so they are inside each other
if(d(a, b) < a.radius + b.radius) {
// set to some other color using your random color code
a.setColor('#'+Math.floor(Math.random()*16777215).toString(16));
b.setColor('#'+Math.floor(Math.random()*16777215).toString(16));
}
}
}
Still, there are things for improvement:
Balls are changing colors as long as they are inside each other, not just once.
If you want them to "touch", you might want to implement some kind of bouncing effect to make it more realistic.

Categories