I am totally new to Processing and I am trying to run a program that draws a circle if the value of a variable is less than 20 and an ellipse if the value is greater than 20. The problem is that the screen is not getting updated if I try to use the value of an array. It just take one value and it does not change. If I just create a random number then it works. I also see that when the screen does not update the values in the terminal do show the correct numbers, so the program runs without an apparent error. (All those println's are just for debugging purposes...)
void draw () {
delay(500);
for (int i = 0; i < totalData.size(); i++) {
record2 = totalData.getJSONObject(i);
distance2 = int(record2.getString("field1"));
//float distance2 = random(15, 25);
println("distance2 is " + distance2);
if (distance2 <= 20) {
println("triangle");
background(500);
triangle(30, 75, 58, 20, 86, 75);
println("distance2 at if " + distance2);
} else{
background(1700);
println("ellipse");
ellipse(56, 46, 55, 55);
println("distance at else is " + distance2);
}
println("distance2 at end " + distance2);
}
}
#John Coleman comment is on spot, but I'll try to give more details so that you understand the problem properly:
First check the draw() function documentation. It says:
Called directly after setup(), the draw() function continuously executes the lines of code contained inside its block until the program is stopped or noLoop() is called. draw() is called automatically and should never be called explicitly. All Processing programs update the screen at the end of draw(), never earlier.
So you draw() function is already a loop, what you want to do is to create the i variable outside of the function and increment it each time draw() is called:
int i = 0;
void draw () {
record2 = totalData.getJSONObject(i);
i++;
distance2 = int(record2.getString("field1"));
println("distance2 is " + distance2);
if (distance2 <= 20) {
println("triangle");
background(500);
triangle(30, 75, 58, 20, 86, 75);
println("distance2 at if " + distance2);
} else{
background(1700);
println("ellipse");
ellipse(56, 46, 55, 55);
println("distance at else is " + distance2);
}
println("distance2 at end " + distance2);
}
This should give you the expected behavior.
The doc also says:
The number of times draw() executes in each second may be controlled with the frameRate() function.
You most probably want to use that instead of delay() to control your frame rate.
If you want to get started with processing, reading the doc is always a good start and I would also recommend checking Daniel Shiffman youtube channel this is probably the most complete and didactic resource about processing you'll find online.
Related
So I want to have a delay in my Draw function so that when I want to do
Xoffset = Xoffset + 1.5
I do not go from 0 to 30 in a secnd, but I want there to be some delay in the code so that I can easily manage it. I usually use scratch and if you are unfimiliar to that the command for a code delay is
Wait(Insert amount of seconds you want to delay here)
So when you start a part of the code, and the Wait is set to 2, the input will go off, wait 2 seconds, and then move on the the line of code below. I am trying to replicate that but in p5.js.
To detail my comment above, you could do something like this:
var xOffset = 0;
// time delay vars
// current time "snapshot" (imagine pressing the lap time button)
var time;
// the interval to wait between time "snapshots": 2s (2000 milliseconds) in this case
var wait = 2000;
function setup() {
createCanvas(300, 300);
//store the current time
time = millis();
}
function draw() {
background(220);
//check if the difference between now and the previously stored time
// is greater than the wait interval
if(millis() - time >= wait){
console.log(wait, "ms passed");
//if it is, do something
xOffset = xOffset + 1.5;
//also update the stored time
time = millis();
}
circle(xOffset, 150, 60);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
I'm not sure what the main purpose of the delay is:
is it to slow things down for debugging purposes
is to go into a series a states (e.g. ball moves up for 2s, then right
2s, etc.),
is to animate/smoothly interpolate between two positions within a given
time ?
Maybe something else completely ?
If you simply want to slow things down, perhaps it might be simpler to adjust the frameRate():
var xOffset = 0;
function setup() {
createCanvas(300, 300);
// update 1 frame every two seconds => 0.5 fps
frameRate(0.5);
}
function draw() {
background(220);
xOffset = xOffset + 1.5;
circle(xOffset, 150, 60);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Processing still has a delay() function, but p5.js doesn't.
Personally I'm not a fan of blocking behaviour like this and prefer the millis() options even though it's more verbose. That being said, if the program/demo you write is super simple delay() might just be enough.
Even if delay() is missing you could use js setTimeout() with a Promise, but that's getting into more advanced JS as Paul already mentioned:
var xOffset = 0;
function setup() {
createCanvas(300, 300);
// prevent p5's default draw() updates
noLoop();
}
function draw() {
background(220);
xOffset = xOffset + 1.5;
circle(xOffset, 150, 60);
// wait 2s then call draw() manually
delay(2000).then(draw);
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Now, if you want to create a smooth animation from a start x offset to an end offset in a set amount of seconds you'd need a different approach.
You can find a p5.js example at the of this answer:
var startTime;
var duration = 2000;
var startValue = 0;
var endValue = 300;
var currentValue = startValue;
function setup(){
createCanvas(300, 300);
textAlign(RIGHT);
startTime = millis();
}
function draw(){
background(255);
moveCircle();
drawCircle();
}
function drawCircle() {
circle(currentValue, 150, 60);
}
function moveCircle(){
var progress = (float)(millis()-startTime)/duration;//millis()-startTime = difference in time from start until now
if(progress < 1.0) currentValue = startValue + (endValue * progress);//the current value is the final value scaled/multiplied by the ratio between the current duration of the update and the total duration
}
function mousePressed(){//reset value and time
currentValue = startValue;
startTime = millis();
}
function keyPressed(){//update duration
if(key == '-') if(duration > 0) duration -= 100;
if(key == '=' || key == '+') duration += 100;
console.log("duration: " + duration);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
And if you're comfortable using external libraries you could simply use a tweening library such as gsap:
var x = 0;
function setup(){
createCanvas(300, 300);
// animate "this" global object's x property to 300 in 2 seconds
gsap.to(this, {x: 300, duration: 2});
}
function draw(){
background(255);
circle(x, 150, 60);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/gsap.min.js"></script>
Notice the animation has a bit of easing (default ease out) which could be nice.
In JavaScript, unlike Scratch, there is not a good way to delay synchronous* code. The reason for this is that all the functionality of a webpage is run using a single thread. Which means that while there can be multiple sequences of instructions performing different pieces of functionality, only one of those sequences can execute at a time. Therefore if you did something to delay the execution of JavaScript within the draw() function, it would block all other functionality, making the browser tab unresponsive (and potentially causing the browser to halt your JavaScript altogether).
Instead of pausing a series of JavaScript statements like one would do in Scratch, it is necessary to either use the amount of time that has elapsed as part of a condition that determines whether some code should run (which is what the example George shared in the comments does), or to use the built in setTimeout() function to schedule some code to run some number of milliseconds in the future (but you would not want to call setTimeout() from draw() because it would repeatedly schedule the code every frame).
* note: there are also ways to create delays using asynchronous javascript code, but this is a more advanced topic
I intend to have an ellipse move smoothly between the stop points while keep rotating. It'll rotate for 1sec at point (20,50), transition to (40,70) on a smooth curve (bezier or random polynomial) and rotate till 3sec and move on to (160,190)
The current issue is that it jumps between stop points rather than move smoothly.
var angle=0;
var x=[20,40,160] // x coordinates for stop points
var y=[50,70,190] // y coordinates for stop points
var t=[1000,2000,4000] // time for stop points
var i=0;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
frameRate(30);
translate(x[i],y[i]);
rotate(angle);
if (millis() >= t[i] & millis() < t[i+1]){
i+=1
}
fill(0);
ellipse(0,0, 20, 80);
angle++
}
There are a lot of different ways to do this, and which approach you take depends on exactly how you want your code to behave.
Similar to the answer to your last question, you need to show the movement between the points.
Right now you're moving directly from one point to another. You need to show the intermediate steps.
Here are a few things to look into:
The frameCount variable holds the current frame number. This is useful for exact timing of behaviors that you want to trigger on specific frames.
The millis() function returns the number of milliseconds the sketch has been running. This is useful for duration-based logic, where you want to do something after a certain number of seconds.
The lerp() function allows you to calculate values that change over time.
You could also use delta values, where you move the X and Y values by some amount each frame.
Here's an example of that last approach:
var circleX = 20;
var circleY = 20;
var mode = 'move right';
function setup() {
createCanvas(400, 400);
}
function draw() {
background(200);
ellipse(circleX, circleY, 20, 20);
if (mode == 'move right') {
circleX++;
if (dist(circleX, circleY, 380, 20) < 1) {
mode = 'move down';
}
} else if (mode == 'move down') {
circleY++;
if (dist(circleX, circleY, 380, 380) < 1) {
mode = 'move left';
}
} else if (mode == 'move left') {
circleX--;
if (dist(circleX, circleY, 20, 380) < 1) {
mode = 'move up';
}
} else if (mode == 'move up') {
circleY--;
if (dist(circleX, circleY, 20, 20) < 1) {
mode = 'move right';
}
}
}
But please note that this code is just an example, and there's a ton of room for improvement here. The important thing is that you need to move the scene a little bit each frame, instead of going directly from one point to another.
float speed = 1;
void setup() {
size(400, 300);
}
void draw() {
background(255);
move();
display();
}
void move() {
x = x + speed;
if (x > 350) {
speed = 0;
}
}
void display(x,y) {
fill(#FF2121);
translate(x,y);
ellipse(0, 0, 60, 60);
rect(-10, 15, 20, 100);
}
Unexpected token: x on "Void display (x,y)"
Basically this program moves the ellipse and rect to other side of the window. is this the right way to do it? or is there any other easy way.
Example
0 = ellipse
[] = rect
move to other side of window (speed of 1) and when it hit the edge, both them stop.
Parameters need types, just like variables do.
void display(float x, float y) {
Also note that since your display() function takes 2 parameters, it's illegal to call it without any parameters, which is what you're doing in your draw() function.
Also note that you've never defined the x variable, so that's another error.
Please get into the habit of working in smaller chunks instead of trying to write your whole program all at one time. You've got quite a few errors here, and it's going to be hard to fix one without fixing the others. I recommend starting over with something simpler, and only moving forward when you have something that works.
I'm attempting to create a very simple "whack-a-mole" type game, designed for students new to p5.js, and Processing in general.
Currently, I've created an array, and a random search within that array that randomly picks a square and makes it brown (a "mole", for now).
How do I make it so that after selecting a square, it stays there for a couple seconds, and then jumps to the next one, using basic p5?
I've managed to implement noLoop(), which can stop the search, but after a certain time I want it to resume.
Here's the code I have so far:
function setup() {
createCanvas(610,610)
}
function draw() {
var grid = []
for (var x = 0; x < 6; x += 1){
grid[x]=0;
for (var y = 0; y < 6; y += 1){
rand=round(random(360))
grid[x][y]=0
if (rand==0){
grid[x]=1
grid[y]=1
noLoop()
}
if (grid[x]==0 || grid[y]==0){
fill(76,153,0)
rect((x*100+10),(y*100+10),90,90)
}
if (grid[x]>0 && grid[y]>0){
fill(102,51,0)
rect((x*100+10),(y*100+10),90,90)
}
}
}
}
Instead of using noLoop(), you could keep looping at 60 frames per second, but then use the millis() function to keep track of the elapsed time.
Here's an example that shows a circle for 1 second whenever the user clicks:
var clickTime;
function mousePressed(){
clickTime = millis();
}
function draw() {
background(0);
if(millis() < clickTime + 1000){
ellipse(width/2, height/2, width/4, height/4);
}
}
Edit: Another approach is to use the % operator along with the frameCount variable in order to do something every X frames. This examples draws a circle in a random position every 60 frames:
function draw() {
if (frameCount % 60 == 0) {
background(0);
ellipse(random(width), random(height), width / 4, height / 4);
}
}
I've got some code that displays 10 ellipses in random locations on the screen, and a square that descends from the top of the screen to the bottom, at which point it resets back at the top. What I'm trying to do is get a counter to increment when that square passes any of the ellipses (by comparing their y-positions). However, the counter increases rapidly instead of steadily and just doesn't behave desirably in general.
Here's my draw() function. barriers[i][0] stores the x-pos, barriers[i][1] the y-pos obviously.
void draw()
{
background(255);
fill(0);
for(int i = 0; i < barriers.length; i++) {
// Draw barriers
ellipse(barriers[i][0], barriers[i][1], 50, 50);
// Did we pass a barrier? (doesn't work!)
if(y >= barriers[i][0] - 1 && y <= barriers[i][1] + 1) {
counter++;
}
}
// Draw the square
rect(x, y, 25, 25);
// Draw counter alongside square
fill(255, 0, 0);
text(counter, x + 25, y - 5);
// Reset
if(y < height) {
y+=5;
} else {
y = -25;
counter = 0;
}
}
Apologies if the solution is blindingly obvious, but I'm just not seeing the problem here...
Looking forward to some assistance.
Look at this section of code:
if(y >= barriers[i][0] - 1 && y <= barriers[i][1] + 1) {
counter++;
}
The draw() function fires 60 times a second, so this code will be fired 60 times per second. That means that while you're passing a barrier, the counter variable will increment 60 times per second!
Presumably you only want the counter to increase once for each barrier. There are a number of ways to do this. You could have another data structure that keeps track of whether each barrier has already been passed, and then only check barriers that haven't been passed yet. Or you could keep track of the previous positions of the square, and then use that to determine when the square starts passing a barrier.
Think about how you would do this in your head, without a computer. How do you know when the square is passing a circle? How do you, in your head, only count one for each barrier?
Following Kevin's advice, I was able to get it working using an array of booleans which I used to ensure I wasn't incrementing counter more than once:
Full code:
float barriers[][] = new float[10][2];
float x = 400;
float y = -25;
int counter = 0;
boolean barriersChecked[] = {false, false, false, false, false, false, false, false, false, false};
void setup()
{
size(800, 600);
genBarriers();
}
void genBarriers()
{
for (int i = 0; i < barriers.length; i++) {
barriers[i][0] = random(width);
barriers[i][1] = random(height);
}
}
void draw()
{
background(255);
fill(0);
for (int i = 0; i < barriers.length; i++) {
// Draw barriers
ellipse(barriers[i][0], barriers[i][1], 50, 50);
// Did we pass a barrier?
if (barriers[i][1] < y && !barriersChecked[i]) {
counter++;
barriersChecked[i] = true;
}
}
// Draw the square
rect(x, y, 25, 25);
// Draw counter alongside square
fill(255, 0, 0);
text(counter, x + 25, y - 5);
// Reset
if (y < height) {
y+=2;
} else {
y = -25;
genBarriers();
// Reset barriersChecked
for(int i = 0; i < barriersChecked.length; i++) {
barriersChecked[i] = false;
}
}
}
Out of curiosity, is there a more elegant (loopless) way of resetting every element in barriersChecked back to false?
Suggestions of additional improvements would also be greatly appreciated.