Loading...
Logo
Processing Forum
Hi all,

I'm using eclipse and have started playing with multiple classes.
I have a Base class which extends PApplet (this is where void setup() and void draw() are). I then pass a reference to the Base object when initialising the new generic Object Class. This way I can generate loads of circle objects etc.

problem is...

How can I get the object class to redraw once every frame?

I could make an update method manually.. and call obj.update() from void draw()
This would work but if I had say ten objects.. obj1, obj2 etc etc then I would have to call update() on all of them separately.

I have tried letting the object class call it's own update() (so that it can draw ellipses or rects itself) but I run into the problem of it drawing more than once per frame.



Dan

Replies(11)

---------------------------------------------------------

package danbrookes.main;

import processing.core.*;

import danbrookes.main.TestObject;

public class Base extends PApplet {

private static final long serialVersionUID = -7052994455601885135L;
public TestObject obj;
// main
public static void main(String[] args) {
PApplet.main(new String[] {Base.class.getName()});
}
// setup
@Override
public void setup() {
frameRate(30);
size(500, 500);
noFill();
stroke(0, 0, 0);
strokeWeight(1);
smooth();
obj = new TestObject(this, 0, 0, 100, 100);
}

// draw
@Override
public void draw() {
background(255);
obj.setPos(obj.getX() + 1f, obj.getY() + 1f);
obj.setScale(obj.getW() + 1f, obj.getH() + 1f);
obj.update();
}
}

---------------------------------------------------------

package danbrookes.main;

import processing.core.*;

public class TestObject {
public PApplet pro;
private float x;
private float y;
private float w;
private float h;
public TestObject(PApplet processing, float tX, float tY, float tW, float tH) {
pro = processing;
x = tX;
y = tY;
w = tW;
h = tH;
}

public void update() {
pro.ellipse(x, y, w, h);
}
public void setPos(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}

public float getY() {
return y;
}

public void setScale(float w, float h) {
this.w = w;
this.h = h;
}
public float getW() {
return w;
}

public float getH() {
return h;
}
}

---------------------------------------------------------

you can add a draw() method to your custom class and in it's constructor call registerDraw(this) on the Base class. that way the draw() method of your custom objects will be automatically called after draw() in the Base class.
" This would work but if I had say ten objects.. obj1, obj2 etc etc then I would have to call update() on all of them separately."
Yes. That's not a problem. Particularly if you put the objects in an array.
registerDraw() is nice and handy. The registerXxx() methods are mostly designed for libraries that can't put their own calls in keyPressed() and such in the sketches and don't want to ask the user to do it for them.

But they have a cost: it is based on reflection, ie. Java has to do introspection to find the methods by name. It is generally slower than a straightforward method call. That's not a problem with a few objects, it can be more problematic with thousands of objects (eg. a particle system). Just something to keep in mind...

Could be interesting to measure if it has a real impact, though...

Note: a more classical way is to have objects to implement an interface, eg. defining a draw() or a display() method. Then, whatever the objects created, you put all the objects implementing the interface in an array list, and in draw() you iterate on the list to call the interface's method.

interface Drawable { void draw(); }
class Ball implements Drawable
{
  Ball() { ... }
  void draw() { ellipse(...); }
}
class Wall implements Drawable
{
  Wall() { ... }
  void draw() { rect(...); }
}

So heterogeneous objects offer a similar API and can be collected in a List<Drawable>.
Thanks, that's great advice. I will have a play with the registerxxx() and the classical way!
I'll do some tests on my old! macbook and see how heavy the reflection is.

I'll post my results later



Cheers!
"Classic way" (using an interface and an array)
about 120 ms to draw each frame on my old macbook.

// code

// image


registerDraw() version coming next

Hmmmmmmm,

Doesn't work right..


// Code

// Image


I tried registerPre() also..
i guess it doesn't work to time it like this 'cause the drawing will happen after draw(). thinking of other ways:
  • time individual draw() calls in the object.
  • time multiple frames, say 100 or 1000 frames so stuff that happens outside of the main draw() method should be timed as well
Cheers

Too your advice and rewrote it >

With 10,000 objects being drawn to screen and animated..

I got 7 fps on both the classic way and the registerDraw() way.


There is hardly any loss it seems using registerDraw()






d
Thanks for having setting up this test.
I rewrote it to make it a pure Processing sketch, and one sketch only.
I see perhaps a very small advantage to the "classical way" (calling draw() manually on each object. The interface is just an implementation detail), but it isn't flagrant or even consistent... In general, the overhead of reflection is near zero with regard to the time needed to draw the scene. Good to know.
Now, I think I will still use the classical way, because we still need to loop over the list of objects to update them, etc. And if we remove objects, we have to remember to unregister them first.
At least, the classical way is consistent over the processing of objects, and we can control when, in the draw() function, we call it (sometime, order of drawing is important).
But that's just my choice, and this registerXxx method is handy for libraries and smart.

My modified code:
Copy code
  1. ArrayList<TestObject> list = new ArrayList<TestObject>();
  2. int amount = 10000;
  3. int ms;
  4. int currentTime;
  5. boolean useRegisterDraw = false;
  6.  
  7. void setup() {
  8.   size(1024, 768);
  9. //~   frameRate(60);
  10.   textSize(40);
  11.   strokeWeight(1);
  12.   stroke(0, 128);
  13.   smooth();
  14.   for (int i = 0; i < amount; i++) {
  15.     float rnd = random(5, 10);
  16.     list.add(new TestObject(random(width), random(height), rnd, rnd));
  17.   }
  18.   ms = millis();
  19. }
  20.  
  21. void draw() {
  22.   background(255);
  23.   for (TestObject obj : list) {
  24.     if (!useRegisterDraw) {
  25.       obj.draw();
  26.     }
  27.     obj.move();
  28.   }
  29.   fill(255,0,0);
  30.   text(str(currentTime), 10, 10, 200, 200);
  31.   if (frameCount % 10 == 0) {
  32.     currentTime = millis() - ms;
  33.     println(currentTime);
  34.     ms = millis();
  35.   }
  36. }
  37.  
  38. public class TestObject {
  39.   float x;
  40.   float y;
  41.   float w;
  42.   float h;
  43.  
  44.   public TestObject(float tX, float tY, float tW, float tH) {
  45.     if (useRegisterDraw) {
  46.       registerDraw(this);
  47.     }
  48.     x = tX;
  49.     y = tY;
  50.     w = tW;
  51.     h = tH;
  52.   }
  53.  
  54.   public void draw() {
  55.     fill(0, 64);
  56.     ellipse(x, y, w, h);
  57.   }
  58.  
  59.   public void move() {
  60.     x += random(-2, 2);
  61.     y += random(-2, 2);
  62.   }
  63. }