Clearing out canvas - processing

I'm trying to program an intro. I want the canvas to erase itself after it. I already have the trigger, but I do not know how to clear the canvas. Would just changing the background work? I still want to make stuff after it.
Here is the code:
void setup () {
frameRate(10);
stroke(255, 255, 255);
noFill();
rect(100,155,300,300);
size(500, 500);
}
void square () {
for (int x = 100; x <= 300; x += 100) {
for (int y = 155; y <= 355; y += 100) {
fill(random(0, 255), random(0, 255), random(0, 255));
rect(x, y,100,100);
}
}
};
void draw () {
int time = 0;
int logoLength = 100;
if (time < logoLength) {
fill(255, 255, 255);
background(0, 0, 0);
textFont(createFont("Lucida console", 19));
textAlign(CENTER,CENTER);
text("Ghost Cube Games presents",250,59);
time++;
print(time);
square();
} else if (time == logoLength) {
background(255, 255, 255);
}
}

You can simply call the background() function.
background(0); draws a black background.
background(255); draws a white background.
background(255, 0, 0); draws a red background.
More info can be found in the reference.
For a more specific example, if you want to show an intro screen, you can simply keep track of whether the intro screen is showing in a boolean variable. If that variable is true, then draw the intro screen. If not, then draw whatever else you want to draw. If you do this from the draw() function, then you don't really have to worry about clearing the screen, since calling the background() function will do that for you:
boolean showingIntro = true;
void draw() {
background(0);
if (showingIntro) {
text("INTRO", 20, 20);
} else {
ellipse(50, 50, 25, 25);
}
}
void mouseClicked() {
showingIntro = false;
}

Related

Hide and show background image when keyboard pressed, preserving overlay elements

