I'm just tinkering around with Processing in order to make some .gif animations procedurally. For some reason, the one I just finished making has a lot of jitter when I run it (and also when I export it using GifAnimation). I'm not entirely sure why this is happening.
Excuse the hack-job that is my code:
long lastTime = 0;
float angle1, angle2, angle3, angle4, angle5, angle6;
int change;
public void setup() {
size(120, 120);
lastTime = millis();
angle1 = -60;
angle2 = 240;
angle3 = -30;
angle4 = 210;
angle5 = -75;
angle6 = 255;
change = 3;
noFill();
stroke(0);
strokeWeight(10);
smooth();
}
public void draw() {
if (millis() - lastTime > 12) {
background(255,255,255);
lastTime = millis();
stroke(#3aa8c3);
arc(60, 60, 70, 70, radians(angle1), radians(angle2));
ellipse(60, 60, 10, 10);
stroke(#e7e7e7);
arc(60, 60, 40, 40, radians(angle3), radians(angle4));
arc(60, 60, 100, 100, radians(angle5), radians(angle6));
angle1-=change*2;
angle2-=change*2;
angle3+=change*3;
angle4+=change*3;
angle5+=change;
angle6+=change;
}
}
The result is the following:
Is it something I'm doing wrong, or is it simply a restriction of the Processing environment?
Edit:
After changing the rendering mode to P3D (size(120, 120, P3D);) things are working a lot more smoothly. It got rid of the rounded edges and I had to add some anti-aliasing (smooth(8)), but it no longer jitters. The transparent background is also a bonus:
You'll have better luck if you post the simplest form of your problem, something like this:
public void setup() {
size(360, 360);
noFill();
stroke(0);
smooth(10);
}
public void draw() {
background(255);
stroke(#3aa8c3);
arc(60, 60, 70, 70, radians(mouseX), radians(mouseY));
}
This allows us to eliminate causes and more easily find the solution.
Also, I googled "processing arc jittery" and "processing arc wobble" and found this answer, which says to try the hint(ENABLE_STROKE_PURE) function. When I add that to the example, it seems to work much better:
public void setup() {
size(360, 360);
noFill();
stroke(0);
smooth(10);
hint(ENABLE_STROKE_PURE);
}
public void draw() {
background(255);
stroke(#3aa8c3);
arc(60, 60, 70, 70, radians(mouseX), radians(mouseY));
}
Adding hint(ENABLE_STROKE_PURE); as the last line in your setup() function also seems to fix your problem.
Related
When I use my code it says: No uv text coordinates supplied with vertex() call.
This is the code I use:
PImage img;
void setup() {
size(720, 360, P3D);
}
void draw() {
beginShape();
img = loadImage("image.png");
texture(img);
vertex(50, 20);
vertex(105, 20);
vertex(105, 75);
vertex(50, 75);
endShape();
}
Like your error and George's comment say, to use a texture you need to pass in 4 parameters to the vertex() function instead of 2 parameters.
From the reference:
size(100, 100, P3D);
noStroke();
PImage img = loadImage("laDefense.jpg");
beginShape();
texture(img);
vertex(10, 20, 0, 0);
vertex(80, 5, 100, 0);
vertex(95, 90, 100, 100);
vertex(40, 95, 0, 100);
endShape();
(source: processing.org)
Also note that you should not be loading your image inside the draw() function, because that causes you to load the same image 60 times per second. You should load it once from the setup() function.
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;
}
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.
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.
I'm trying to add animation in my code. What I have so far is an object that can be changed by pressing a button. So every time you press the button, the object changes (it is a tree and I'm changing its branches). Is it possible to add some kind of animation like snow? The problem with that is that I have to put it inside the draw method so it will be called automatically and make us think that it is animation. Thus, I also have to add the background / button and everything all the time. But I can't do that with my main object (tree) as I want to change it only when you press the button.
Is there any solution to that?
Thanks in advance
To persist some objects while refreshing others, you either:
Refresh only part of the screen. Like, draw a shape (rect or whatever) with background colour erasing only part of screen
Conditionally draw selected objects. Use flags to selective draw what you need, every draw, and use background() to clear the whole screen every draw cycle.
Use layers. Erase one layer and not other as you need, display all them in draw. This is usually done with PGraphics objects. Search processing + layers to see samples. Here and/or in processing forum.
EDIT:
Here some simple examples of each approach:
1.
/**
* A very simple example of erasing just part of the screen to
* selective persist draws
**/
void setup() {
size(400, 400);
background(0);
noStroke();
}
void draw() {
fill(0);
rect(0, 0, width/2, height);
fill(120);
ellipse(width/4, frameCount%width, 100, 100);
}
void mouseMoved() {
fill(255);
ellipse(mouseX, mouseY, 10, 10);
}
2.
/**
* A very simple example of conditionally draw stuf
* to selective persist draws
**/
ArrayList <PVector> points = new ArrayList <PVector>();
boolean showBalls = true; // any key to toogle
void setup() {
size(400, 400);
background(0);
noStroke();
}
void draw() {
background(0);
fill(30);
rect(frameCount%width, 100, 200, 200);
fill(120);
ellipse(width/2, frameCount%width, 150, 150);
fill(255);
if (showBalls) {
for (PVector p : points) {
ellipse(p.x, p.y, 10, 10);
}
}
if (points.size() > 500) {
points.clear();
}
}
void mouseMoved() {
ellipse(mouseX, mouseY, 10, 10);
points.add(new PVector(mouseX, mouseY));
}
void keyPressed() {
showBalls = !showBalls;
}
3.
/**
* A very simple example of using PGraphics as layers
* to selective persist draws
**/
PGraphics layer;
void setup() {
size(400, 400);
layer = createGraphics(width, height);
layer.beginDraw();
layer.fill(255);
layer.endDraw();
background(0);
noStroke();
}
void draw() {
background(0);
fill(30);
rect(frameCount%width, 100, 200, 200);
fill(120);
ellipse(width/2, frameCount%width, 150, 150);
image(layer, 0, 0);
}
void mouseMoved() {
layer.beginDraw();
layer.ellipse(mouseX, mouseY, 10, 10);
layer.endDraw();
}