I would like to paint ellipses over an image when clicking the mouse, and when I press the keyboard, hide and show the underneath image alternatively, without cleaning the ellipses.
I'm using createGraphics() to store the image data, and remove() so when the keyboard is pressed I spect the image disappear but it doesn't work
Here is a sketch of what I trying to do:
let isMouseBeeingPressed = false;
let img;
let isShow = true
let bufferImg;
function preload() {
img = loadImage(
"https://images.pexels.com/photos/20787/pexels-photo.jpg?auto=compress&cs=tinysrgb&dpr=1&w=500"
);
}
function setup() {
createCanvas(500, 500);
background(255);
loadImageBuffer();
}
function loadImageBuffer() {
bufferImg = createGraphics(400, 400);
bufferImg.image(img, 0, 0);
image(bufferImg, 0, 0);
}
function draw() {
if(isMouseBeeingPressed) {
stroke(0, 0, 0, 50);
fill(255);
ellipse(mouseX, mouseY, 20);
}
}
function keyPressed() {
if(isShow) {
bufferImg.remove();
} else {
image(bufferImg, 0, 0);
}
console.log('isShow:', isShow);
return isShow = !isShow;
}
function mousePressed() {
isMouseBeeingPressed = true;
}
function mouseReleased() {
isMouseBeeingPressed = false;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Any idea of how to achieve this?
The best approach is probably to draw the ellipses to a separate buffer (p5.Graphics), and then draw that on top of the image or blank background as needed. An alternative approach would be to record the position of each ellipse in an array so that they can be redrawn. However, the latter approach will use more memory and switching the image on and off will have a noticeable delay after many ellipses have been drawn.
Approach #1 (render via p5.Graphics)
let img;
let graphics;
let isShow = true;
function preload() {
img = loadImage(
"https://images.pexels.com/photos/20787/pexels-photo.jpg?auto=compress&cs=tinysrgb&dpr=1&w=500"
);
}
function setup() {
createCanvas(500, 500);
background(255);
image(img, 0, 0);
graphics = createGraphics(width, height);
}
function draw() {
if (mouseIsPressed) {
graphics.stroke(0, 0, 0, 50);
graphics.fill(255);
graphics.ellipse(mouseX, mouseY, 20);
background(255);
if (isShow) {
image(img, 0, 0);
}
image(graphics, 0, 0);
}
}
function keyPressed(e) {
isShow = !isShow;
background(255);
if (isShow) {
image(img, 0, 0);
}
image(graphics, 0, 0);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Update: one thing to note about this approach is that whenever the mouse is pressed, the entire scene must be redrawn (white background, image if applicable, and foreground). The reason for this is antialiasing and transparency. When you draw an ellipse on the graphics buffer the edges will have some partially transparent pixels due to antialiasing. If you repeatedly draw the buffer as an overlay without redrawing what is behind it then the partially transparent pixels will become less and less transparent until they are sold black. This will cause your ellipses to have a slightly thicker and more pixelated outer edge.
Approach #2 (Array of ellipse positions)
let img;
let isShow = true
let positions = [];
function preload() {
img = loadImage(
"https://images.pexels.com/photos/20787/pexels-photo.jpg?auto=compress&cs=tinysrgb&dpr=1&w=500"
);
}
function setup() {
createCanvas(500, 500);
background(255);
image(img, 0, 0);
}
function draw() {
if (mouseIsPressed) {
stroke(0, 0, 0, 50);
fill(255);
ellipse(mouseX, mouseY, 20);
positions.push([mouseX, mouseY]);
}
}
function keyPressed() {
isShow = !isShow;
background(255);
if (isShow) {
image(img, 0, 0);
}
stroke(0, 0, 0, 50);
fill(255);
for (const [x, y] of positions) {
ellipse(x, y, 20);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>

How to use custom shapes in a repeating loop pattern?

I already made a custom shape (myFunction), and I also made patterns using simpler shapes. I want to know how to replace those simple shapes with my custom shape while maintaining the pattern drawn on processing...
You're already calling functions such as noFill(), noStroke(), etc.
It's the same for your function: call it by simply using it's name and () (because it has no arguments): myFunction();
Let's say you want to draw it in pattern 1, you could do something like:
if (pattern==1) {
for (int x=50; x<width; x+=100) {
for (int y=20; y<height; y+=100) {
myFunction();
}
}
}
You will need to pay attention to rendering though.
Running the above will not display anything you call in noFill() in myFunction() and also noStroke() in draw(), right after background(): you won't be able to see a shape with no fill and no stroke :)
One suggestion is to add a stroke:
void myFunction() {
noFill();
stroke(255);
ellipse(300, 300, 200, 400);
ellipse(300, 300, 400, 200);
translate(300, 300);
rotate(radians(130));
ellipse(0, 0, 200, 400);
translate(0, 0);
rotate(radians(0));
ellipse(0, 0, 400, 200);
}
Of course feel free to experiment and make this look nicer.
Here's a modified version of your sketch that uses a few key presses to change the pattern type and shape type at runtime:
int pattern = 1;
// 0 = pluseEllipseCluser, 1 = blobs, 2= myFunction spirograph circles
int shape = 0;
void setup() {
size(600, 600);
println("press 1,2 to change pattern");
println("press p/b/c to change shapes");
}
void draw() {
background(30);
noStroke();
if (pattern==1) {
for (int x=50; x<width; x+=100) {
for (int y=20; y<height; y+=100) {
drawSelectedShapes(x, y);
}
}
}
if (pattern==2) {
float rando = random(10, 90);
for (float x= rando; x >= 0; x-=random(2.5)) {
for (float y= rando; y >= 0; y-=random(2.5)) {
drawSelectedShapes(x, y);
}
}
}
}
void drawSelectedShapes(float x, float y){
if(shape == 0){
plusEllipseCluser(x, y);
}
if(shape == 1){
blobs();
}
if(shape == 2){
myFunction();
}
}
void plusEllipseCluser(float x, float y){
fill(random(255), random(255), random(255), random(255));
ellipse(x, y+30, 50, 20); //plus ellipse cluster
ellipse(x, y+30, 20, 50);
}
void blobs(){
noStroke();
fill(random(250), random(120), random(100));
ellipse(random(width), random(height), 20, 50);
noFill();
stroke(random(255));
ellipse(random(width), random(height), 50, 20);
}
void myFunction() {
noFill();
stroke(255);
ellipse(300, 300, 200, 400);
ellipse(300, 300, 400, 200);
translate(300, 300);
rotate(radians(130));
ellipse(0, 0, 200, 400);
translate(0, 0);
rotate(radians(0));
ellipse(0, 0, 400, 200);
}
void keyPressed(){
if(key == '1') {
pattern = 1;
}
if(key == '2') {
pattern = 2;
}
if(key == 'p'){
shape = 0;
}
if(key == 'b'){
shape = 1;
}
if(key == 'c'){
shape = 2;
}
}
Notice that the example above also calls plusEllipseCluser() passing two arguments: it's a basic example of defining and calling a function that takes two arguments. Of course you've already called functions with arguments before (e.g. random(min,max), ellipse(x,y,w,h), etc.)
Have fun with shapes and patterns.

p5.js not drawing a 3d box

I am creating a simulator using P5.js. Within the simulator, I need a green box, however it does not seem to be appearing. The code is below:
var outputs = [];
function setup() {
createCanvas(600, 400, WEBGL);
background(200);
for (var i = 0; i < 1; i++) {
drop = new Water(width / 2, height / 2, 0, 1);
outputs[i] = drop;
}
}
function draw() {
push();
translate(200, 150, 0);
stroke(0, 100, 0);
fill(0, 255, 0);
box(150, 150, 150);
pop();
for (var i = 0; i < outputs.length; i++) {
outputs[i].update();
}
background(200);
}
Here is the water class:
function Water(x_, y_, z_, yVel_) {
this.r = random(0.25, 1);
this.xOff = random(-(this.r / 10), (this.r / 10));
this.zOff = random(-(this.r / 10), (this.r / 10));
this.x = x_ + this.xOff;
this.y = y_;
this.z = z_ + this.zOff;
this.yVel = yVel_;
this.pos = createVector(this.x, this.y, this.z);
this.show = function() {
push();
translate(this.pos.x, this.pos.y, this.pos.z);
noStroke();
fill(0, 0, 255);
sphere(this.r * 2);
pop();
}
this.update = function() {
this.vel = createVector(random(-(this.r / 10), (this.r / 10)),
this.yVel, random(-(this.r / 10),
(this.r / 10)));
this.pos.add(this.vel);
this.show();
}
}
This is a web based simulation, with another module which appears to be working fine.
Any help would be greatly appreciated. Thanks in advance!
Removing the parts that require the Water class, and moving the background function call to the top of draw, it seems to work just fine.
So, the problem is
Either that you forgot to put background on top
Or something's wrong with the Water class.
Here's your code with the mentioned problems fixed.
var outputs = [];
function setup() {
createCanvas(600, 400, WEBGL);
}
function draw() {
background(200);
push();
translate(200, 150, 0);
stroke(0, 100, 0);
fill(0, 255, 0);
box(150, 150, 150);
pop();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.1/p5.min.js"></script>
Its not rendering because you're background is over your scene
function draw() {
background(200);
push();
translate(200, 150, 0);
stroke(0, 100, 0);
fill(0, 255, 0);
box(150, 150, 150);
pop();
for (var i = 0; i < outputs.length; i++) {
outputs[i].update();
}
}
What your doing is drawing the box and the drops and you cover it all up with your background
if you don't have a background you will see how p5.js renders animation
p5.js not moving it, its just looping through draw every frame and the background covers up the previous frame

Trying to get the Lives and Game Over portion of my game to work

I'm creating a collision detection game where:
Every time I hit the wall, the number of my lives go down.
Once I get to 0 lives, the game is over.
But the game is letting me go into negative lives. Also, my click to begin once you win doesn't seem to work either... Does anybody know how to fix this?
PImage startScreen;
int gamestate=1;
int lives = 3;
class Sprite {
float x;
float y;
float dx;
float dy;
}
Sprite rect=new Sprite();
Sprite ball=new Sprite();
void setup(){
size(500,500);
rect.x = 500;
rect.y = 12;
ball.y= mouseY;
background(0);
fill(0,255,0);
text("Click to Begin", 10, 250);
}
void draw(){
if(gamestate ==0){
background(0);
fill(0, 255,0);
noStroke();
rect(0,235, 500,2.5);
rect(0,250, 500,2.5);
fill(0);
rect(0,238,rect.x,rect.y);
fill(0,255,0);
ellipse(mouseX, mouseY, 2,2);
text("lives left:"+lives, 10, 20);
if (mouseY<240 || mouseY>247){
background(0);
lives = lives-1;
if(lives <= 0){
text("Game Over. \nClick to Begin", 225,250);
gamestate=1;
}
}
if (mouseX >= 495){
background(0);
text("You Win! \nClick to Begin Again.", 225,250);
}
}
}
void mousePressed(){
if (gamestate ==1){
gamestate=0;
}
}
Keep in mind that before your mouse enters the sketch for the first time, mouseX and mouseY are both 0. So in your draw() function, when you check if mouseY < 240, that's true. You do that 60 times per second, so you lose all 3 of your lives right away.
To fix this, you might want to have a "starting rectangle" that the player has to click to start the game, that way you know the mouse is in the window.
Then after that, you have to give the player a chance to get back to the starting circle before starting the next round, otherwise you just keep losing lives 60 times per second.
That basics might look something like this:
int gamestate=1; //1 is start screen, 0 is playing, 2 is between lives, 3 wins
int lives = 3;
class Sprite {
float x;
float y;
float dx;
float dy;
}
Sprite rect=new Sprite();
Sprite ball=new Sprite();
void setup() {
size(500, 500);
rect.x = 500;
rect.y = 12;
ball.y= mouseY;
}
void draw() {
background(0);
if (gamestate ==0) {
fill(0, 255, 0);
noStroke();
rect(0, 235, 500, 2.5);
rect(0, 250, 500, 2.5);
fill(0);
rect(0, 238, rect.x, rect.y);
fill(0, 255, 0);
ellipse(mouseX, mouseY, 2, 2);
text("lives left:"+lives, 10, 20);
if (mouseY<240 || mouseY>247) {
lives = lives-1;
gamestate=2;
if (lives <= 0) {
text("Game Over. \nClick to Begin", 225, 250);
gamestate=1;
}
}
if (mouseX >= 495) {
gamestate=3;
}
}
else if(gamestate == 1 || gamestate==2){
fill(0, 255, 0);
text("Click to Begin", 10, 230);
rect(0, 240, width, 7);
}
else if(gamestate == 3){
text("You Win! \nClick to Begin Again.", 225, 250);
}
}
void mousePressed() {
if (gamestate ==1 || gamestate == 2) {
if(mouseY>240 && mouseY<247){
gamestate=0;
}
}
}
Note that I don't really understand what your game is supposed to do: your "safe area" is only 7 pixels tall, which seems pretty small. But assuming this is just an example, my answer should generalize to your real code:
Split your game up into "modes". You started to do this with your gamestate variable, but you're also mixing event code and drawing code. Instead, make every mode a state: the start screen, the playing screen, the "in between lives" screen, the game over screen. Only draw the stuff for that mode, and then change the mode based on input. Basically, instead of checking for "play mode" and then drawing "game over" when you run out of lives, just switch to "game over mode" and let that part of the code draw the "game over" to the screen.

Stop background from refreshing?

I am trying to get my gif to do something similar to this gif.
I have been able to get the line to draw, and the 'planets' to orbit, but can't figure out how to keep the line connecting the two circles, like the gif does.
Here's the basic code:
int x = 500;
int y = 500;
int radius = y/2;
int cX = x/2;
int cY = y/2;
String text1;
int lg_xBall;
int lg_yBall;
int sm_xBall;
int sm_yBall;
void setup() {
size(x, y);
smooth();
colorMode(RGB);
}
void draw() {
background(0);
stroke(255);
float t = millis()/1000.0f;
drawSmBallOrbit(100);
drawLgBallOrbit(100);
moveSmBall(t);
moveLgBall(t);
sun();
// showMouse();
connectingLines();
}
void drawCircle() { // This will draw a simple circle
stroke(1);
// x1=a+r*cos t, y1=b+r*sin t
ellipse(x/2, y/2, x/2, y/2);
}
void drawLines() { // This will draw lines from the center of the circle.
stroke(1);
line(x/2, y/2, radius/2, radius); // line from 6 to center
line(x/2, y/2, x/2, y/4); // line from 12 to center
for (int i = 0; i <= 5; i+=2.5) {
float x1 = x/2+radius/2*cos(i);
float y1 = y/2+radius/2*sin(i);
line(x/2, y/2, x1, y1);
}
}
void moveSmBall(float ky) { // This will create, and move, a small 'planet'
pushStyle();
stroke(100);
sm_xBall = (int)(cX+radius*cos(ky));
sm_yBall = (int)(cY+radius*sin(ky));
fill(190, 0, 0);
// background(0);
ellipse(sm_xBall, sm_yBall, 10, 10);
popStyle();
}
void drawSmBallOrbit(float opacity) {
pushStyle();
stroke(255, opacity);
strokeWeight(1);
noFill();
ellipse(x/2, y/2, cX+radius, cY+radius);
popStyle();
}
void moveLgBall(float kx) {
kx = kx/.7;
pushStyle();
lg_xBall = (int)(cX+radius*cos(kx)*.6);
lg_yBall = (int)(cY+radius*sin(kx)*.6);
fill(0, 0, 230);
ellipse(lg_xBall, lg_yBall, 30, 30);
popStyle();
}
void drawLgBallOrbit(float opacity) {
pushStyle();
stroke(255, opacity);
strokeWeight(1);
noFill();
ellipse(x/2, y/2, (cX+radius)*.6, (cY+radius)*.6);
popStyle();
}
void sun() {
pushStyle();
fill(250, 250, 0);
ellipse(cX, cY, 40, 40);
popStyle();
}
void connectingLines() {
line(sm_xBall, sm_yBall, lg_xBall, lg_yBall);
}
void showMouse() {
text("X: " + mouseX, x/2, y/2-30);
text("Y: " + mouseY, x/2, y/2-50);
}
Thanks for any help/advice!
The problem is that you're calling background() during every frame, which will clear away anything you've already drawn.
So you either need to stop calling background(), or you need to redraw the old lines every frame.
If you simply move the call to background() out of your draw() function and into your setup() function, you're about 50% there already:
void setup() {
size(x, y);
smooth();
colorMode(RGB);
background(0);
}
void draw() {
// background(0);
stroke(255);
float t = millis()/1000.0f;
drawSmBallOrbit(100);
drawLgBallOrbit(100);
moveSmBall(t);
moveLgBall(t);
sun();
// showMouse();
connectingLines();
}
However, the original animation does not show the previous positions of the ellipses. So you need to clear away the previous frame by calling the background() function, and then redraw previous line positions. You'd do that by having an ArrayList that holds those previous positions.
Here's a simple example that uses an ArrayList to redraw anywhere the mouse has been:
ArrayList<PVector> points = new ArrayList<PVector>();
void setup() {
size(500, 500);
}
void draw() {
background(0);
stroke(255);
points.add(new PVector(mouseX, mouseY));
for(PVector p : points){
ellipse(p.x, p.y, 10, 10);
}
}
You would need to do something very similar, but you'd have to keep track of two points at a time instead of one, since you're tracking two ellipses and not just the mouse position.

Resources